Teach SP-GiST to do index-only scans.
Operator classes can specify whether or not they support this; this preserves the flexibility to use lossy representations within an index. In passing, move constant data about a given index into the rd_amcache cache area, instead of doing fresh lookups each time we start an index operation. This is mainly to try to make sure that spgcanreturn() has insignificant cost; I still don't have any proof that it matters for actual index accesses. Also, get rid of useless copying of FmgrInfo pointers; we can perfectly well use the relcache's versions in-place.
This commit is contained in:
parent
3695a55513
commit
9220362493
|
@ -145,6 +145,7 @@ typedef struct spgConfigOut
|
||||||
{
|
{
|
||||||
Oid prefixType; /* Data type of inner-tuple prefixes */
|
Oid prefixType; /* Data type of inner-tuple prefixes */
|
||||||
Oid labelType; /* Data type of inner-tuple node labels */
|
Oid labelType; /* Data type of inner-tuple node labels */
|
||||||
|
bool canReturnData; /* Opclass can reconstruct original data */
|
||||||
bool longValuesOK; /* Opclass can cope with values > 1 page */
|
bool longValuesOK; /* Opclass can cope with values > 1 page */
|
||||||
} spgConfigOut;
|
} spgConfigOut;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
@ -159,6 +160,8 @@ typedef struct spgConfigOut
|
||||||
<structfield>prefixType</> can be set to <literal>VOIDOID</>.
|
<structfield>prefixType</> can be set to <literal>VOIDOID</>.
|
||||||
Likewise, for operator classes that do not use node labels,
|
Likewise, for operator classes that do not use node labels,
|
||||||
<structfield>labelType</> can be set to <literal>VOIDOID</>.
|
<structfield>labelType</> can be set to <literal>VOIDOID</>.
|
||||||
|
<structfield>canReturnData</> should be set true if the operator class
|
||||||
|
is capable of reconstructing the originally-supplied index value.
|
||||||
<structfield>longValuesOK</> should be set true only when the
|
<structfield>longValuesOK</> should be set true only when the
|
||||||
<structfield>attType</> is of variable length and the operator
|
<structfield>attType</> is of variable length and the operator
|
||||||
class is capable of segmenting long values by repeated suffixing
|
class is capable of segmenting long values by repeated suffixing
|
||||||
|
@ -441,6 +444,7 @@ typedef struct spgInnerConsistentIn
|
||||||
|
|
||||||
Datum reconstructedValue; /* value reconstructed at parent */
|
Datum reconstructedValue; /* value reconstructed at parent */
|
||||||
int level; /* current level (counting from zero) */
|
int level; /* current level (counting from zero) */
|
||||||
|
bool returnData; /* original data must be returned? */
|
||||||
|
|
||||||
/* Data from current inner tuple */
|
/* Data from current inner tuple */
|
||||||
bool allTheSame; /* tuple is marked all-the-same? */
|
bool allTheSame; /* tuple is marked all-the-same? */
|
||||||
|
@ -467,6 +471,9 @@ typedef struct spgInnerConsistentOut
|
||||||
parent level.
|
parent level.
|
||||||
<structfield>level</> is the current inner tuple's level, starting at
|
<structfield>level</> is the current inner tuple's level, starting at
|
||||||
zero for the root level.
|
zero for the root level.
|
||||||
|
<structfield>returnData</> is <literal>true</> if reconstructed data is
|
||||||
|
required for this query; this will only be so if the
|
||||||
|
<function>config</> function asserted <structfield>canReturnData</>.
|
||||||
<structfield>allTheSame</> is true if the current inner tuple is
|
<structfield>allTheSame</> is true if the current inner tuple is
|
||||||
marked <quote>all-the-same</>; in this case all the nodes have the
|
marked <quote>all-the-same</>; in this case all the nodes have the
|
||||||
same label (if any) and so either all or none of them match the query
|
same label (if any) and so either all or none of them match the query
|
||||||
|
@ -525,12 +532,14 @@ typedef struct spgLeafConsistentIn
|
||||||
|
|
||||||
Datum reconstructedValue; /* value reconstructed at parent */
|
Datum reconstructedValue; /* value reconstructed at parent */
|
||||||
int level; /* current level (counting from zero) */
|
int level; /* current level (counting from zero) */
|
||||||
|
bool returnData; /* original data must be returned? */
|
||||||
|
|
||||||
Datum leafDatum; /* datum in leaf tuple */
|
Datum leafDatum; /* datum in leaf tuple */
|
||||||
} spgLeafConsistentIn;
|
} spgLeafConsistentIn;
|
||||||
|
|
||||||
typedef struct spgLeafConsistentOut
|
typedef struct spgLeafConsistentOut
|
||||||
{
|
{
|
||||||
|
Datum leafValue; /* reconstructed original data, if any */
|
||||||
bool recheck; /* set true if operator must be rechecked */
|
bool recheck; /* set true if operator must be rechecked */
|
||||||
} spgLeafConsistentOut;
|
} spgLeafConsistentOut;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
@ -543,6 +552,9 @@ typedef struct spgLeafConsistentOut
|
||||||
parent level.
|
parent level.
|
||||||
<structfield>level</> is the current leaf tuple's level, starting at
|
<structfield>level</> is the current leaf tuple's level, starting at
|
||||||
zero for the root level.
|
zero for the root level.
|
||||||
|
<structfield>returnData</> is <literal>true</> if reconstructed data is
|
||||||
|
required for this query; this will only be so if the
|
||||||
|
<function>config</> function asserted <structfield>canReturnData</>.
|
||||||
<structfield>leafDatum</> is the key value stored in the current
|
<structfield>leafDatum</> is the key value stored in the current
|
||||||
leaf tuple.
|
leaf tuple.
|
||||||
</para>
|
</para>
|
||||||
|
@ -550,6 +562,9 @@ typedef struct spgLeafConsistentOut
|
||||||
<para>
|
<para>
|
||||||
The function must return <literal>true</> if the leaf tuple matches the
|
The function must return <literal>true</> if the leaf tuple matches the
|
||||||
query, or <literal>false</> if not. In the <literal>true</> case,
|
query, or <literal>false</> if not. In the <literal>true</> case,
|
||||||
|
if <structfield>returnData</> is <literal>true</> then
|
||||||
|
<structfield>leafValue</> must be set to the value originally supplied
|
||||||
|
to be indexed for this leaf tuple. Also,
|
||||||
<structfield>recheck</> may be set to <literal>true</> if the match
|
<structfield>recheck</> may be set to <literal>true</> if the match
|
||||||
is uncertain and so the operator must be re-applied to the actual heap
|
is uncertain and so the operator must be re-applied to the actual heap
|
||||||
tuple to verify the match.
|
tuple to verify the match.
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
#include "access/spgist_private.h"
|
#include "access/spgist_private.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
@ -678,6 +679,7 @@ doPickSplit(Relation index, SpGistState *state,
|
||||||
bool insertedNew = false;
|
bool insertedNew = false;
|
||||||
spgPickSplitIn in;
|
spgPickSplitIn in;
|
||||||
spgPickSplitOut out;
|
spgPickSplitOut out;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
bool includeNew;
|
bool includeNew;
|
||||||
int i,
|
int i,
|
||||||
max,
|
max,
|
||||||
|
@ -816,7 +818,8 @@ doPickSplit(Relation index, SpGistState *state,
|
||||||
*/
|
*/
|
||||||
memset(&out, 0, sizeof(out));
|
memset(&out, 0, sizeof(out));
|
||||||
|
|
||||||
FunctionCall2Coll(&state->picksplitFn,
|
procinfo = index_getprocinfo(index, 1, SPGIST_PICKSPLIT_PROC);
|
||||||
|
FunctionCall2Coll(procinfo,
|
||||||
index->rd_indcollation[0],
|
index->rd_indcollation[0],
|
||||||
PointerGetDatum(&in),
|
PointerGetDatum(&in),
|
||||||
PointerGetDatum(&out));
|
PointerGetDatum(&out));
|
||||||
|
@ -1944,6 +1947,7 @@ spgdoinsert(Relation index, SpGistState *state,
|
||||||
SpGistInnerTuple innerTuple;
|
SpGistInnerTuple innerTuple;
|
||||||
spgChooseIn in;
|
spgChooseIn in;
|
||||||
spgChooseOut out;
|
spgChooseOut out;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spgAddNode and spgSplitTuple cases will loop back to here to
|
* spgAddNode and spgSplitTuple cases will loop back to here to
|
||||||
|
@ -1968,7 +1972,8 @@ spgdoinsert(Relation index, SpGistState *state,
|
||||||
|
|
||||||
memset(&out, 0, sizeof(out));
|
memset(&out, 0, sizeof(out));
|
||||||
|
|
||||||
FunctionCall2Coll(&state->chooseFn,
|
procinfo = index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC);
|
||||||
|
FunctionCall2Coll(procinfo,
|
||||||
index->rd_indcollation[0],
|
index->rd_indcollation[0],
|
||||||
PointerGetDatum(&in),
|
PointerGetDatum(&in),
|
||||||
PointerGetDatum(&out));
|
PointerGetDatum(&out));
|
||||||
|
|
|
@ -30,6 +30,7 @@ spg_kd_config(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
cfg->prefixType = FLOAT8OID;
|
cfg->prefixType = FLOAT8OID;
|
||||||
cfg->labelType = VOIDOID; /* we don't need node labels */
|
cfg->labelType = VOIDOID; /* we don't need node labels */
|
||||||
|
cfg->canReturnData = true;
|
||||||
cfg->longValuesOK = false;
|
cfg->longValuesOK = false;
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ spg_quad_config(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
cfg->prefixType = POINTOID;
|
cfg->prefixType = POINTOID;
|
||||||
cfg->labelType = VOIDOID; /* we don't need node labels */
|
cfg->labelType = VOIDOID; /* we don't need node labels */
|
||||||
|
cfg->canReturnData = true;
|
||||||
cfg->longValuesOK = false;
|
cfg->longValuesOK = false;
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
@ -324,6 +325,9 @@ spg_quad_leaf_consistent(PG_FUNCTION_ARGS)
|
||||||
/* all tests are exact */
|
/* all tests are exact */
|
||||||
out->recheck = false;
|
out->recheck = false;
|
||||||
|
|
||||||
|
/* leafDatum is what it is... */
|
||||||
|
out->leafValue = in->leafDatum;
|
||||||
|
|
||||||
switch (in->strategy)
|
switch (in->strategy)
|
||||||
{
|
{
|
||||||
case RTLeftStrategyNumber:
|
case RTLeftStrategyNumber:
|
||||||
|
|
|
@ -55,7 +55,10 @@ freeScanStack(SpGistScanOpaque so)
|
||||||
so->scanStack = NIL;
|
so->scanStack = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize scanStack with a single entry for the root page */
|
/*
|
||||||
|
* Initialize scanStack with a single entry for the root page, resetting
|
||||||
|
* any previously active scan
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
resetSpGistScanOpaque(SpGistScanOpaque so)
|
resetSpGistScanOpaque(SpGistScanOpaque so)
|
||||||
{
|
{
|
||||||
|
@ -65,7 +68,16 @@ resetSpGistScanOpaque(SpGistScanOpaque so)
|
||||||
|
|
||||||
freeScanStack(so);
|
freeScanStack(so);
|
||||||
so->scanStack = list_make1(startEntry);
|
so->scanStack = list_make1(startEntry);
|
||||||
so->nPtrs = so->iPtr = 0;
|
|
||||||
|
if (so->want_itup)
|
||||||
|
{
|
||||||
|
/* Must pfree IndexTuples to avoid memory leak */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < so->nPtrs; i++)
|
||||||
|
pfree(so->indexTups[i]);
|
||||||
|
}
|
||||||
|
so->iPtr = so->nPtrs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
|
@ -87,6 +99,10 @@ spgbeginscan(PG_FUNCTION_ARGS)
|
||||||
ALLOCSET_DEFAULT_INITSIZE,
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
ALLOCSET_DEFAULT_MAXSIZE);
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
resetSpGistScanOpaque(so);
|
resetSpGistScanOpaque(so);
|
||||||
|
|
||||||
|
/* Set up indexTupDesc and xs_itupdesc in case it's an index-only scan */
|
||||||
|
so->indexTupDesc = scan->xs_itupdesc = RelationGetDescr(rel);
|
||||||
|
|
||||||
scan->opaque = so;
|
scan->opaque = so;
|
||||||
|
|
||||||
PG_RETURN_POINTER(scan);
|
PG_RETURN_POINTER(scan);
|
||||||
|
@ -138,28 +154,35 @@ spgrestrpos(PG_FUNCTION_ARGS)
|
||||||
/*
|
/*
|
||||||
* Test whether a leaf datum satisfies all the scan keys
|
* Test whether a leaf datum satisfies all the scan keys
|
||||||
*
|
*
|
||||||
|
* *leafValue is set to the reconstructed datum, if provided
|
||||||
* *recheck is set true if any of the operators are lossy
|
* *recheck is set true if any of the operators are lossy
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
|
spgLeafTest(Relation index, SpGistScanOpaque so, Datum leafDatum,
|
||||||
int level, Datum reconstructedValue,
|
int level, Datum reconstructedValue,
|
||||||
bool *recheck)
|
Datum *leafValue, bool *recheck)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
spgLeafConsistentIn in;
|
spgLeafConsistentIn in;
|
||||||
spgLeafConsistentOut out;
|
spgLeafConsistentOut out;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
MemoryContext oldCtx;
|
MemoryContext oldCtx;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
*leafValue = (Datum) 0;
|
||||||
*recheck = false;
|
*recheck = false;
|
||||||
|
|
||||||
/* set up values that are the same for all quals */
|
/* set up values that are the same for all quals */
|
||||||
in.reconstructedValue = reconstructedValue;
|
in.reconstructedValue = reconstructedValue;
|
||||||
in.level = level;
|
in.level = level;
|
||||||
|
in.returnData = so->want_itup;
|
||||||
in.leafDatum = leafDatum;
|
in.leafDatum = leafDatum;
|
||||||
|
|
||||||
/* Apply each leaf consistent function, working in the temp context */
|
/* Apply each leaf consistency check, working in the temp context */
|
||||||
oldCtx = MemoryContextSwitchTo(so->tempCxt);
|
oldCtx = MemoryContextSwitchTo(so->tempCxt);
|
||||||
|
|
||||||
|
procinfo = index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC);
|
||||||
|
|
||||||
for (i = 0; i < so->numberOfKeys; i++)
|
for (i = 0; i < so->numberOfKeys; i++)
|
||||||
{
|
{
|
||||||
ScanKey skey = &so->keyData[i];
|
ScanKey skey = &so->keyData[i];
|
||||||
|
@ -174,12 +197,14 @@ spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
|
||||||
in.strategy = skey->sk_strategy;
|
in.strategy = skey->sk_strategy;
|
||||||
in.query = skey->sk_argument;
|
in.query = skey->sk_argument;
|
||||||
|
|
||||||
|
out.leafValue = (Datum) 0;
|
||||||
out.recheck = false;
|
out.recheck = false;
|
||||||
|
|
||||||
result = DatumGetBool(FunctionCall2Coll(&so->state.leafConsistentFn,
|
result = DatumGetBool(FunctionCall2Coll(procinfo,
|
||||||
skey->sk_collation,
|
skey->sk_collation,
|
||||||
PointerGetDatum(&in),
|
PointerGetDatum(&in),
|
||||||
PointerGetDatum(&out)));
|
PointerGetDatum(&out)));
|
||||||
|
*leafValue = out.leafValue;
|
||||||
*recheck |= out.recheck;
|
*recheck |= out.recheck;
|
||||||
if (!result)
|
if (!result)
|
||||||
break;
|
break;
|
||||||
|
@ -198,7 +223,7 @@ spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
|
spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
|
||||||
void (*storeRes) (SpGistScanOpaque, ItemPointer, bool))
|
void (*storeRes) (SpGistScanOpaque, ItemPointer, Datum, bool))
|
||||||
{
|
{
|
||||||
Buffer buffer = InvalidBuffer;
|
Buffer buffer = InvalidBuffer;
|
||||||
bool reportedSome = false;
|
bool reportedSome = false;
|
||||||
|
@ -243,6 +268,7 @@ redirect:
|
||||||
{
|
{
|
||||||
SpGistLeafTuple leafTuple;
|
SpGistLeafTuple leafTuple;
|
||||||
OffsetNumber max = PageGetMaxOffsetNumber(page);
|
OffsetNumber max = PageGetMaxOffsetNumber(page);
|
||||||
|
Datum leafValue = (Datum) 0;
|
||||||
bool recheck = false;
|
bool recheck = false;
|
||||||
|
|
||||||
if (blkno == SPGIST_HEAD_BLKNO)
|
if (blkno == SPGIST_HEAD_BLKNO)
|
||||||
|
@ -260,13 +286,14 @@ redirect:
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
|
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
|
||||||
if (spgLeafTest(so,
|
if (spgLeafTest(index, so,
|
||||||
SGLTDATUM(leafTuple, &so->state),
|
SGLTDATUM(leafTuple, &so->state),
|
||||||
stackEntry->level,
|
stackEntry->level,
|
||||||
stackEntry->reconstructedValue,
|
stackEntry->reconstructedValue,
|
||||||
|
&leafValue,
|
||||||
&recheck))
|
&recheck))
|
||||||
{
|
{
|
||||||
storeRes(so, &leafTuple->heapPtr, recheck);
|
storeRes(so, &leafTuple->heapPtr, leafValue, recheck);
|
||||||
reportedSome = true;
|
reportedSome = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,13 +331,14 @@ redirect:
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
|
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
|
||||||
if (spgLeafTest(so,
|
if (spgLeafTest(index, so,
|
||||||
SGLTDATUM(leafTuple, &so->state),
|
SGLTDATUM(leafTuple, &so->state),
|
||||||
stackEntry->level,
|
stackEntry->level,
|
||||||
stackEntry->reconstructedValue,
|
stackEntry->reconstructedValue,
|
||||||
|
&leafValue,
|
||||||
&recheck))
|
&recheck))
|
||||||
{
|
{
|
||||||
storeRes(so, &leafTuple->heapPtr, recheck);
|
storeRes(so, &leafTuple->heapPtr, leafValue, recheck);
|
||||||
reportedSome = true;
|
reportedSome = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,6 +402,7 @@ redirect:
|
||||||
{
|
{
|
||||||
spgInnerConsistentIn in;
|
spgInnerConsistentIn in;
|
||||||
spgInnerConsistentOut out;
|
spgInnerConsistentOut out;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
SpGistNodeTuple *nodes;
|
SpGistNodeTuple *nodes;
|
||||||
int *andMap;
|
int *andMap;
|
||||||
int *levelAdds;
|
int *levelAdds;
|
||||||
|
@ -388,6 +417,7 @@ redirect:
|
||||||
/* set up values that are the same for all scankeys */
|
/* set up values that are the same for all scankeys */
|
||||||
in.reconstructedValue = stackEntry->reconstructedValue;
|
in.reconstructedValue = stackEntry->reconstructedValue;
|
||||||
in.level = stackEntry->level;
|
in.level = stackEntry->level;
|
||||||
|
in.returnData = so->want_itup;
|
||||||
in.allTheSame = innerTuple->allTheSame;
|
in.allTheSame = innerTuple->allTheSame;
|
||||||
in.hasPrefix = (innerTuple->prefixSize > 0);
|
in.hasPrefix = (innerTuple->prefixSize > 0);
|
||||||
in.prefixDatum = SGITDATUM(innerTuple, &so->state);
|
in.prefixDatum = SGITDATUM(innerTuple, &so->state);
|
||||||
|
@ -405,6 +435,8 @@ redirect:
|
||||||
levelAdds = (int *) palloc0(sizeof(int) * in.nNodes);
|
levelAdds = (int *) palloc0(sizeof(int) * in.nNodes);
|
||||||
reconstructedValues = (Datum *) palloc0(sizeof(Datum) * in.nNodes);
|
reconstructedValues = (Datum *) palloc0(sizeof(Datum) * in.nNodes);
|
||||||
|
|
||||||
|
procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
|
||||||
|
|
||||||
for (j = 0; j < so->numberOfKeys; j++)
|
for (j = 0; j < so->numberOfKeys; j++)
|
||||||
{
|
{
|
||||||
ScanKey skey = &so->keyData[j];
|
ScanKey skey = &so->keyData[j];
|
||||||
|
@ -421,7 +453,7 @@ redirect:
|
||||||
|
|
||||||
memset(&out, 0, sizeof(out));
|
memset(&out, 0, sizeof(out));
|
||||||
|
|
||||||
FunctionCall2Coll(&so->state.innerConsistentFn,
|
FunctionCall2Coll(procinfo,
|
||||||
skey->sk_collation,
|
skey->sk_collation,
|
||||||
PointerGetDatum(&in),
|
PointerGetDatum(&in),
|
||||||
PointerGetDatum(&out));
|
PointerGetDatum(&out));
|
||||||
|
@ -490,7 +522,8 @@ redirect:
|
||||||
|
|
||||||
/* storeRes subroutine for getbitmap case */
|
/* storeRes subroutine for getbitmap case */
|
||||||
static void
|
static void
|
||||||
storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr, bool recheck)
|
storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
|
||||||
|
Datum leafValue, bool recheck)
|
||||||
{
|
{
|
||||||
tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
|
tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
|
||||||
so->ntids++;
|
so->ntids++;
|
||||||
|
@ -506,6 +539,8 @@ spggetbitmap(PG_FUNCTION_ARGS)
|
||||||
/* Copy scankey to *so so we don't need to pass it around separately */
|
/* Copy scankey to *so so we don't need to pass it around separately */
|
||||||
so->numberOfKeys = scan->numberOfKeys;
|
so->numberOfKeys = scan->numberOfKeys;
|
||||||
so->keyData = scan->keyData;
|
so->keyData = scan->keyData;
|
||||||
|
/* Ditto for the want_itup flag */
|
||||||
|
so->want_itup = false;
|
||||||
|
|
||||||
so->tbm = tbm;
|
so->tbm = tbm;
|
||||||
so->ntids = 0;
|
so->ntids = 0;
|
||||||
|
@ -517,11 +552,24 @@ spggetbitmap(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
/* storeRes subroutine for gettuple case */
|
/* storeRes subroutine for gettuple case */
|
||||||
static void
|
static void
|
||||||
storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr, bool recheck)
|
storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
|
||||||
|
Datum leafValue, bool recheck)
|
||||||
{
|
{
|
||||||
Assert(so->nPtrs < MaxIndexTuplesPerPage);
|
Assert(so->nPtrs < MaxIndexTuplesPerPage);
|
||||||
so->heapPtrs[so->nPtrs] = *heapPtr;
|
so->heapPtrs[so->nPtrs] = *heapPtr;
|
||||||
so->recheck[so->nPtrs] = recheck;
|
so->recheck[so->nPtrs] = recheck;
|
||||||
|
if (so->want_itup)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Reconstruct desired IndexTuple. We have to copy the datum out of
|
||||||
|
* the temp context anyway, so we may as well create the tuple here.
|
||||||
|
*/
|
||||||
|
bool isnull = false;
|
||||||
|
|
||||||
|
so->indexTups[so->nPtrs] = index_form_tuple(so->indexTupDesc,
|
||||||
|
&leafValue,
|
||||||
|
&isnull);
|
||||||
|
}
|
||||||
so->nPtrs++;
|
so->nPtrs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,6 +586,8 @@ spggettuple(PG_FUNCTION_ARGS)
|
||||||
/* Copy scankey to *so so we don't need to pass it around separately */
|
/* Copy scankey to *so so we don't need to pass it around separately */
|
||||||
so->numberOfKeys = scan->numberOfKeys;
|
so->numberOfKeys = scan->numberOfKeys;
|
||||||
so->keyData = scan->keyData;
|
so->keyData = scan->keyData;
|
||||||
|
/* Ditto for the want_itup flag */
|
||||||
|
so->want_itup = scan->xs_want_itup;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -546,11 +596,21 @@ spggettuple(PG_FUNCTION_ARGS)
|
||||||
/* continuing to return tuples from a leaf page */
|
/* continuing to return tuples from a leaf page */
|
||||||
scan->xs_ctup.t_self = so->heapPtrs[so->iPtr];
|
scan->xs_ctup.t_self = so->heapPtrs[so->iPtr];
|
||||||
scan->xs_recheck = so->recheck[so->iPtr];
|
scan->xs_recheck = so->recheck[so->iPtr];
|
||||||
|
scan->xs_itup = so->indexTups[so->iPtr];
|
||||||
so->iPtr++;
|
so->iPtr++;
|
||||||
PG_RETURN_BOOL(true);
|
PG_RETURN_BOOL(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (so->want_itup)
|
||||||
|
{
|
||||||
|
/* Must pfree IndexTuples to avoid memory leak */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < so->nPtrs; i++)
|
||||||
|
pfree(so->indexTups[i]);
|
||||||
|
}
|
||||||
so->iPtr = so->nPtrs = 0;
|
so->iPtr = so->nPtrs = 0;
|
||||||
|
|
||||||
spgWalk(scan->indexRelation, so, false, storeGettuple);
|
spgWalk(scan->indexRelation, so, false, storeGettuple);
|
||||||
|
|
||||||
if (so->nPtrs == 0)
|
if (so->nPtrs == 0)
|
||||||
|
@ -563,6 +623,11 @@ spggettuple(PG_FUNCTION_ARGS)
|
||||||
Datum
|
Datum
|
||||||
spgcanreturn(PG_FUNCTION_ARGS)
|
spgcanreturn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
/* Not implemented yet */
|
Relation index = (Relation) PG_GETARG_POINTER(0);
|
||||||
PG_RETURN_BOOL(false);
|
SpGistCache *cache;
|
||||||
|
|
||||||
|
/* We can do it if the opclass config function says so */
|
||||||
|
cache = spgGetCache(index);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(cache->config.canReturnData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ spg_text_config(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
cfg->prefixType = TEXTOID;
|
cfg->prefixType = TEXTOID;
|
||||||
cfg->labelType = CHAROID;
|
cfg->labelType = CHAROID;
|
||||||
|
cfg->canReturnData = true;
|
||||||
cfg->longValuesOK = true; /* suffixing will shorten long values */
|
cfg->longValuesOK = true; /* suffixing will shorten long values */
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
@ -521,7 +522,10 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
queryLen = VARSIZE_ANY_EXHDR(query);
|
queryLen = VARSIZE_ANY_EXHDR(query);
|
||||||
|
|
||||||
/* For equality, we needn't reconstruct fullValue if not same length */
|
/*
|
||||||
|
* For an equality check, we needn't reconstruct fullValue if not same
|
||||||
|
* length; it can't match
|
||||||
|
*/
|
||||||
if (strategy == BTEqualStrategyNumber && queryLen != fullLen)
|
if (strategy == BTEqualStrategyNumber && queryLen != fullLen)
|
||||||
PG_RETURN_BOOL(false);
|
PG_RETURN_BOOL(false);
|
||||||
|
|
||||||
|
@ -529,15 +533,20 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
|
||||||
if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
|
if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
|
||||||
{
|
{
|
||||||
fullValue = VARDATA(reconstrValue);
|
fullValue = VARDATA(reconstrValue);
|
||||||
|
out->leafValue = PointerGetDatum(reconstrValue);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fullValue = palloc(fullLen);
|
text *fullText = palloc(VARHDRSZ + fullLen);
|
||||||
|
|
||||||
|
SET_VARSIZE(fullText, VARHDRSZ + fullLen);
|
||||||
|
fullValue = VARDATA(fullText);
|
||||||
if (level)
|
if (level)
|
||||||
memcpy(fullValue, VARDATA(reconstrValue), level);
|
memcpy(fullValue, VARDATA(reconstrValue), level);
|
||||||
if (VARSIZE_ANY_EXHDR(leafValue) > 0)
|
if (VARSIZE_ANY_EXHDR(leafValue) > 0)
|
||||||
memcpy(fullValue + level, VARDATA_ANY(leafValue),
|
memcpy(fullValue + level, VARDATA_ANY(leafValue),
|
||||||
VARSIZE_ANY_EXHDR(leafValue));
|
VARSIZE_ANY_EXHDR(leafValue));
|
||||||
|
out->leafValue = PointerGetDatum(fullText);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run the appropriate type of comparison */
|
/* Run the appropriate type of comparison */
|
||||||
|
|
|
@ -34,51 +34,88 @@ fillTypeDesc(SpGistTypeDesc *desc, Oid type)
|
||||||
get_typlenbyval(type, &desc->attlen, &desc->attbyval);
|
get_typlenbyval(type, &desc->attlen, &desc->attbyval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize SpGistState for working with the given index */
|
/*
|
||||||
void
|
* Fetch local cache of AM-specific info about the index, initializing it
|
||||||
initSpGistState(SpGistState *state, Relation index)
|
* if necessary
|
||||||
|
*/
|
||||||
|
SpGistCache *
|
||||||
|
spgGetCache(Relation index)
|
||||||
|
{
|
||||||
|
SpGistCache *cache;
|
||||||
|
|
||||||
|
if (index->rd_amcache == NULL)
|
||||||
{
|
{
|
||||||
Oid atttype;
|
Oid atttype;
|
||||||
spgConfigIn in;
|
spgConfigIn in;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
|
Buffer metabuffer;
|
||||||
|
SpGistMetaPageData *metadata;
|
||||||
|
|
||||||
|
cache = MemoryContextAllocZero(index->rd_indexcxt,
|
||||||
|
sizeof(SpGistCache));
|
||||||
|
|
||||||
/* SPGiST doesn't support multi-column indexes */
|
/* SPGiST doesn't support multi-column indexes */
|
||||||
Assert(index->rd_att->natts == 1);
|
Assert(index->rd_att->natts == 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the actual data type of the indexed column from the index tupdesc.
|
* Get the actual data type of the indexed column from the index
|
||||||
* We pass this to the opclass config function so that polymorphic
|
* tupdesc. We pass this to the opclass config function so that
|
||||||
* opclasses are possible.
|
* polymorphic opclasses are possible.
|
||||||
*/
|
*/
|
||||||
atttype = index->rd_att->attrs[0]->atttypid;
|
atttype = index->rd_att->attrs[0]->atttypid;
|
||||||
|
|
||||||
/* Get the config info for the opclass */
|
/* Call the config function to get config info for the opclass */
|
||||||
in.attType = atttype;
|
in.attType = atttype;
|
||||||
|
|
||||||
memset(&state->config, 0, sizeof(state->config));
|
procinfo = index_getprocinfo(index, 1, SPGIST_CONFIG_PROC);
|
||||||
|
FunctionCall2Coll(procinfo,
|
||||||
FunctionCall2Coll(index_getprocinfo(index, 1, SPGIST_CONFIG_PROC),
|
|
||||||
index->rd_indcollation[0],
|
index->rd_indcollation[0],
|
||||||
PointerGetDatum(&in),
|
PointerGetDatum(&in),
|
||||||
PointerGetDatum(&state->config));
|
PointerGetDatum(&cache->config));
|
||||||
|
|
||||||
/* Get the information we need about each relevant datatype */
|
/* Get the information we need about each relevant datatype */
|
||||||
fillTypeDesc(&state->attType, atttype);
|
fillTypeDesc(&cache->attType, atttype);
|
||||||
fillTypeDesc(&state->attPrefixType, state->config.prefixType);
|
fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
|
||||||
fillTypeDesc(&state->attLabelType, state->config.labelType);
|
fillTypeDesc(&cache->attLabelType, cache->config.labelType);
|
||||||
|
|
||||||
/* Get lookup info for opclass support procs */
|
/* Last, get the lastUsedPages data from the metapage */
|
||||||
fmgr_info_copy(&(state->chooseFn),
|
metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
|
||||||
index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC),
|
LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
|
||||||
CurrentMemoryContext);
|
|
||||||
fmgr_info_copy(&(state->picksplitFn),
|
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
|
||||||
index_getprocinfo(index, 1, SPGIST_PICKSPLIT_PROC),
|
|
||||||
CurrentMemoryContext);
|
if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
|
||||||
fmgr_info_copy(&(state->innerConsistentFn),
|
elog(ERROR, "index \"%s\" is not an SP-GiST index",
|
||||||
index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC),
|
RelationGetRelationName(index));
|
||||||
CurrentMemoryContext);
|
|
||||||
fmgr_info_copy(&(state->leafConsistentFn),
|
cache->lastUsedPages = metadata->lastUsedPages;
|
||||||
index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC),
|
|
||||||
CurrentMemoryContext);
|
UnlockReleaseBuffer(metabuffer);
|
||||||
|
|
||||||
|
index->rd_amcache = (void *) cache;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* assume it's up to date */
|
||||||
|
cache = (SpGistCache *) index->rd_amcache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize SpGistState for working with the given index */
|
||||||
|
void
|
||||||
|
initSpGistState(SpGistState *state, Relation index)
|
||||||
|
{
|
||||||
|
SpGistCache *cache;
|
||||||
|
|
||||||
|
/* Get cached static information about index */
|
||||||
|
cache = spgGetCache(index);
|
||||||
|
|
||||||
|
state->config = cache->config;
|
||||||
|
state->attType = cache->attType;
|
||||||
|
state->attPrefixType = cache->attPrefixType;
|
||||||
|
state->attLabelType = cache->attLabelType;
|
||||||
|
|
||||||
/* Make workspace for constructing dead tuples */
|
/* Make workspace for constructing dead tuples */
|
||||||
state->deadTupleStorage = palloc0(SGDTSIZE);
|
state->deadTupleStorage = palloc0(SGDTSIZE);
|
||||||
|
@ -86,6 +123,7 @@ initSpGistState(SpGistState *state, Relation index)
|
||||||
/* Set XID to use in redirection tuples */
|
/* Set XID to use in redirection tuples */
|
||||||
state->myXid = GetTopTransactionIdIfAny();
|
state->myXid = GetTopTransactionIdIfAny();
|
||||||
|
|
||||||
|
/* Assume we're not in an index build (spgbuild will override) */
|
||||||
state->isBuild = false;
|
state->isBuild = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,46 +191,6 @@ SpGistNewBuffer(Relation index)
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch local cache of lastUsedPages info, initializing it from the metapage
|
|
||||||
* if necessary
|
|
||||||
*/
|
|
||||||
static SpGistCache *
|
|
||||||
spgGetCache(Relation index)
|
|
||||||
{
|
|
||||||
SpGistCache *cache;
|
|
||||||
|
|
||||||
if (index->rd_amcache == NULL)
|
|
||||||
{
|
|
||||||
Buffer metabuffer;
|
|
||||||
SpGistMetaPageData *metadata;
|
|
||||||
|
|
||||||
cache = MemoryContextAlloc(index->rd_indexcxt,
|
|
||||||
sizeof(SpGistCache));
|
|
||||||
|
|
||||||
metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
|
|
||||||
LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
|
|
||||||
|
|
||||||
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
|
|
||||||
|
|
||||||
if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
|
|
||||||
elog(ERROR, "index \"%s\" is not an SP-GiST index",
|
|
||||||
RelationGetRelationName(index));
|
|
||||||
|
|
||||||
*cache = metadata->lastUsedPages;
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(metabuffer);
|
|
||||||
|
|
||||||
index->rd_amcache = cache;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cache = (SpGistCache *) index->rd_amcache;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update index metapage's lastUsedPages info from local cache, if possible
|
* Update index metapage's lastUsedPages info from local cache, if possible
|
||||||
*
|
*
|
||||||
|
@ -215,7 +213,7 @@ SpGistUpdateMetaPage(Relation index)
|
||||||
if (ConditionalLockBuffer(metabuffer))
|
if (ConditionalLockBuffer(metabuffer))
|
||||||
{
|
{
|
||||||
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
|
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
|
||||||
metadata->lastUsedPages = *cache;
|
metadata->lastUsedPages = cache->lastUsedPages;
|
||||||
|
|
||||||
MarkBufferDirty(metabuffer);
|
MarkBufferDirty(metabuffer);
|
||||||
UnlockReleaseBuffer(metabuffer);
|
UnlockReleaseBuffer(metabuffer);
|
||||||
|
@ -229,8 +227,8 @@ SpGistUpdateMetaPage(Relation index)
|
||||||
|
|
||||||
/* Macro to select proper element of lastUsedPages cache depending on flags */
|
/* Macro to select proper element of lastUsedPages cache depending on flags */
|
||||||
#define GET_LUP(c, f) (((f) & GBUF_LEAF) ? \
|
#define GET_LUP(c, f) (((f) & GBUF_LEAF) ? \
|
||||||
&(c)->leafPage : \
|
&(c)->lastUsedPages.leafPage : \
|
||||||
&(c)->innerPage[(f) & GBUF_PARITY_MASK])
|
&(c)->lastUsedPages.innerPage[(f) & GBUF_PARITY_MASK])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate and initialize a new buffer of the type and parity specified by
|
* Allocate and initialize a new buffer of the type and parity specified by
|
||||||
|
@ -282,8 +280,8 @@ allocNewBuffer(Relation index, int flags)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Page has wrong parity, record it in cache and try again */
|
/* Page has wrong parity, record it in cache and try again */
|
||||||
cache->innerPage[blkParity].blkno = blkno;
|
cache->lastUsedPages.innerPage[blkParity].blkno = blkno;
|
||||||
cache->innerPage[blkParity].freeSpace =
|
cache->lastUsedPages.innerPage[blkParity].freeSpace =
|
||||||
PageGetExactFreeSpace(BufferGetPage(buffer));
|
PageGetExactFreeSpace(BufferGetPage(buffer));
|
||||||
UnlockReleaseBuffer(buffer);
|
UnlockReleaseBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ typedef struct spgConfigOut
|
||||||
{
|
{
|
||||||
Oid prefixType; /* Data type of inner-tuple prefixes */
|
Oid prefixType; /* Data type of inner-tuple prefixes */
|
||||||
Oid labelType; /* Data type of inner-tuple node labels */
|
Oid labelType; /* Data type of inner-tuple node labels */
|
||||||
|
bool canReturnData; /* Opclass can reconstruct original data */
|
||||||
bool longValuesOK; /* Opclass can cope with values > 1 page */
|
bool longValuesOK; /* Opclass can cope with values > 1 page */
|
||||||
} spgConfigOut;
|
} spgConfigOut;
|
||||||
|
|
||||||
|
@ -132,6 +133,7 @@ typedef struct spgInnerConsistentIn
|
||||||
|
|
||||||
Datum reconstructedValue; /* value reconstructed at parent */
|
Datum reconstructedValue; /* value reconstructed at parent */
|
||||||
int level; /* current level (counting from zero) */
|
int level; /* current level (counting from zero) */
|
||||||
|
bool returnData; /* original data must be returned? */
|
||||||
|
|
||||||
/* Data from current inner tuple */
|
/* Data from current inner tuple */
|
||||||
bool allTheSame; /* tuple is marked all-the-same? */
|
bool allTheSame; /* tuple is marked all-the-same? */
|
||||||
|
@ -159,12 +161,14 @@ typedef struct spgLeafConsistentIn
|
||||||
|
|
||||||
Datum reconstructedValue; /* value reconstructed at parent */
|
Datum reconstructedValue; /* value reconstructed at parent */
|
||||||
int level; /* current level (counting from zero) */
|
int level; /* current level (counting from zero) */
|
||||||
|
bool returnData; /* original data must be returned? */
|
||||||
|
|
||||||
Datum leafDatum; /* datum in leaf tuple */
|
Datum leafDatum; /* datum in leaf tuple */
|
||||||
} spgLeafConsistentIn;
|
} spgLeafConsistentIn;
|
||||||
|
|
||||||
typedef struct spgLeafConsistentOut
|
typedef struct spgLeafConsistentOut
|
||||||
{
|
{
|
||||||
|
Datum leafValue; /* reconstructed original data, if any */
|
||||||
bool recheck; /* set true if operator must be rechecked */
|
bool recheck; /* set true if operator must be rechecked */
|
||||||
} spgLeafConsistentOut;
|
} spgLeafConsistentOut;
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,11 @@ typedef struct SpGistLastUsedPage
|
||||||
int freeSpace; /* its free space (could be obsolete!) */
|
int freeSpace; /* its free space (could be obsolete!) */
|
||||||
} SpGistLastUsedPage;
|
} SpGistLastUsedPage;
|
||||||
|
|
||||||
typedef struct SpGistCache
|
typedef struct SpGistLUPCache
|
||||||
{
|
{
|
||||||
SpGistLastUsedPage innerPage[3]; /* one per triple-parity group */
|
SpGistLastUsedPage innerPage[3]; /* one per triple-parity group */
|
||||||
SpGistLastUsedPage leafPage;
|
SpGistLastUsedPage leafPage;
|
||||||
} SpGistCache;
|
} SpGistLUPCache;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* metapage
|
* metapage
|
||||||
|
@ -83,7 +83,7 @@ typedef struct SpGistCache
|
||||||
typedef struct SpGistMetaPageData
|
typedef struct SpGistMetaPageData
|
||||||
{
|
{
|
||||||
uint32 magicNumber; /* for identity cross-check */
|
uint32 magicNumber; /* for identity cross-check */
|
||||||
SpGistCache lastUsedPages; /* shared storage of last-used info */
|
SpGistLUPCache lastUsedPages; /* shared storage of last-used info */
|
||||||
} SpGistMetaPageData;
|
} SpGistMetaPageData;
|
||||||
|
|
||||||
#define SPGIST_MAGIC_NUMBER (0xBA0BABED)
|
#define SPGIST_MAGIC_NUMBER (0xBA0BABED)
|
||||||
|
@ -112,12 +112,6 @@ typedef struct SpGistState
|
||||||
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
|
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
|
||||||
SpGistTypeDesc attLabelType; /* type of node label values */
|
SpGistTypeDesc attLabelType; /* type of node label values */
|
||||||
|
|
||||||
/* lookup data for the opclass support functions, except config */
|
|
||||||
FmgrInfo chooseFn;
|
|
||||||
FmgrInfo picksplitFn;
|
|
||||||
FmgrInfo innerConsistentFn;
|
|
||||||
FmgrInfo leafConsistentFn;
|
|
||||||
|
|
||||||
char *deadTupleStorage; /* workspace for spgFormDeadTuple */
|
char *deadTupleStorage; /* workspace for spgFormDeadTuple */
|
||||||
|
|
||||||
TransactionId myXid; /* XID to use when creating a redirect tuple */
|
TransactionId myXid; /* XID to use when creating a redirect tuple */
|
||||||
|
@ -144,10 +138,13 @@ typedef struct SpGistScanOpaqueData
|
||||||
int64 ntids; /* number of TIDs passed to bitmap */
|
int64 ntids; /* number of TIDs passed to bitmap */
|
||||||
|
|
||||||
/* These fields are only used in amgettuple scans: */
|
/* These fields are only used in amgettuple scans: */
|
||||||
|
bool want_itup; /* are we reconstructing tuples? */
|
||||||
|
TupleDesc indexTupDesc; /* if so, tuple descriptor for them */
|
||||||
int nPtrs; /* number of TIDs found on current page */
|
int nPtrs; /* number of TIDs found on current page */
|
||||||
int iPtr; /* index for scanning through same */
|
int iPtr; /* index for scanning through same */
|
||||||
ItemPointerData heapPtrs[MaxIndexTuplesPerPage]; /* TIDs from cur page */
|
ItemPointerData heapPtrs[MaxIndexTuplesPerPage]; /* TIDs from cur page */
|
||||||
bool recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
|
bool recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
|
||||||
|
IndexTuple indexTups[MaxIndexTuplesPerPage]; /* reconstructed tuples */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: using MaxIndexTuplesPerPage above is a bit hokey since
|
* Note: using MaxIndexTuplesPerPage above is a bit hokey since
|
||||||
|
@ -158,6 +155,21 @@ typedef struct SpGistScanOpaqueData
|
||||||
|
|
||||||
typedef SpGistScanOpaqueData *SpGistScanOpaque;
|
typedef SpGistScanOpaqueData *SpGistScanOpaque;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This struct is what we actually keep in index->rd_amcache. It includes
|
||||||
|
* static configuration information as well as the lastUsedPages cache.
|
||||||
|
*/
|
||||||
|
typedef struct SpGistCache
|
||||||
|
{
|
||||||
|
spgConfigOut config; /* filled in by opclass config method */
|
||||||
|
|
||||||
|
SpGistTypeDesc attType; /* type of input data and leaf values */
|
||||||
|
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
|
||||||
|
SpGistTypeDesc attLabelType; /* type of node label values */
|
||||||
|
|
||||||
|
SpGistLUPCache lastUsedPages; /* local storage of last-used info */
|
||||||
|
} SpGistCache;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPGiST tuple types. Note: inner, leaf, and dead tuple structs
|
* SPGiST tuple types. Note: inner, leaf, and dead tuple structs
|
||||||
|
@ -570,6 +582,7 @@ typedef struct spgxlogVacuumRedirect
|
||||||
#define GBUF_INNER_PARITY(x) ((x) % 3)
|
#define GBUF_INNER_PARITY(x) ((x) % 3)
|
||||||
|
|
||||||
/* spgutils.c */
|
/* spgutils.c */
|
||||||
|
extern SpGistCache *spgGetCache(Relation index);
|
||||||
extern void initSpGistState(SpGistState *state, Relation index);
|
extern void initSpGistState(SpGistState *state, Relation index);
|
||||||
extern Buffer SpGistNewBuffer(Relation index);
|
extern Buffer SpGistNewBuffer(Relation index);
|
||||||
extern void SpGistUpdateMetaPage(Relation index);
|
extern void SpGistUpdateMetaPage(Relation index);
|
||||||
|
|
|
@ -681,9 +681,9 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -696,10 +696,10 @@ SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: ('(1000,1000),(200,200)'::box @> p)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
|
@ -711,9 +711,9 @@ SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)';
|
SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p << '(5000,4000)'::point)
|
Index Cond: (p << '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -726,9 +726,9 @@ SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)';
|
SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p >> '(5000,4000)'::point)
|
Index Cond: (p >> '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -741,9 +741,9 @@ SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)';
|
SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p <^ '(5000,4000)'::point)
|
Index Cond: (p <^ '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -756,9 +756,9 @@ SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)';
|
SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p >^ '(5000,4000)'::point)
|
Index Cond: (p >^ '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -771,9 +771,9 @@ SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)';
|
SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_quad_ind on quad_point_tbl
|
-> Index Only Scan using sp_quad_ind on quad_point_tbl
|
||||||
Index Cond: (p ~= '(4585,365)'::point)
|
Index Cond: (p ~= '(4585,365)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -788,7 +788,7 @@ SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -803,8 +803,8 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: ('(1000,1000),(200,200)'::box @> p)
|
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
|
@ -816,9 +816,9 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)';
|
SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p << '(5000,4000)'::point)
|
Index Cond: (p << '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -831,9 +831,9 @@ SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)';
|
SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p >> '(5000,4000)'::point)
|
Index Cond: (p >> '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -846,9 +846,9 @@ SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)';
|
SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p <^ '(5000,4000)'::point)
|
Index Cond: (p <^ '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -861,9 +861,9 @@ SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)';
|
SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p >^ '(5000,4000)'::point)
|
Index Cond: (p >^ '(5000,4000)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -876,9 +876,9 @@ SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)';
|
SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
-------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_kd_ind on kd_point_tbl
|
-> Index Only Scan using sp_kd_ind on kd_point_tbl
|
||||||
Index Cond: (p ~= '(4585,365)'::point)
|
Index Cond: (p ~= '(4585,365)'::point)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -891,9 +891,9 @@ SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdef';
|
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdef';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t = '0123456789abcdef'::text)
|
Index Cond: (t = '0123456789abcdef'::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -906,9 +906,9 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdef';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcde';
|
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcde';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t = '0123456789abcde'::text)
|
Index Cond: (t = '0123456789abcde'::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -921,9 +921,9 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcde';
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdefF';
|
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdefF';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t = '0123456789abcdefF'::text)
|
Index Cond: (t = '0123456789abcdefF'::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -938,7 +938,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t < 'Aztec
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t < 'Aztec Ct '::text)
|
Index Cond: (t < 'Aztec Ct '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -953,7 +953,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~<~ 'Aztec
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t ~<~ 'Aztec Ct '::text)
|
Index Cond: (t ~<~ 'Aztec Ct '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -968,7 +968,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t <= 'Aztec
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t <= 'Aztec Ct '::text)
|
Index Cond: (t <= 'Aztec Ct '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -983,7 +983,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~<=~ 'Aztec
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t ~<=~ 'Aztec Ct '::text)
|
Index Cond: (t ~<=~ 'Aztec Ct '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -998,7 +998,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = 'Aztec
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t = 'Aztec Ct '::text)
|
Index Cond: (t = 'Aztec Ct '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -1013,7 +1013,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = 'Worth
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t = 'Worth St '::text)
|
Index Cond: (t = 'Worth St '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -1028,7 +1028,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t >= 'Worth
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t >= 'Worth St '::text)
|
Index Cond: (t >= 'Worth St '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -1043,7 +1043,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~>=~ 'Worth
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t ~>=~ 'Worth St '::text)
|
Index Cond: (t ~>=~ 'Worth St '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -1058,7 +1058,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t > 'Worth
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t > 'Worth St '::text)
|
Index Cond: (t > 'Worth St '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
@ -1073,7 +1073,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~>~ 'Worth
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
Aggregate
|
Aggregate
|
||||||
-> Index Scan using sp_suff_ind on suffix_text_tbl
|
-> Index Only Scan using sp_suff_ind on suffix_text_tbl
|
||||||
Index Cond: (t ~>~ 'Worth St '::text)
|
Index Cond: (t ~>~ 'Worth St '::text)
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue