aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/spgist/spgscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/spgist/spgscan.c')
-rw-r--r--src/backend/access/spgist/spgscan.c345
1 files changed, 170 insertions, 175 deletions
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index 22cfcc87929..99b0852611f 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -56,18 +56,25 @@ freeScanStack(SpGistScanOpaque so)
}
/*
- * Initialize scanStack with a single entry for the root page, resetting
+ * Initialize scanStack to search the root page, resetting
* any previously active scan
*/
static void
resetSpGistScanOpaque(SpGistScanOpaque so)
{
- ScanStackEntry *startEntry = palloc0(sizeof(ScanStackEntry));
-
- ItemPointerSet(&startEntry->ptr, SPGIST_HEAD_BLKNO, FirstOffsetNumber);
+ ScanStackEntry *startEntry;
freeScanStack(so);
- so->scanStack = list_make1(startEntry);
+
+ Assert(!so->searchNulls); /* XXX fixme */
+
+ if (so->searchNonNulls)
+ {
+ /* Stack a work item to scan the non-null index entries */
+ startEntry = (ScanStackEntry *) palloc0(sizeof(ScanStackEntry));
+ ItemPointerSet(&startEntry->ptr, SPGIST_HEAD_BLKNO, FirstOffsetNumber);
+ so->scanStack = list_make1(startEntry);
+ }
if (so->want_itup)
{
@@ -80,6 +87,82 @@ resetSpGistScanOpaque(SpGistScanOpaque so)
so->iPtr = so->nPtrs = 0;
}
+/*
+ * Prepare scan keys in SpGistScanOpaque from caller-given scan keys
+ *
+ * Sets searchNulls, searchNonNulls, numberOfKeys, keyData fields of *so.
+ *
+ * The point here is to eliminate null-related considerations from what the
+ * opclass consistent functions need to deal with. We assume all SPGiST-
+ * indexable operators are strict, so any null RHS value makes the scan
+ * condition unsatisfiable. We also pull out any IS NULL/IS NOT NULL
+ * conditions; their effect is reflected into searchNulls/searchNonNulls.
+ */
+static void
+spgPrepareScanKeys(IndexScanDesc scan)
+{
+ SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
+ bool qual_ok;
+ bool haveIsNull;
+ bool haveNotNull;
+ int nkeys;
+ int i;
+
+ if (scan->numberOfKeys <= 0)
+ {
+ /* If no quals, whole-index scan is required */
+ so->searchNulls = true;
+ so->searchNonNulls = true;
+ so->numberOfKeys = 0;
+ return;
+ }
+
+ /* Examine the given quals */
+ qual_ok = true;
+ haveIsNull = haveNotNull = false;
+ nkeys = 0;
+ for (i = 0; i < scan->numberOfKeys; i++)
+ {
+ ScanKey skey = &scan->keyData[i];
+
+ if (skey->sk_flags & SK_SEARCHNULL)
+ haveIsNull = true;
+ else if (skey->sk_flags & SK_SEARCHNOTNULL)
+ haveNotNull = true;
+ else if (skey->sk_flags & SK_ISNULL)
+ {
+ /* ordinary qual with null argument - unsatisfiable */
+ qual_ok = false;
+ break;
+ }
+ else
+ {
+ /* ordinary qual, propagate into so->keyData */
+ so->keyData[nkeys++] = *skey;
+ /* this effectively creates a not-null requirement */
+ haveNotNull = true;
+ }
+ }
+
+ /* IS NULL in combination with something else is unsatisfiable */
+ if (haveIsNull && haveNotNull)
+ qual_ok = false;
+
+ /* Emit results */
+ if (qual_ok)
+ {
+ so->searchNulls = haveIsNull;
+ so->searchNonNulls = haveNotNull;
+ so->numberOfKeys = nkeys;
+ }
+ else
+ {
+ so->searchNulls = false;
+ so->searchNonNulls = false;
+ so->numberOfKeys = 0;
+ }
+}
+
Datum
spgbeginscan(PG_FUNCTION_ARGS)
{
@@ -92,13 +175,16 @@ spgbeginscan(PG_FUNCTION_ARGS)
scan = RelationGetIndexScan(rel, keysz, 0);
so = (SpGistScanOpaque) palloc0(sizeof(SpGistScanOpaqueData));
+ if (keysz > 0)
+ so->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * keysz);
+ else
+ so->keyData = NULL;
initSpGistState(&so->state, scan->indexRelation);
so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
"SP-GiST search temporary context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
- resetSpGistScanOpaque(so);
/* Set up indexTupDesc and xs_itupdesc in case it's an index-only scan */
so->indexTupDesc = scan->xs_itupdesc = RelationGetDescr(rel);
@@ -115,12 +201,17 @@ spgrescan(PG_FUNCTION_ARGS)
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
+ /* copy scankeys into local storage */
if (scankey && scan->numberOfKeys > 0)
{
memmove(scan->keyData, scankey,
scan->numberOfKeys * sizeof(ScanKeyData));
}
+ /* preprocess scankeys, set up the representation in *so */
+ spgPrepareScanKeys(scan);
+
+ /* set up starting stack entries */
resetSpGistScanOpaque(so);
PG_RETURN_VOID();
@@ -162,53 +253,34 @@ spgLeafTest(Relation index, SpGistScanOpaque so, Datum leafDatum,
int level, Datum reconstructedValue,
Datum *leafValue, bool *recheck)
{
- bool result = true;
+ bool result;
spgLeafConsistentIn in;
spgLeafConsistentOut out;
FmgrInfo *procinfo;
MemoryContext oldCtx;
- int i;
- *leafValue = (Datum) 0;
- *recheck = false;
+ /* use temp context for calling leaf_consistent */
+ oldCtx = MemoryContextSwitchTo(so->tempCxt);
- /* set up values that are the same for all quals */
+ in.scankeys = so->keyData;
+ in.nkeys = so->numberOfKeys;
in.reconstructedValue = reconstructedValue;
in.level = level;
in.returnData = so->want_itup;
in.leafDatum = leafDatum;
- /* Apply each leaf consistency check, working in the temp context */
- oldCtx = MemoryContextSwitchTo(so->tempCxt);
+ out.leafValue = (Datum) 0;
+ out.recheck = false;
procinfo = index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC);
+ result = DatumGetBool(FunctionCall2Coll(procinfo,
+ index->rd_indcollation[0],
+ PointerGetDatum(&in),
+ PointerGetDatum(&out)));
- for (i = 0; i < so->numberOfKeys; i++)
- {
- ScanKey skey = &so->keyData[i];
-
- /* Assume SPGiST-indexable operators are strict */
- if (skey->sk_flags & SK_ISNULL)
- {
- result = false;
- break;
- }
+ *leafValue = out.leafValue;
+ *recheck = out.recheck;
- in.strategy = skey->sk_strategy;
- in.query = skey->sk_argument;
-
- out.leafValue = (Datum) 0;
- out.recheck = false;
-
- result = DatumGetBool(FunctionCall2Coll(procinfo,
- skey->sk_collation,
- PointerGetDatum(&in),
- PointerGetDatum(&out)));
- *leafValue = out.leafValue;
- *recheck |= out.recheck;
- if (!result)
- break;
- }
MemoryContextSwitchTo(oldCtx);
return result;
@@ -349,8 +421,13 @@ redirect:
else /* page is inner */
{
SpGistInnerTuple innerTuple;
+ spgInnerConsistentIn in;
+ spgInnerConsistentOut out;
+ FmgrInfo *procinfo;
+ SpGistNodeTuple *nodes;
SpGistNodeTuple node;
int i;
+ MemoryContext oldCtx;
innerTuple = (SpGistInnerTuple) PageGetItem(page,
PageGetItemId(page, offset));
@@ -368,144 +445,68 @@ redirect:
innerTuple->tupstate);
}
- if (so->numberOfKeys == 0)
+ /* use temp context for calling inner_consistent */
+ oldCtx = MemoryContextSwitchTo(so->tempCxt);
+
+ in.scankeys = so->keyData;
+ in.nkeys = so->numberOfKeys;
+ in.reconstructedValue = stackEntry->reconstructedValue;
+ in.level = stackEntry->level;
+ in.returnData = so->want_itup;
+ in.allTheSame = innerTuple->allTheSame;
+ in.hasPrefix = (innerTuple->prefixSize > 0);
+ in.prefixDatum = SGITDATUM(innerTuple, &so->state);
+ in.nNodes = innerTuple->nNodes;
+ in.nodeLabels = spgExtractNodeLabels(&so->state, innerTuple);
+
+ /* collect node pointers */
+ nodes = (SpGistNodeTuple *) palloc(sizeof(SpGistNodeTuple) * in.nNodes);
+ SGITITERATE(innerTuple, i, node)
{
- /*
- * This case cannot happen at the moment, because we don't
- * set pg_am.amoptionalkey for SP-GiST. In order for full
- * index scans to produce correct answers, we'd need to
- * index nulls, which we don't.
- */
- Assert(false);
-
-#ifdef NOT_USED
- /*
- * A full index scan could be done approximately like this,
- * but note that reconstruction of indexed values would be
- * impossible unless the API for inner_consistent is changed.
- */
- SGITITERATE(innerTuple, i, node)
- {
- if (ItemPointerIsValid(&node->t_tid))
- {
- ScanStackEntry *newEntry = palloc(sizeof(ScanStackEntry));
-
- newEntry->ptr = node->t_tid;
- newEntry->level = -1;
- newEntry->reconstructedValue = (Datum) 0;
- so->scanStack = lcons(newEntry, so->scanStack);
- }
- }
-#endif
+ nodes[i] = node;
}
- else
- {
- spgInnerConsistentIn in;
- spgInnerConsistentOut out;
- FmgrInfo *procinfo;
- SpGistNodeTuple *nodes;
- int *andMap;
- int *levelAdds;
- Datum *reconstructedValues;
- int j,
- nMatches = 0;
- MemoryContext oldCtx;
-
- /* use temp context for calling inner_consistent */
- oldCtx = MemoryContextSwitchTo(so->tempCxt);
-
- /* set up values that are the same for all scankeys */
- in.reconstructedValue = stackEntry->reconstructedValue;
- in.level = stackEntry->level;
- in.returnData = so->want_itup;
- in.allTheSame = innerTuple->allTheSame;
- in.hasPrefix = (innerTuple->prefixSize > 0);
- in.prefixDatum = SGITDATUM(innerTuple, &so->state);
- in.nNodes = innerTuple->nNodes;
- in.nodeLabels = spgExtractNodeLabels(&so->state, innerTuple);
-
- /* collect node pointers */
- nodes = (SpGistNodeTuple *) palloc(sizeof(SpGistNodeTuple) * in.nNodes);
- SGITITERATE(innerTuple, i, node)
- {
- nodes[i] = node;
- }
- andMap = (int *) palloc0(sizeof(int) * in.nNodes);
- levelAdds = (int *) palloc0(sizeof(int) * in.nNodes);
- reconstructedValues = (Datum *) palloc0(sizeof(Datum) * in.nNodes);
+ memset(&out, 0, sizeof(out));
- procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
+ procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
+ FunctionCall2Coll(procinfo,
+ index->rd_indcollation[0],
+ PointerGetDatum(&in),
+ PointerGetDatum(&out));
- for (j = 0; j < so->numberOfKeys; j++)
- {
- ScanKey skey = &so->keyData[j];
-
- /* Assume SPGiST-indexable operators are strict */
- if (skey->sk_flags & SK_ISNULL)
- {
- nMatches = 0;
- break;
- }
-
- in.strategy = skey->sk_strategy;
- in.query = skey->sk_argument;
+ MemoryContextSwitchTo(oldCtx);
- memset(&out, 0, sizeof(out));
-
- FunctionCall2Coll(procinfo,
- skey->sk_collation,
- PointerGetDatum(&in),
- PointerGetDatum(&out));
-
- /* If allTheSame, they should all or none of 'em match */
- if (innerTuple->allTheSame)
- if (out.nNodes != 0 && out.nNodes != in.nNodes)
- elog(ERROR, "inconsistent inner_consistent results for allTheSame inner tuple");
-
- nMatches = 0;
- for (i = 0; i < out.nNodes; i++)
- {
- int nodeN = out.nodeNumbers[i];
-
- andMap[nodeN]++;
- if (andMap[nodeN] == j + 1)
- nMatches++;
- if (out.levelAdds)
- levelAdds[nodeN] = out.levelAdds[i];
- if (out.reconstructedValues)
- reconstructedValues[nodeN] = out.reconstructedValues[i];
- }
+ /* If allTheSame, they should all or none of 'em match */
+ if (innerTuple->allTheSame)
+ if (out.nNodes != 0 && out.nNodes != in.nNodes)
+ elog(ERROR, "inconsistent inner_consistent results for allTheSame inner tuple");
- /* quit as soon as all nodes have failed some qual */
- if (nMatches == 0)
- break;
- }
-
- MemoryContextSwitchTo(oldCtx);
+ for (i = 0; i < out.nNodes; i++)
+ {
+ int nodeN = out.nodeNumbers[i];
- if (nMatches > 0)
+ Assert(nodeN >= 0 && nodeN < in.nNodes);
+ if (ItemPointerIsValid(&nodes[nodeN]->t_tid))
{
- for (i = 0; i < in.nNodes; i++)
- {
- if (andMap[i] == so->numberOfKeys &&
- ItemPointerIsValid(&nodes[i]->t_tid))
- {
- ScanStackEntry *newEntry;
-
- /* Create new work item for this node */
- newEntry = palloc(sizeof(ScanStackEntry));
- newEntry->ptr = nodes[i]->t_tid;
- newEntry->level = stackEntry->level + levelAdds[i];
- /* Must copy value out of temp context */
- newEntry->reconstructedValue =
- datumCopy(reconstructedValues[i],
- so->state.attType.attbyval,
- so->state.attType.attlen);
-
- so->scanStack = lcons(newEntry, so->scanStack);
- }
- }
+ ScanStackEntry *newEntry;
+
+ /* Create new work item for this node */
+ newEntry = palloc(sizeof(ScanStackEntry));
+ newEntry->ptr = nodes[nodeN]->t_tid;
+ if (out.levelAdds)
+ newEntry->level = stackEntry->level + out.levelAdds[i];
+ else
+ newEntry->level = stackEntry->level;
+ /* Must copy value out of temp context */
+ if (out.reconstructedValues)
+ newEntry->reconstructedValue =
+ datumCopy(out.reconstructedValues[i],
+ so->state.attType.attbyval,
+ so->state.attType.attlen);
+ else
+ newEntry->reconstructedValue = (Datum) 0;
+
+ so->scanStack = lcons(newEntry, so->scanStack);
}
}
}
@@ -536,10 +537,7 @@ spggetbitmap(PG_FUNCTION_ARGS)
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
- /* Copy scankey to *so so we don't need to pass it around separately */
- so->numberOfKeys = scan->numberOfKeys;
- so->keyData = scan->keyData;
- /* Ditto for the want_itup flag */
+ /* Copy want_itup to *so so we don't need to pass it around separately */
so->want_itup = false;
so->tbm = tbm;
@@ -583,10 +581,7 @@ spggettuple(PG_FUNCTION_ARGS)
if (dir != ForwardScanDirection)
elog(ERROR, "SP-GiST only supports forward scan direction");
- /* Copy scankey to *so so we don't need to pass it around separately */
- so->numberOfKeys = scan->numberOfKeys;
- so->keyData = scan->keyData;
- /* Ditto for the want_itup flag */
+ /* Copy want_itup to *so so we don't need to pass it around separately */
so->want_itup = scan->xs_want_itup;
for (;;)