mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Support INCLUDE'd columns in SP-GiST.
Not much to say here: does what it says on the tin. We steal a previously-always-zero bit from the nextOffset field of leaf index tuples in order to track whether there is a nulls bitmap. Otherwise it works about like included columns in other index types. Pavel Borisov, reviewed by Andrey Borodin and Anastasia Lubennikova, and rather heavily editorialized on by me Discussion: https://postgr.es/m/CALT9ZEFi-vMp4faht9f9Junb1nO3NOSjhpxTmbm1UGLMsLqiEQ@mail.gmail.com
This commit is contained in:
@@ -27,7 +27,8 @@
|
||||
#include "utils/rel.h"
|
||||
|
||||
typedef void (*storeRes_func) (SpGistScanOpaque so, ItemPointer heapPtr,
|
||||
Datum leafValue, bool isNull, bool recheck,
|
||||
Datum leafValue, bool isNull,
|
||||
SpGistLeafTuple leafTuple, bool recheck,
|
||||
bool recheckDistances, double *distances);
|
||||
|
||||
/*
|
||||
@@ -88,6 +89,9 @@ spgFreeSearchItem(SpGistScanOpaque so, SpGistSearchItem *item)
|
||||
DatumGetPointer(item->value) != NULL)
|
||||
pfree(DatumGetPointer(item->value));
|
||||
|
||||
if (item->leafTuple)
|
||||
pfree(item->leafTuple);
|
||||
|
||||
if (item->traversalValue)
|
||||
pfree(item->traversalValue);
|
||||
|
||||
@@ -133,6 +137,7 @@ spgAddStartItem(SpGistScanOpaque so, bool isnull)
|
||||
startEntry->isLeaf = false;
|
||||
startEntry->level = 0;
|
||||
startEntry->value = (Datum) 0;
|
||||
startEntry->leafTuple = NULL;
|
||||
startEntry->traversalValue = NULL;
|
||||
startEntry->recheck = false;
|
||||
startEntry->recheckDistances = false;
|
||||
@@ -299,7 +304,6 @@ spgbeginscan(Relation rel, int keysz, int orderbysz)
|
||||
{
|
||||
IndexScanDesc scan;
|
||||
SpGistScanOpaque so;
|
||||
TupleDesc outTupDesc;
|
||||
int i;
|
||||
|
||||
scan = RelationGetIndexScan(rel, keysz, orderbysz);
|
||||
@@ -319,20 +323,13 @@ spgbeginscan(Relation rel, int keysz, int orderbysz)
|
||||
ALLOCSET_DEFAULT_SIZES);
|
||||
|
||||
/*
|
||||
* Set up indexTupDesc and xs_hitupdesc in case it's an index-only scan.
|
||||
* Set up reconTupDesc and xs_hitupdesc in case it's an index-only scan,
|
||||
* making sure that the key column is shown as being of type attType.
|
||||
* (It's rather annoying to do this work when it might be wasted, but for
|
||||
* most opclasses we can re-use the index reldesc instead of making one.)
|
||||
*/
|
||||
if (so->state.attType.type ==
|
||||
TupleDescAttr(RelationGetDescr(rel), 0)->atttypid)
|
||||
outTupDesc = RelationGetDescr(rel);
|
||||
else
|
||||
{
|
||||
outTupDesc = CreateTemplateTupleDesc(1);
|
||||
TupleDescInitEntry(outTupDesc, 1, NULL,
|
||||
so->state.attType.type, -1, 0);
|
||||
}
|
||||
so->indexTupDesc = scan->xs_hitupdesc = outTupDesc;
|
||||
so->reconTupDesc = scan->xs_hitupdesc =
|
||||
getSpGistTupleDesc(rel, &so->state.attType);
|
||||
|
||||
/* Allocate various arrays needed for order-by scans */
|
||||
if (scan->numberOfOrderBys > 0)
|
||||
@@ -435,6 +432,10 @@ spgendscan(IndexScanDesc scan)
|
||||
if (so->keyData)
|
||||
pfree(so->keyData);
|
||||
|
||||
if (so->state.leafTupDesc &&
|
||||
so->state.leafTupDesc != RelationGetDescr(so->state.index))
|
||||
FreeTupleDesc(so->state.leafTupDesc);
|
||||
|
||||
if (so->state.deadTupleStorage)
|
||||
pfree(so->state.deadTupleStorage);
|
||||
|
||||
@@ -455,14 +456,14 @@ spgendscan(IndexScanDesc scan)
|
||||
* Leaf SpGistSearchItem constructor, called in queue context
|
||||
*/
|
||||
static SpGistSearchItem *
|
||||
spgNewHeapItem(SpGistScanOpaque so, int level, ItemPointer heapPtr,
|
||||
spgNewHeapItem(SpGistScanOpaque so, int level, SpGistLeafTuple leafTuple,
|
||||
Datum leafValue, bool recheck, bool recheckDistances,
|
||||
bool isnull, double *distances)
|
||||
{
|
||||
SpGistSearchItem *item = spgAllocSearchItem(so, isnull, distances);
|
||||
|
||||
item->level = level;
|
||||
item->heapPtr = *heapPtr;
|
||||
item->heapPtr = leafTuple->heapPtr;
|
||||
|
||||
/*
|
||||
* If we need the reconstructed value, copy it to queue cxt out of tmp
|
||||
@@ -471,11 +472,28 @@ spgNewHeapItem(SpGistScanOpaque so, int level, ItemPointer heapPtr,
|
||||
* the wrong type. The correct leafValue type is attType not leafType.
|
||||
*/
|
||||
if (so->want_itup)
|
||||
{
|
||||
item->value = isnull ? (Datum) 0 :
|
||||
datumCopy(leafValue, so->state.attType.attbyval,
|
||||
so->state.attType.attlen);
|
||||
|
||||
/*
|
||||
* If we're going to need to reconstruct INCLUDE attributes, store the
|
||||
* whole leaf tuple so we can get the INCLUDE attributes out of it.
|
||||
*/
|
||||
if (so->state.leafTupDesc->natts > 1)
|
||||
{
|
||||
item->leafTuple = palloc(leafTuple->size);
|
||||
memcpy(item->leafTuple, leafTuple, leafTuple->size);
|
||||
}
|
||||
else
|
||||
item->leafTuple = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->value = (Datum) 0;
|
||||
item->leafTuple = NULL;
|
||||
}
|
||||
item->traversalValue = NULL;
|
||||
item->isLeaf = true;
|
||||
item->recheck = recheck;
|
||||
@@ -555,7 +573,7 @@ spgLeafTest(SpGistScanOpaque so, SpGistSearchItem *item,
|
||||
/* the scan is ordered -> add the item to the queue */
|
||||
MemoryContext oldCxt = MemoryContextSwitchTo(so->traversalCxt);
|
||||
SpGistSearchItem *heapItem = spgNewHeapItem(so, item->level,
|
||||
&leafTuple->heapPtr,
|
||||
leafTuple,
|
||||
leafValue,
|
||||
recheck,
|
||||
recheckDistances,
|
||||
@@ -571,7 +589,7 @@ spgLeafTest(SpGistScanOpaque so, SpGistSearchItem *item,
|
||||
/* non-ordered scan, so report the item right away */
|
||||
Assert(!recheckDistances);
|
||||
storeRes(so, &leafTuple->heapPtr, leafValue, isnull,
|
||||
recheck, false, NULL);
|
||||
leafTuple, recheck, false, NULL);
|
||||
*reportedSome = true;
|
||||
}
|
||||
}
|
||||
@@ -624,6 +642,8 @@ spgMakeInnerItem(SpGistScanOpaque so,
|
||||
so->state.attLeafType.attlen)
|
||||
: (Datum) 0;
|
||||
|
||||
item->leafTuple = NULL;
|
||||
|
||||
/*
|
||||
* Elements of out.traversalValues should be allocated in
|
||||
* in.traversalMemoryContext, which is actually a long lived context of
|
||||
@@ -765,7 +785,7 @@ spgTestLeafTuple(SpGistScanOpaque so,
|
||||
/* dead tuple should be first in chain */
|
||||
Assert(offset == ItemPointerGetOffsetNumber(&item->heapPtr));
|
||||
/* No live entries on this page */
|
||||
Assert(leafTuple->nextOffset == InvalidOffsetNumber);
|
||||
Assert(SGLT_GET_NEXTOFFSET(leafTuple) == InvalidOffsetNumber);
|
||||
return SpGistBreakOffsetNumber;
|
||||
}
|
||||
}
|
||||
@@ -779,7 +799,7 @@ spgTestLeafTuple(SpGistScanOpaque so,
|
||||
|
||||
spgLeafTest(so, item, leafTuple, isnull, reportedSome, storeRes);
|
||||
|
||||
return leafTuple->nextOffset;
|
||||
return SGLT_GET_NEXTOFFSET(leafTuple);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -812,7 +832,8 @@ redirect:
|
||||
/* We store heap items in the queue only in case of ordered search */
|
||||
Assert(so->numberOfNonNullOrderBys > 0);
|
||||
storeRes(so, &item->heapPtr, item->value, item->isNull,
|
||||
item->recheck, item->recheckDistances, item->distances);
|
||||
item->leafTuple, item->recheck,
|
||||
item->recheckDistances, item->distances);
|
||||
reportedSome = true;
|
||||
}
|
||||
else
|
||||
@@ -905,8 +926,9 @@ redirect:
|
||||
/* storeRes subroutine for getbitmap case */
|
||||
static void
|
||||
storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
|
||||
Datum leafValue, bool isnull, bool recheck, bool recheckDistances,
|
||||
double *distances)
|
||||
Datum leafValue, bool isnull,
|
||||
SpGistLeafTuple leafTuple, bool recheck,
|
||||
bool recheckDistances, double *distances)
|
||||
{
|
||||
Assert(!recheckDistances && !distances);
|
||||
tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
|
||||
@@ -932,8 +954,9 @@ spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
|
||||
/* storeRes subroutine for gettuple case */
|
||||
static void
|
||||
storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
|
||||
Datum leafValue, bool isnull, bool recheck, bool recheckDistances,
|
||||
double *nonNullDistances)
|
||||
Datum leafValue, bool isnull,
|
||||
SpGistLeafTuple leafTuple, bool recheck,
|
||||
bool recheckDistances, double *nonNullDistances)
|
||||
{
|
||||
Assert(so->nPtrs < MaxIndexTuplesPerPage);
|
||||
so->heapPtrs[so->nPtrs] = *heapPtr;
|
||||
@@ -978,9 +1001,20 @@ storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
|
||||
* Reconstruct index data. We have to copy the datum out of the temp
|
||||
* context anyway, so we may as well create the tuple here.
|
||||
*/
|
||||
so->reconTups[so->nPtrs] = heap_form_tuple(so->indexTupDesc,
|
||||
&leafValue,
|
||||
&isnull);
|
||||
Datum leafDatums[INDEX_MAX_KEYS];
|
||||
bool leafIsnulls[INDEX_MAX_KEYS];
|
||||
|
||||
/* We only need to deform the old tuple if it has INCLUDE attributes */
|
||||
if (so->state.leafTupDesc->natts > 1)
|
||||
spgDeformLeafTuple(leafTuple, so->state.leafTupDesc,
|
||||
leafDatums, leafIsnulls, isnull);
|
||||
|
||||
leafDatums[spgKeyColumn] = leafValue;
|
||||
leafIsnulls[spgKeyColumn] = isnull;
|
||||
|
||||
so->reconTups[so->nPtrs] = heap_form_tuple(so->reconTupDesc,
|
||||
leafDatums,
|
||||
leafIsnulls);
|
||||
}
|
||||
so->nPtrs++;
|
||||
}
|
||||
@@ -1048,6 +1082,10 @@ spgcanreturn(Relation index, int attno)
|
||||
{
|
||||
SpGistCache *cache;
|
||||
|
||||
/* INCLUDE attributes can always be fetched for index-only scans */
|
||||
if (attno > 1)
|
||||
return true;
|
||||
|
||||
/* We can do it if the opclass config function says so */
|
||||
cache = spgGetCache(index);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user