mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Restructure SPGiST opclass interface API to support whole-index scans.
The original API definition was incapable of supporting whole-index scans because there was no way to invoke leaf-value reconstruction without checking any qual conditions. Also, it was inefficient for multiple-qual-condition scans because value reconstruction got done over again for each qual condition, and because other internal work in the consistent functions likewise had to be done for each qual. To fix these issues, pass the whole scankey array to the opclass consistent functions, instead of only letting them see one item at a time. (Essentially, the loop over scankey entries is now inside the consistent functions not outside them. This makes the consistent functions a bit more complicated, but not unreasonably so.) In itself this commit does nothing except save a few cycles in multiple-qual-condition index scans, since we can't support whole-index scans on SPGiST indexes until nulls are included in the index. However, I consider this a must-fix for 9.2 because once we release it will get very much harder to change the opclass API definition.
This commit is contained in:
parent
39d74e346c
commit
03e56f798e
@ -439,8 +439,8 @@ CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
typedef struct spgInnerConsistentIn
|
typedef struct spgInnerConsistentIn
|
||||||
{
|
{
|
||||||
StrategyNumber strategy; /* operator strategy number */
|
ScanKey scankeys; /* array of operators and comparison values */
|
||||||
Datum query; /* operator's RHS value */
|
int nkeys; /* length of array */
|
||||||
|
|
||||||
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) */
|
||||||
@ -463,8 +463,17 @@ typedef struct spgInnerConsistentOut
|
|||||||
} spgInnerConsistentOut;
|
} spgInnerConsistentOut;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
<structfield>strategy</> and
|
The array <structfield>scankeys</>, of length <structfield>nkeys</>,
|
||||||
<structfield>query</> describe the index search condition.
|
describes the index search condition(s). These conditions are
|
||||||
|
combined with AND — only index entries that satisfy all of
|
||||||
|
them are interesting. (Note that <structfield>nkeys</> = 0 implies
|
||||||
|
that all index entries satisfy the query.) Usually the consistent
|
||||||
|
function only cares about the <structfield>sk_strategy</> and
|
||||||
|
<structfield>sk_argument</> fields of each array entry, which
|
||||||
|
respectively give the indexable operator and comparison value.
|
||||||
|
In particular it is not necessary to check <structfield>sk_flags</> to
|
||||||
|
see if the comparison value is NULL, because the SP-GiST core code
|
||||||
|
will filter out such conditions.
|
||||||
<structfield>reconstructedValue</> is the value reconstructed for the
|
<structfield>reconstructedValue</> is the value reconstructed for the
|
||||||
parent tuple; it is <literal>(Datum) 0</> at the root level or if the
|
parent tuple; it is <literal>(Datum) 0</> at the root level or if the
|
||||||
<function>inner_consistent</> function did not provide a value at the
|
<function>inner_consistent</> function did not provide a value at the
|
||||||
@ -527,8 +536,8 @@ CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
typedef struct spgLeafConsistentIn
|
typedef struct spgLeafConsistentIn
|
||||||
{
|
{
|
||||||
StrategyNumber strategy; /* operator strategy number */
|
ScanKey scankeys; /* array of operators and comparison values */
|
||||||
Datum query; /* operator's RHS value */
|
int nkeys; /* length of array */
|
||||||
|
|
||||||
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) */
|
||||||
@ -544,8 +553,17 @@ typedef struct spgLeafConsistentOut
|
|||||||
} spgLeafConsistentOut;
|
} spgLeafConsistentOut;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
<structfield>strategy</> and
|
The array <structfield>scankeys</>, of length <structfield>nkeys</>,
|
||||||
<structfield>query</> define the index search condition.
|
describes the index search condition(s). These conditions are
|
||||||
|
combined with AND — only index entries that satisfy all of
|
||||||
|
them satisfy the query. (Note that <structfield>nkeys</> = 0 implies
|
||||||
|
that all index entries satisfy the query.) Usually the consistent
|
||||||
|
function only cares about the <structfield>sk_strategy</> and
|
||||||
|
<structfield>sk_argument</> fields of each array entry, which
|
||||||
|
respectively give the indexable operator and comparison value.
|
||||||
|
In particular it is not necessary to check <structfield>sk_flags</> to
|
||||||
|
see if the comparison value is NULL, because the SP-GiST core code
|
||||||
|
will filter out such conditions.
|
||||||
<structfield>reconstructedValue</> is the value reconstructed for the
|
<structfield>reconstructedValue</> is the value reconstructed for the
|
||||||
parent tuple; it is <literal>(Datum) 0</> at the root level or if the
|
parent tuple; it is <literal>(Datum) 0</> at the root level or if the
|
||||||
<function>inner_consistent</> function did not provide a value at the
|
<function>inner_consistent</> function did not provide a value at the
|
||||||
@ -566,8 +584,8 @@ typedef struct spgLeafConsistentOut
|
|||||||
<structfield>leafValue</> must be set to the value originally supplied
|
<structfield>leafValue</> must be set to the value originally supplied
|
||||||
to be indexed for this leaf tuple. Also,
|
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(s) must be re-applied to the actual
|
||||||
tuple to verify the match.
|
heap tuple to verify the match.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -159,11 +159,10 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
||||||
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
||||||
Point *query;
|
|
||||||
BOX *boxQuery;
|
|
||||||
double coord;
|
double coord;
|
||||||
|
int which;
|
||||||
|
int i;
|
||||||
|
|
||||||
query = DatumGetPointP(in->query);
|
|
||||||
Assert(in->hasPrefix);
|
Assert(in->hasPrefix);
|
||||||
coord = DatumGetFloat8(in->prefixDatum);
|
coord = DatumGetFloat8(in->prefixDatum);
|
||||||
|
|
||||||
@ -171,123 +170,96 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
elog(ERROR, "allTheSame should not occur for k-d trees");
|
elog(ERROR, "allTheSame should not occur for k-d trees");
|
||||||
|
|
||||||
Assert(in->nNodes == 2);
|
Assert(in->nNodes == 2);
|
||||||
|
|
||||||
|
/* "which" is a bitmask of children that satisfy all constraints */
|
||||||
|
which = (1 << 1) | (1 << 2);
|
||||||
|
|
||||||
|
for (i = 0; i < in->nkeys; i++)
|
||||||
|
{
|
||||||
|
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
|
||||||
|
BOX *boxQuery;
|
||||||
|
|
||||||
|
switch (in->scankeys[i].sk_strategy)
|
||||||
|
{
|
||||||
|
case RTLeftStrategyNumber:
|
||||||
|
if ((in->level % 2) != 0 && FPlt(query->x, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
break;
|
||||||
|
case RTRightStrategyNumber:
|
||||||
|
if ((in->level % 2) != 0 && FPgt(query->x, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
break;
|
||||||
|
case RTSameStrategyNumber:
|
||||||
|
if ((in->level % 2) != 0)
|
||||||
|
{
|
||||||
|
if (FPlt(query->x, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
else if (FPgt(query->x, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (FPlt(query->y, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
else if (FPgt(query->y, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RTBelowStrategyNumber:
|
||||||
|
if ((in->level % 2) == 0 && FPlt(query->y, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
break;
|
||||||
|
case RTAboveStrategyNumber:
|
||||||
|
if ((in->level % 2) == 0 && FPgt(query->y, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
break;
|
||||||
|
case RTContainedByStrategyNumber:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For this operator, the query is a box not a point. We
|
||||||
|
* cheat to the extent of assuming that DatumGetPointP won't
|
||||||
|
* do anything that would be bad for a pointer-to-box.
|
||||||
|
*/
|
||||||
|
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
|
||||||
|
|
||||||
|
if ((in->level % 2) != 0)
|
||||||
|
{
|
||||||
|
if (FPlt(boxQuery->high.x, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
else if (FPgt(boxQuery->low.x, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (FPlt(boxQuery->high.y, coord))
|
||||||
|
which &= (1 << 1);
|
||||||
|
else if (FPgt(boxQuery->low.y, coord))
|
||||||
|
which &= (1 << 2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
in->scankeys[i].sk_strategy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (which == 0)
|
||||||
|
break; /* no need to consider remaining conditions */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We must descend into the children identified by which */
|
||||||
out->nodeNumbers = (int *) palloc(sizeof(int) * 2);
|
out->nodeNumbers = (int *) palloc(sizeof(int) * 2);
|
||||||
|
out->nNodes = 0;
|
||||||
|
for (i = 1; i <= 2; i++)
|
||||||
|
{
|
||||||
|
if (which & (1 << i))
|
||||||
|
out->nodeNumbers[out->nNodes++] = i - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up level increments, too */
|
||||||
out->levelAdds = (int *) palloc(sizeof(int) * 2);
|
out->levelAdds = (int *) palloc(sizeof(int) * 2);
|
||||||
out->levelAdds[0] = 1;
|
out->levelAdds[0] = 1;
|
||||||
out->levelAdds[1] = 1;
|
out->levelAdds[1] = 1;
|
||||||
out->nNodes = 0;
|
|
||||||
|
|
||||||
switch (in->strategy)
|
|
||||||
{
|
|
||||||
case RTLeftStrategyNumber:
|
|
||||||
out->nNodes = 1;
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
|
|
||||||
if ((in->level % 2) == 0 || FPge(query->x, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[1] = 1;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTRightStrategyNumber:
|
|
||||||
out->nNodes = 1;
|
|
||||||
out->nodeNumbers[0] = 1;
|
|
||||||
|
|
||||||
if ((in->level % 2) == 0 || FPle(query->x, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[1] = 0;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTSameStrategyNumber:
|
|
||||||
if (in->level % 2)
|
|
||||||
{
|
|
||||||
if (FPle(query->x, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[out->nNodes] = 0;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
if (FPge(query->x, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[out->nNodes] = 1;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FPle(query->y, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[out->nNodes] = 0;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
if (FPge(query->y, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[out->nNodes] = 1;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTBelowStrategyNumber:
|
|
||||||
out->nNodes = 1;
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
|
|
||||||
if ((in->level % 2) == 1 || FPge(query->y, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[1] = 1;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTAboveStrategyNumber:
|
|
||||||
out->nNodes = 1;
|
|
||||||
out->nodeNumbers[0] = 1;
|
|
||||||
|
|
||||||
if ((in->level % 2) == 1 || FPle(query->y, coord))
|
|
||||||
{
|
|
||||||
out->nodeNumbers[1] = 0;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RTContainedByStrategyNumber:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For this operator, the query is a box not a point. We cheat to
|
|
||||||
* the extent of assuming that DatumGetPointP won't do anything
|
|
||||||
* that would be bad for a pointer-to-box.
|
|
||||||
*/
|
|
||||||
boxQuery = DatumGetBoxP(in->query);
|
|
||||||
|
|
||||||
out->nNodes = 1;
|
|
||||||
if (in->level % 2)
|
|
||||||
{
|
|
||||||
if (FPlt(boxQuery->high.x, coord))
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
else if (FPgt(boxQuery->low.x, coord))
|
|
||||||
out->nodeNumbers[0] = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
out->nodeNumbers[1] = 1;
|
|
||||||
out->nNodes = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FPlt(boxQuery->high.y, coord))
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
else if (FPgt(boxQuery->low.y, coord))
|
|
||||||
out->nodeNumbers[0] = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
out->nodeNumbers[1] = 1;
|
|
||||||
out->nNodes = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unrecognized strategy number: %d", in->strategy);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
@ -190,45 +190,21 @@ spg_quad_picksplit(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Subroutine to fill out->nodeNumbers[] for spg_quad_inner_consistent */
|
|
||||||
static void
|
|
||||||
setNodes(spgInnerConsistentOut *out, bool isAll, int first, int second)
|
|
||||||
{
|
|
||||||
if (isAll)
|
|
||||||
{
|
|
||||||
out->nNodes = 4;
|
|
||||||
out->nodeNumbers[0] = 0;
|
|
||||||
out->nodeNumbers[1] = 1;
|
|
||||||
out->nodeNumbers[2] = 2;
|
|
||||||
out->nodeNumbers[3] = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out->nNodes = 2;
|
|
||||||
out->nodeNumbers[0] = first - 1;
|
|
||||||
out->nodeNumbers[1] = second - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
spg_quad_inner_consistent(PG_FUNCTION_ARGS)
|
spg_quad_inner_consistent(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
||||||
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
||||||
Point *query,
|
Point *centroid;
|
||||||
*centroid;
|
int which;
|
||||||
BOX *boxQuery;
|
int i;
|
||||||
|
|
||||||
query = DatumGetPointP(in->query);
|
|
||||||
Assert(in->hasPrefix);
|
Assert(in->hasPrefix);
|
||||||
centroid = DatumGetPointP(in->prefixDatum);
|
centroid = DatumGetPointP(in->prefixDatum);
|
||||||
|
|
||||||
if (in->allTheSame)
|
if (in->allTheSame)
|
||||||
{
|
{
|
||||||
/* Report that all nodes should be visited */
|
/* Report that all nodes should be visited */
|
||||||
int i;
|
|
||||||
|
|
||||||
out->nNodes = in->nNodes;
|
out->nNodes = in->nNodes;
|
||||||
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
|
out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
|
||||||
for (i = 0; i < in->nNodes; i++)
|
for (i = 0; i < in->nNodes; i++)
|
||||||
@ -237,76 +213,86 @@ spg_quad_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Assert(in->nNodes == 4);
|
Assert(in->nNodes == 4);
|
||||||
out->nodeNumbers = (int *) palloc(sizeof(int) * 4);
|
|
||||||
|
|
||||||
switch (in->strategy)
|
/* "which" is a bitmask of quadrants that satisfy all constraints */
|
||||||
|
which = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
|
||||||
|
|
||||||
|
for (i = 0; i < in->nkeys; i++)
|
||||||
{
|
{
|
||||||
case RTLeftStrategyNumber:
|
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
|
||||||
setNodes(out, SPTEST(point_left, centroid, query), 3, 4);
|
BOX *boxQuery;
|
||||||
break;
|
|
||||||
case RTRightStrategyNumber:
|
|
||||||
setNodes(out, SPTEST(point_right, centroid, query), 1, 2);
|
|
||||||
break;
|
|
||||||
case RTSameStrategyNumber:
|
|
||||||
out->nNodes = 1;
|
|
||||||
out->nodeNumbers[0] = getQuadrant(centroid, query) - 1;
|
|
||||||
break;
|
|
||||||
case RTBelowStrategyNumber:
|
|
||||||
setNodes(out, SPTEST(point_below, centroid, query), 2, 3);
|
|
||||||
break;
|
|
||||||
case RTAboveStrategyNumber:
|
|
||||||
setNodes(out, SPTEST(point_above, centroid, query), 1, 4);
|
|
||||||
break;
|
|
||||||
case RTContainedByStrategyNumber:
|
|
||||||
|
|
||||||
/*
|
switch (in->scankeys[i].sk_strategy)
|
||||||
* For this operator, the query is a box not a point. We cheat to
|
{
|
||||||
* the extent of assuming that DatumGetPointP won't do anything
|
case RTLeftStrategyNumber:
|
||||||
* that would be bad for a pointer-to-box.
|
if (SPTEST(point_right, centroid, query))
|
||||||
*/
|
which &= (1 << 3) | (1 << 4);
|
||||||
boxQuery = DatumGetBoxP(in->query);
|
break;
|
||||||
|
case RTRightStrategyNumber:
|
||||||
|
if (SPTEST(point_left, centroid, query))
|
||||||
|
which &= (1 << 1) | (1 << 2);
|
||||||
|
break;
|
||||||
|
case RTSameStrategyNumber:
|
||||||
|
which &= (1 << getQuadrant(centroid, query));
|
||||||
|
break;
|
||||||
|
case RTBelowStrategyNumber:
|
||||||
|
if (SPTEST(point_above, centroid, query))
|
||||||
|
which &= (1 << 2) | (1 << 3);
|
||||||
|
break;
|
||||||
|
case RTAboveStrategyNumber:
|
||||||
|
if (SPTEST(point_below, centroid, query))
|
||||||
|
which &= (1 << 1) | (1 << 4);
|
||||||
|
break;
|
||||||
|
case RTContainedByStrategyNumber:
|
||||||
|
|
||||||
if (DatumGetBool(DirectFunctionCall2(box_contain_pt,
|
/*
|
||||||
PointerGetDatum(boxQuery),
|
* For this operator, the query is a box not a point. We
|
||||||
PointerGetDatum(centroid))))
|
* cheat to the extent of assuming that DatumGetPointP won't
|
||||||
{
|
* do anything that would be bad for a pointer-to-box.
|
||||||
/* centroid is in box, so descend to all quadrants */
|
*/
|
||||||
setNodes(out, true, 0, 0);
|
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* identify quadrant(s) containing all corners of box */
|
|
||||||
Point p;
|
|
||||||
int i,
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
p = boxQuery->low;
|
if (DatumGetBool(DirectFunctionCall2(box_contain_pt,
|
||||||
r |= 1 << (getQuadrant(centroid, &p) - 1);
|
PointerGetDatum(boxQuery),
|
||||||
|
PointerGetDatum(centroid))))
|
||||||
p.y = boxQuery->high.y;
|
|
||||||
r |= 1 << (getQuadrant(centroid, &p) - 1);
|
|
||||||
|
|
||||||
p = boxQuery->high;
|
|
||||||
r |= 1 << (getQuadrant(centroid, &p) - 1);
|
|
||||||
|
|
||||||
p.x = boxQuery->low.x;
|
|
||||||
r |= 1 << (getQuadrant(centroid, &p) - 1);
|
|
||||||
|
|
||||||
/* we must descend into those quadrant(s) */
|
|
||||||
out->nNodes = 0;
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
{
|
||||||
if (r & (1 << i))
|
/* centroid is in box, so all quadrants are OK */
|
||||||
{
|
|
||||||
out->nodeNumbers[out->nNodes] = i;
|
|
||||||
out->nNodes++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
break;
|
{
|
||||||
default:
|
/* identify quadrant(s) containing all corners of box */
|
||||||
elog(ERROR, "unrecognized strategy number: %d", in->strategy);
|
Point p;
|
||||||
break;
|
int r = 0;
|
||||||
|
|
||||||
|
p = boxQuery->low;
|
||||||
|
r |= 1 << getQuadrant(centroid, &p);
|
||||||
|
p.y = boxQuery->high.y;
|
||||||
|
r |= 1 << getQuadrant(centroid, &p);
|
||||||
|
p = boxQuery->high;
|
||||||
|
r |= 1 << getQuadrant(centroid, &p);
|
||||||
|
p.x = boxQuery->low.x;
|
||||||
|
r |= 1 << getQuadrant(centroid, &p);
|
||||||
|
|
||||||
|
which &= r;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
in->scankeys[i].sk_strategy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (which == 0)
|
||||||
|
break; /* no need to consider remaining conditions */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We must descend into the quadrant(s) identified by which */
|
||||||
|
out->nodeNumbers = (int *) palloc(sizeof(int) * 4);
|
||||||
|
out->nNodes = 0;
|
||||||
|
for (i = 1; i <= 4; i++)
|
||||||
|
{
|
||||||
|
if (which & (1 << i))
|
||||||
|
out->nodeNumbers[out->nNodes++] = i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
@ -318,9 +304,9 @@ spg_quad_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
|
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
|
||||||
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
|
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
|
||||||
Point *query = DatumGetPointP(in->query);
|
|
||||||
Point *datum = DatumGetPointP(in->leafDatum);
|
Point *datum = DatumGetPointP(in->leafDatum);
|
||||||
bool res;
|
bool res;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* all tests are exact */
|
/* all tests are exact */
|
||||||
out->recheck = false;
|
out->recheck = false;
|
||||||
@ -328,35 +314,45 @@ spg_quad_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
/* leafDatum is what it is... */
|
/* leafDatum is what it is... */
|
||||||
out->leafValue = in->leafDatum;
|
out->leafValue = in->leafDatum;
|
||||||
|
|
||||||
switch (in->strategy)
|
/* Perform the required comparison(s) */
|
||||||
|
res = true;
|
||||||
|
for (i = 0; i < in->nkeys; i++)
|
||||||
{
|
{
|
||||||
case RTLeftStrategyNumber:
|
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
|
||||||
res = SPTEST(point_left, datum, query);
|
|
||||||
break;
|
|
||||||
case RTRightStrategyNumber:
|
|
||||||
res = SPTEST(point_right, datum, query);
|
|
||||||
break;
|
|
||||||
case RTSameStrategyNumber:
|
|
||||||
res = SPTEST(point_eq, datum, query);
|
|
||||||
break;
|
|
||||||
case RTBelowStrategyNumber:
|
|
||||||
res = SPTEST(point_below, datum, query);
|
|
||||||
break;
|
|
||||||
case RTAboveStrategyNumber:
|
|
||||||
res = SPTEST(point_above, datum, query);
|
|
||||||
break;
|
|
||||||
case RTContainedByStrategyNumber:
|
|
||||||
|
|
||||||
/*
|
switch (in->scankeys[i].sk_strategy)
|
||||||
* For this operator, the query is a box not a point. We cheat to
|
{
|
||||||
* the extent of assuming that DatumGetPointP won't do anything
|
case RTLeftStrategyNumber:
|
||||||
* that would be bad for a pointer-to-box.
|
res = SPTEST(point_left, datum, query);
|
||||||
*/
|
break;
|
||||||
res = SPTEST(box_contain_pt, query, datum);
|
case RTRightStrategyNumber:
|
||||||
break;
|
res = SPTEST(point_right, datum, query);
|
||||||
default:
|
break;
|
||||||
elog(ERROR, "unrecognized strategy number: %d", in->strategy);
|
case RTSameStrategyNumber:
|
||||||
res = false;
|
res = SPTEST(point_eq, datum, query);
|
||||||
|
break;
|
||||||
|
case RTBelowStrategyNumber:
|
||||||
|
res = SPTEST(point_below, datum, query);
|
||||||
|
break;
|
||||||
|
case RTAboveStrategyNumber:
|
||||||
|
res = SPTEST(point_above, datum, query);
|
||||||
|
break;
|
||||||
|
case RTContainedByStrategyNumber:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For this operator, the query is a box not a point. We
|
||||||
|
* cheat to the extent of assuming that DatumGetPointP won't
|
||||||
|
* do anything that would be bad for a pointer-to-box.
|
||||||
|
*/
|
||||||
|
res = SPTEST(box_contain_pt, query, datum);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
in->scankeys[i].sk_strategy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
* any previously active scan
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
resetSpGistScanOpaque(SpGistScanOpaque so)
|
resetSpGistScanOpaque(SpGistScanOpaque so)
|
||||||
{
|
{
|
||||||
ScanStackEntry *startEntry = palloc0(sizeof(ScanStackEntry));
|
ScanStackEntry *startEntry;
|
||||||
|
|
||||||
ItemPointerSet(&startEntry->ptr, SPGIST_HEAD_BLKNO, FirstOffsetNumber);
|
|
||||||
|
|
||||||
freeScanStack(so);
|
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)
|
if (so->want_itup)
|
||||||
{
|
{
|
||||||
@ -80,6 +87,82 @@ resetSpGistScanOpaque(SpGistScanOpaque so)
|
|||||||
so->iPtr = so->nPtrs = 0;
|
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
|
Datum
|
||||||
spgbeginscan(PG_FUNCTION_ARGS)
|
spgbeginscan(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -92,13 +175,16 @@ spgbeginscan(PG_FUNCTION_ARGS)
|
|||||||
scan = RelationGetIndexScan(rel, keysz, 0);
|
scan = RelationGetIndexScan(rel, keysz, 0);
|
||||||
|
|
||||||
so = (SpGistScanOpaque) palloc0(sizeof(SpGistScanOpaqueData));
|
so = (SpGistScanOpaque) palloc0(sizeof(SpGistScanOpaqueData));
|
||||||
|
if (keysz > 0)
|
||||||
|
so->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * keysz);
|
||||||
|
else
|
||||||
|
so->keyData = NULL;
|
||||||
initSpGistState(&so->state, scan->indexRelation);
|
initSpGistState(&so->state, scan->indexRelation);
|
||||||
so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
|
so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"SP-GiST search temporary context",
|
"SP-GiST search temporary context",
|
||||||
ALLOCSET_DEFAULT_MINSIZE,
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
ALLOCSET_DEFAULT_INITSIZE,
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
ALLOCSET_DEFAULT_MAXSIZE);
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
resetSpGistScanOpaque(so);
|
|
||||||
|
|
||||||
/* Set up indexTupDesc and xs_itupdesc in case it's an index-only scan */
|
/* Set up indexTupDesc and xs_itupdesc in case it's an index-only scan */
|
||||||
so->indexTupDesc = scan->xs_itupdesc = RelationGetDescr(rel);
|
so->indexTupDesc = scan->xs_itupdesc = RelationGetDescr(rel);
|
||||||
@ -115,12 +201,17 @@ spgrescan(PG_FUNCTION_ARGS)
|
|||||||
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
|
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
|
||||||
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
|
ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
|
||||||
|
|
||||||
|
/* copy scankeys into local storage */
|
||||||
if (scankey && scan->numberOfKeys > 0)
|
if (scankey && scan->numberOfKeys > 0)
|
||||||
{
|
{
|
||||||
memmove(scan->keyData, scankey,
|
memmove(scan->keyData, scankey,
|
||||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* preprocess scankeys, set up the representation in *so */
|
||||||
|
spgPrepareScanKeys(scan);
|
||||||
|
|
||||||
|
/* set up starting stack entries */
|
||||||
resetSpGistScanOpaque(so);
|
resetSpGistScanOpaque(so);
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
@ -162,53 +253,34 @@ spgLeafTest(Relation index, SpGistScanOpaque so, Datum leafDatum,
|
|||||||
int level, Datum reconstructedValue,
|
int level, Datum reconstructedValue,
|
||||||
Datum *leafValue, bool *recheck)
|
Datum *leafValue, bool *recheck)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result;
|
||||||
spgLeafConsistentIn in;
|
spgLeafConsistentIn in;
|
||||||
spgLeafConsistentOut out;
|
spgLeafConsistentOut out;
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
MemoryContext oldCtx;
|
MemoryContext oldCtx;
|
||||||
int i;
|
|
||||||
|
|
||||||
*leafValue = (Datum) 0;
|
/* use temp context for calling leaf_consistent */
|
||||||
*recheck = false;
|
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.reconstructedValue = reconstructedValue;
|
||||||
in.level = level;
|
in.level = level;
|
||||||
in.returnData = so->want_itup;
|
in.returnData = so->want_itup;
|
||||||
in.leafDatum = leafDatum;
|
in.leafDatum = leafDatum;
|
||||||
|
|
||||||
/* Apply each leaf consistency check, working in the temp context */
|
out.leafValue = (Datum) 0;
|
||||||
oldCtx = MemoryContextSwitchTo(so->tempCxt);
|
out.recheck = false;
|
||||||
|
|
||||||
procinfo = index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC);
|
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++)
|
*leafValue = out.leafValue;
|
||||||
{
|
*recheck = out.recheck;
|
||||||
ScanKey skey = &so->keyData[i];
|
|
||||||
|
|
||||||
/* Assume SPGiST-indexable operators are strict */
|
|
||||||
if (skey->sk_flags & SK_ISNULL)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -349,8 +421,13 @@ redirect:
|
|||||||
else /* page is inner */
|
else /* page is inner */
|
||||||
{
|
{
|
||||||
SpGistInnerTuple innerTuple;
|
SpGistInnerTuple innerTuple;
|
||||||
|
spgInnerConsistentIn in;
|
||||||
|
spgInnerConsistentOut out;
|
||||||
|
FmgrInfo *procinfo;
|
||||||
|
SpGistNodeTuple *nodes;
|
||||||
SpGistNodeTuple node;
|
SpGistNodeTuple node;
|
||||||
int i;
|
int i;
|
||||||
|
MemoryContext oldCtx;
|
||||||
|
|
||||||
innerTuple = (SpGistInnerTuple) PageGetItem(page,
|
innerTuple = (SpGistInnerTuple) PageGetItem(page,
|
||||||
PageGetItemId(page, offset));
|
PageGetItemId(page, offset));
|
||||||
@ -368,144 +445,68 @@ redirect:
|
|||||||
innerTuple->tupstate);
|
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)
|
||||||
{
|
{
|
||||||
/*
|
nodes[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
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
memset(&out, 0, sizeof(out));
|
||||||
|
|
||||||
|
procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
|
||||||
|
FunctionCall2Coll(procinfo,
|
||||||
|
index->rd_indcollation[0],
|
||||||
|
PointerGetDatum(&in),
|
||||||
|
PointerGetDatum(&out));
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
|
||||||
|
/* 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");
|
||||||
|
|
||||||
|
for (i = 0; i < out.nNodes; i++)
|
||||||
{
|
{
|
||||||
spgInnerConsistentIn in;
|
int nodeN = out.nodeNumbers[i];
|
||||||
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 */
|
Assert(nodeN >= 0 && nodeN < in.nNodes);
|
||||||
oldCtx = MemoryContextSwitchTo(so->tempCxt);
|
if (ItemPointerIsValid(&nodes[nodeN]->t_tid))
|
||||||
|
|
||||||
/* 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;
|
ScanStackEntry *newEntry;
|
||||||
}
|
|
||||||
|
|
||||||
andMap = (int *) palloc0(sizeof(int) * in.nNodes);
|
/* Create new work item for this node */
|
||||||
levelAdds = (int *) palloc0(sizeof(int) * in.nNodes);
|
newEntry = palloc(sizeof(ScanStackEntry));
|
||||||
reconstructedValues = (Datum *) palloc0(sizeof(Datum) * in.nNodes);
|
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;
|
||||||
|
|
||||||
procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
|
so->scanStack = lcons(newEntry, so->scanStack);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* quit as soon as all nodes have failed some qual */
|
|
||||||
if (nMatches == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldCtx);
|
|
||||||
|
|
||||||
if (nMatches > 0)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,10 +537,7 @@ spggetbitmap(PG_FUNCTION_ARGS)
|
|||||||
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
|
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
|
||||||
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
|
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
|
||||||
|
|
||||||
/* Copy scankey to *so so we don't need to pass it around separately */
|
/* Copy want_itup 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 */
|
|
||||||
so->want_itup = false;
|
so->want_itup = false;
|
||||||
|
|
||||||
so->tbm = tbm;
|
so->tbm = tbm;
|
||||||
@ -583,10 +581,7 @@ spggettuple(PG_FUNCTION_ARGS)
|
|||||||
if (dir != ForwardScanDirection)
|
if (dir != ForwardScanDirection)
|
||||||
elog(ERROR, "SP-GiST only supports forward scan direction");
|
elog(ERROR, "SP-GiST only supports forward scan direction");
|
||||||
|
|
||||||
/* Copy scankey to *so so we don't need to pass it around separately */
|
/* Copy want_itup 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 */
|
|
||||||
so->want_itup = scan->xs_want_itup;
|
so->want_itup = scan->xs_want_itup;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
|
@ -362,25 +362,12 @@ spg_text_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
|
||||||
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
|
||||||
StrategyNumber strategy = in->strategy;
|
bool collate_is_c = lc_collate_is_c(PG_GET_COLLATION());
|
||||||
text *inText;
|
|
||||||
int inSize;
|
|
||||||
int i;
|
|
||||||
text *reconstrText = NULL;
|
text *reconstrText = NULL;
|
||||||
int maxReconstrLen = 0;
|
int maxReconstrLen = 0;
|
||||||
text *prefixText = NULL;
|
text *prefixText = NULL;
|
||||||
int prefixSize = 0;
|
int prefixSize = 0;
|
||||||
|
int i;
|
||||||
/*
|
|
||||||
* If it's a collation-aware operator, but the collation is C, we can
|
|
||||||
* treat it as non-collation-aware.
|
|
||||||
*/
|
|
||||||
if (strategy > 10 &&
|
|
||||||
lc_collate_is_c(PG_GET_COLLATION()))
|
|
||||||
strategy -= 10;
|
|
||||||
|
|
||||||
inText = DatumGetTextPP(in->query);
|
|
||||||
inSize = VARSIZE_ANY_EXHDR(inText);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reconstruct values represented at this tuple, including parent data,
|
* Reconstruct values represented at this tuple, including parent data,
|
||||||
@ -431,8 +418,8 @@ spg_text_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
uint8 nodeChar = DatumGetUInt8(in->nodeLabels[i]);
|
uint8 nodeChar = DatumGetUInt8(in->nodeLabels[i]);
|
||||||
int thisLen;
|
int thisLen;
|
||||||
int r;
|
bool res = true;
|
||||||
bool res = false;
|
int j;
|
||||||
|
|
||||||
/* If nodeChar is zero, don't include it in data */
|
/* If nodeChar is zero, don't include it in data */
|
||||||
if (nodeChar == '\0')
|
if (nodeChar == '\0')
|
||||||
@ -443,38 +430,57 @@ spg_text_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
thisLen = maxReconstrLen;
|
thisLen = maxReconstrLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = memcmp(VARDATA(reconstrText), VARDATA_ANY(inText),
|
for (j = 0; j < in->nkeys; j++)
|
||||||
Min(inSize, thisLen));
|
|
||||||
|
|
||||||
switch (strategy)
|
|
||||||
{
|
{
|
||||||
case BTLessStrategyNumber:
|
StrategyNumber strategy = in->scankeys[j].sk_strategy;
|
||||||
case BTLessEqualStrategyNumber:
|
text *inText;
|
||||||
if (r <= 0)
|
int inSize;
|
||||||
res = true;
|
int r;
|
||||||
break;
|
|
||||||
case BTEqualStrategyNumber:
|
/*
|
||||||
if (r == 0 && inSize >= thisLen)
|
* If it's a collation-aware operator, but the collation is C, we
|
||||||
res = true;
|
* can treat it as non-collation-aware. With non-C collation we
|
||||||
break;
|
* need to traverse whole tree :-( so there's no point in making
|
||||||
case BTGreaterEqualStrategyNumber:
|
* any check here.
|
||||||
case BTGreaterStrategyNumber:
|
*/
|
||||||
if (r >= 0)
|
if (strategy > 10)
|
||||||
res = true;
|
{
|
||||||
break;
|
if (collate_is_c)
|
||||||
case BTLessStrategyNumber + 10:
|
strategy -= 10;
|
||||||
case BTLessEqualStrategyNumber + 10:
|
else
|
||||||
case BTGreaterEqualStrategyNumber + 10:
|
continue;
|
||||||
case BTGreaterStrategyNumber + 10:
|
}
|
||||||
/*
|
|
||||||
* with non-C collation we need to traverse whole tree :-(
|
inText = DatumGetTextPP(in->scankeys[j].sk_argument);
|
||||||
*/
|
inSize = VARSIZE_ANY_EXHDR(inText);
|
||||||
res = true;
|
|
||||||
break;
|
r = memcmp(VARDATA(reconstrText), VARDATA_ANY(inText),
|
||||||
default:
|
Min(inSize, thisLen));
|
||||||
elog(ERROR, "unrecognized strategy number: %d",
|
|
||||||
in->strategy);
|
switch (strategy)
|
||||||
break;
|
{
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
if (r > 0)
|
||||||
|
res = false;
|
||||||
|
break;
|
||||||
|
case BTEqualStrategyNumber:
|
||||||
|
if (r != 0 || inSize < thisLen)
|
||||||
|
res = false;
|
||||||
|
break;
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
if (r < 0)
|
||||||
|
res = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
in->scankeys[j].sk_strategy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
break; /* no need to consider remaining conditions */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
@ -496,16 +502,13 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
|
spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
|
||||||
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
|
spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
|
||||||
StrategyNumber strategy = in->strategy;
|
|
||||||
text *query = DatumGetTextPP(in->query);
|
|
||||||
int level = in->level;
|
int level = in->level;
|
||||||
text *leafValue,
|
text *leafValue,
|
||||||
*reconstrValue = NULL;
|
*reconstrValue = NULL;
|
||||||
char *fullValue;
|
char *fullValue;
|
||||||
int fullLen;
|
int fullLen;
|
||||||
int queryLen;
|
|
||||||
int r;
|
|
||||||
bool res;
|
bool res;
|
||||||
|
int j;
|
||||||
|
|
||||||
/* all tests are exact */
|
/* all tests are exact */
|
||||||
out->recheck = false;
|
out->recheck = false;
|
||||||
@ -518,18 +521,8 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
Assert(level == 0 ? reconstrValue == NULL :
|
Assert(level == 0 ? reconstrValue == NULL :
|
||||||
VARSIZE_ANY_EXHDR(reconstrValue) == level);
|
VARSIZE_ANY_EXHDR(reconstrValue) == level);
|
||||||
|
|
||||||
|
/* Reconstruct the full string represented by this leaf tuple */
|
||||||
fullLen = level + VARSIZE_ANY_EXHDR(leafValue);
|
fullLen = level + VARSIZE_ANY_EXHDR(leafValue);
|
||||||
|
|
||||||
queryLen = VARSIZE_ANY_EXHDR(query);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For an equality check, we needn't reconstruct fullValue if not same
|
|
||||||
* length; it can't match
|
|
||||||
*/
|
|
||||||
if (strategy == BTEqualStrategyNumber && queryLen != fullLen)
|
|
||||||
PG_RETURN_BOOL(false);
|
|
||||||
|
|
||||||
/* Else, reconstruct the full string represented by this leaf tuple */
|
|
||||||
if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
|
if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
|
||||||
{
|
{
|
||||||
fullValue = VARDATA(reconstrValue);
|
fullValue = VARDATA(reconstrValue);
|
||||||
@ -549,54 +542,67 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
out->leafValue = PointerGetDatum(fullText);
|
out->leafValue = PointerGetDatum(fullText);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run the appropriate type of comparison */
|
/* Perform the required comparison(s) */
|
||||||
if (strategy > 10)
|
res = true;
|
||||||
|
for (j = 0; j < in->nkeys; j++)
|
||||||
{
|
{
|
||||||
/* Collation-aware comparison */
|
StrategyNumber strategy = in->scankeys[j].sk_strategy;
|
||||||
strategy -= 10;
|
text *query = DatumGetTextPP(in->scankeys[j].sk_argument);
|
||||||
|
int queryLen = VARSIZE_ANY_EXHDR(query);
|
||||||
|
int r;
|
||||||
|
|
||||||
/* If asserts are enabled, verify encoding of reconstructed string */
|
if (strategy > 10)
|
||||||
Assert(pg_verifymbstr(fullValue, fullLen, false));
|
{
|
||||||
|
/* Collation-aware comparison */
|
||||||
|
strategy -= 10;
|
||||||
|
|
||||||
r = varstr_cmp(fullValue, Min(queryLen, fullLen),
|
/* If asserts enabled, verify encoding of reconstructed string */
|
||||||
VARDATA_ANY(query), Min(queryLen, fullLen),
|
Assert(pg_verifymbstr(fullValue, fullLen, false));
|
||||||
PG_GET_COLLATION());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Non-collation-aware comparison */
|
|
||||||
r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r == 0)
|
r = varstr_cmp(fullValue, Min(queryLen, fullLen),
|
||||||
{
|
VARDATA_ANY(query), Min(queryLen, fullLen),
|
||||||
if (queryLen > fullLen)
|
PG_GET_COLLATION());
|
||||||
r = -1;
|
}
|
||||||
else if (queryLen < fullLen)
|
else
|
||||||
r = 1;
|
{
|
||||||
}
|
/* Non-collation-aware comparison */
|
||||||
|
r = memcmp(fullValue, VARDATA_ANY(query), Min(queryLen, fullLen));
|
||||||
|
}
|
||||||
|
|
||||||
switch (strategy)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
case BTLessStrategyNumber:
|
if (queryLen > fullLen)
|
||||||
res = (r < 0);
|
r = -1;
|
||||||
break;
|
else if (queryLen < fullLen)
|
||||||
case BTLessEqualStrategyNumber:
|
r = 1;
|
||||||
res = (r <= 0);
|
}
|
||||||
break;
|
|
||||||
case BTEqualStrategyNumber:
|
switch (strategy)
|
||||||
res = (r == 0);
|
{
|
||||||
break;
|
case BTLessStrategyNumber:
|
||||||
case BTGreaterEqualStrategyNumber:
|
res = (r < 0);
|
||||||
res = (r >= 0);
|
break;
|
||||||
break;
|
case BTLessEqualStrategyNumber:
|
||||||
case BTGreaterStrategyNumber:
|
res = (r <= 0);
|
||||||
res = (r > 0);
|
break;
|
||||||
break;
|
case BTEqualStrategyNumber:
|
||||||
default:
|
res = (r == 0);
|
||||||
elog(ERROR, "unrecognized strategy number: %d", in->strategy);
|
break;
|
||||||
res = false;
|
case BTGreaterEqualStrategyNumber:
|
||||||
break;
|
res = (r >= 0);
|
||||||
|
break;
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
res = (r > 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
in->scankeys[j].sk_strategy);
|
||||||
|
res = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
break; /* no need to consider remaining conditions */
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_BOOL(res);
|
PG_RETURN_BOOL(res);
|
||||||
|
@ -128,8 +128,8 @@ typedef struct spgPickSplitOut
|
|||||||
*/
|
*/
|
||||||
typedef struct spgInnerConsistentIn
|
typedef struct spgInnerConsistentIn
|
||||||
{
|
{
|
||||||
StrategyNumber strategy; /* operator strategy number */
|
ScanKey scankeys; /* array of operators and comparison values */
|
||||||
Datum query; /* operator's RHS value */
|
int nkeys; /* length of array */
|
||||||
|
|
||||||
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) */
|
||||||
@ -156,8 +156,8 @@ typedef struct spgInnerConsistentOut
|
|||||||
*/
|
*/
|
||||||
typedef struct spgLeafConsistentIn
|
typedef struct spgLeafConsistentIn
|
||||||
{
|
{
|
||||||
StrategyNumber strategy; /* operator strategy number */
|
ScanKey scankeys; /* array of operators and comparison values */
|
||||||
Datum query; /* operator's RHS value */
|
int nkeys; /* length of array */
|
||||||
|
|
||||||
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) */
|
||||||
|
@ -126,7 +126,11 @@ typedef struct SpGistScanOpaqueData
|
|||||||
SpGistState state; /* see above */
|
SpGistState state; /* see above */
|
||||||
MemoryContext tempCxt; /* short-lived memory context */
|
MemoryContext tempCxt; /* short-lived memory context */
|
||||||
|
|
||||||
/* Index quals for scan (copied from IndexScanDesc for convenience) */
|
/* Control flags showing whether to search nulls and/or non-nulls */
|
||||||
|
bool searchNulls; /* scan matches (all) null entries */
|
||||||
|
bool searchNonNulls; /* scan matches (some) non-null entries */
|
||||||
|
|
||||||
|
/* Index quals to be passed to opclass (null-related quals removed) */
|
||||||
int numberOfKeys; /* number of index qualifier conditions */
|
int numberOfKeys; /* number of index qualifier conditions */
|
||||||
ScanKey keyData; /* array of index qualifier descriptors */
|
ScanKey keyData; /* array of index qualifier descriptors */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user