|
box_ops>
- box
+ box>
<<>
&<>
Inner tuples are more complex, since they are branching points in the
search tree. Each inner tuple contains a set of one or more
nodes>, which represent groups of similar leaf values.
- A node contains a downlink that leads to either another, lower-level inner
- tuple, or a short list of leaf tuples that all lie on the same index page.
- Each node has a label> that describes it; for example,
+ A node contains a downlink that leads either to another, lower-level inner
+ tuple, or to a short list of leaf tuples that all lie on the same index page.
+ Each node normally has a label> that describes it; for example,
in a radix tree the node label could be the next character of the string
- value. Optionally, an inner tuple can have a prefix> value
+ value. (Alternatively, an operator class can omit the node labels, if it
+ works with a fixed set of nodes for all inner tuples;
+ see .)
+ Optionally, an inner tuple can have a prefix> value
that describes all its members. In a radix tree this could be the common
prefix of the represented strings. The prefix value is not necessarily
really a prefix, but can be any data needed by the operator class;
tuple, so the
SP-GiST core provides the possibility for
operator classes to manage level counting while descending the tree.
There is also support for incrementally reconstructing the represented
- value when that is needed.
+ value when that is needed, and for passing down additional data (called
+ traverse values>) during a tree descent.
} addNode;
struct /* results for spgSplitTuple */
{
- /* Info to form new inner tuple with one node */
+ /* Info to form new upper-level inner tuple with one child tuple */
bool prefixHasPrefix; /* tuple should have a prefix? */
Datum prefixPrefixDatum; /* if so, its value */
- Datum nodeLabel; /* node's label */
+ int prefixNNodes; /* number of nodes */
+ Datum *prefixNodeLabels; /* their labels (or NULL for
+ * no labels) */
+ int childNodeN; /* which node gets child tuple */
/* Info to form new lower-level inner tuple with all old nodes */
bool postfixHasPrefix; /* tuple should have a prefix? */
set resultType> to spgSplitTuple>.
This action moves all the existing nodes into a new lower-level
inner tuple, and replaces the existing inner tuple with a tuple
- having a single node that links to the new lower-level inner tuple.
+ having a single downlink pointing to the new lower-level inner tuple.
Set prefixHasPrefix> to indicate whether the new
upper tuple should have a prefix, and if so set
prefixPrefixDatum> to the prefix value. This new
prefix value must be sufficiently less restrictive than the original
- to accept the new value to be indexed, and it should be no longer
- than the original prefix.
- Set nodeLabel> to the label to be used for the
- node that will point to the new lower-level inner tuple.
+ to accept the new value to be indexed.
+ Set prefixNNodes> to the number of nodes needed in the
+ new tuple, and set prefixNodeLabels> to a palloc'd array
+ holding their labels, or to NULL if node labels are not required.
+ Note that the total size of the new upper tuple must be no more
+ than the total size of the tuple it is replacing; this constrains
+ the lengths of the new prefix and new labels.
+ Set childNodeN> to the index (from zero) of the node
+ that will downlink to the new lower-level inner tuple.
Set postfixHasPrefix> to indicate whether the new
lower-level inner tuple should have a prefix, and if so set
postfixPrefixDatum> to the prefix value. The
- combination of these two prefixes and the additional label must
- have the same meaning as the original prefix, because there is
- no opportunity to alter the node labels that are moved to the new
- lower-level tuple, nor to change any child index entries.
+ combination of these two prefixes and the downlink node's label
+ (if any) must have the same meaning as the original prefix, because
+ there is no opportunity to alter the node labels that are moved to
+ the new lower-level tuple, nor to change any child index entries.
After the node has been split, the choose
function will be called again with the replacement inner tuple.
- That call will usually result in an spgAddNode> result,
- since presumably the node label added in the split step will not
- match the new value; so after that, there will be a third call
- that finally returns spgMatchNode> and allows the
- insertion to descend to the leaf level.
+ That call may return an spgAddNode> result, if no suitable
+ node was created by the spgSplitTuple> action. Eventually
+ choose must return spgMatchNode> to
+ allow the insertion to descend to the next level.
prefixDatum> to the prefix value.
Set nNodes> to indicate the number of nodes that
the new inner tuple will contain, and
- set nodeLabels> to an array of their label values.
- (If the nodes do not require labels, set nodeLabels>
- to NULL; see for details.)
+ set nodeLabels> to an array of their label values,
+ or to NULL if node labels are not required.
Set mapTuplesToNodes> to an array that gives the index
(from zero) of the node that each leaf tuple should be assigned to.
Set leafTupleDatums> to an array of the values to
Datum reconstructedValue; /* value reconstructed at parent */
void *traversalValue; /* opclass-specific traverse value */
- MemoryContext traversalMemoryContext;
+ MemoryContext traversalMemoryContext; /* put new traverse values here */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
int *levelAdds; /* increment level by this much for each */
Datum *reconstructedValues; /* associated reconstructed values */
void **traversalValues; /* opclass-specific traverse values */
-
} spgInnerConsistentOut;
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
parent level.
+ traversalValue> is a pointer to any traverse data
+ passed down from the previous call of inner_consistent>
+ on the parent index tuple, or NULL at the root level.
+ traversalMemoryContext> is the memory context in which
+ to store output traverse values (see below).
level> is the current inner tuple's level, starting at
zero for the root level.
returnData> is true> if reconstructed data is
inner tuple, and
nodeLabels> is an array of their label values, or
NULL if the nodes do not have labels.
- traversalValue> is a pointer to data that
- inner_consistent> gets when called on child nodes from an
- outer call of inner_consistent> on parent nodes.
reconstructedValues> to an array of the values
reconstructed for each child node to be visited; otherwise, leave
reconstructedValues> as NULL.
+ If it is desired to pass down additional out-of-band information
+ (traverse values>) to lower levels of the tree search,
+ set traversalValues> to an array of the appropriate
+ traverse values, one for each child node to be visited; otherwise,
+ leave traversalValues> as NULL.
Note that the inner_consistent> function is
responsible for palloc'ing the
- nodeNumbers>, levelAdds> and
- reconstructedValues> arrays.
- Sometimes accumulating some information is needed, while
- descending from parent to child node was happened. In this case
- traversalValues> array keeps pointers to
- specific data you need to accumulate for every child node.
- Memory for traversalValues> should be allocated in
- the default context, but each element of it should be allocated in
- traversalMemoryContext>.
+ nodeNumbers>, levelAdds>,
+ reconstructedValues>, and
+ traversalValues> arrays in the current memory context.
+ However, any output traverse values pointed to by
+ the traversalValues> array should be allocated
+ in traversalMemoryContext>.
ScanKey scankeys; /* array of operators and comparison values */
int nkeys; /* length of array */
- void *traversalValue; /* opclass-specific traverse value */
Datum reconstructedValue; /* value reconstructed at parent */
+ void *traversalValue; /* opclass-specific traverse value */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
parent level.
+ traversalValue> is a pointer to any traverse data
+ passed down from the previous call of inner_consistent>
+ on the parent index tuple, or NULL at the root level.
level> is the current leaf tuple's level, starting at
zero for the root level.
returnData> is true> if reconstructed data is
point. In such a case the code typically works with the nodes by
number, and there is no need for explicit node labels. To suppress
node labels (and thereby save some space), the picksplit>
- function can return NULL for the nodeLabels> array.
+ function can return NULL for the nodeLabels> array,
+ and likewise the choose> function can return NULL for
+ the prefixNodeLabels> array during
+ a spgSplitTuple> action.
This will in turn result in nodeLabels> being NULL during
subsequent calls to choose> and inner_consistent>.
In principle, node labels could be used for some inner tuples and omitted
When working with an inner tuple having unlabeled nodes, it is an error
for choose> to return spgAddNode>, since the set
- of nodes is supposed to be fixed in such cases. Also, there is no
- provision for generating an unlabeled node in spgSplitTuple>
- actions, since it is expected that an spgAddNode> action will
- be needed as well.
+ of nodes is supposed to be fixed in such cases.
The
PostgreSQL source distribution includes
- several examples of index operator classes for
-
SP-GiST. The core system currently provides radix
- trees over text columns and two types of trees over points: quad-tree and
- k-d tree. Look into src/backend/access/spgist/> to see the
- code.
+ several examples of index operator classes for
SP-GiST,
+ as described in . Look
+ into src/backend/access/spgist/>
+ and src/backend/utils/adt/> to see the code.
/* Should not be applied to nulls */
Assert(!SpGistPageStoresNulls(current->page));
+ /* Check opclass gave us sane values */
+ if (out->result.splitTuple.prefixNNodes <= 0 ||
+ out->result.splitTuple.prefixNNodes > SGITMAXNNODES)
+ elog(ERROR, "invalid number of prefix nodes: %d",
+ out->result.splitTuple.prefixNNodes);
+ if (out->result.splitTuple.childNodeN < 0 ||
+ out->result.splitTuple.childNodeN >=
+ out->result.splitTuple.prefixNNodes)
+ elog(ERROR, "invalid child node number: %d",
+ out->result.splitTuple.childNodeN);
+
/*
- * Construct new prefix tuple, containing a single node with the specified
- * label. (We'll update the node's downlink to point to the new postfix
- * tuple, below.)
+ * Construct new prefix tuple with requested number of nodes. We'll fill
+ * in the childNodeN'th node's downlink below.
*/
- node = spgFormNodeTuple(state, out->result.splitTuple.nodeLabel, false);
+ nodes = (SpGistNodeTuple *) palloc(sizeof(SpGistNodeTuple) *
+ out->result.splitTuple.prefixNNodes);
+
+ for (i = 0; i < out->result.splitTuple.prefixNNodes; i++)
+ {
+ Datum label = (Datum) 0;
+ bool labelisnull;
+
+ labelisnull = (out->result.splitTuple.prefixNodeLabels == NULL);
+ if (!labelisnull)
+ label = out->result.splitTuple.prefixNodeLabels[i];
+ nodes[i] = spgFormNodeTuple(state, label, labelisnull);
+ }
prefixTuple = spgFormInnerTuple(state,
out->result.splitTuple.prefixHasPrefix,
out->result.splitTuple.prefixPrefixDatum,
- 1, &node);
+ out->result.splitTuple.prefixNNodes,
+ nodes);
/* it must fit in the space that innerTuple now occupies */
if (prefixTuple->size > innerTuple->size)
* the postfix tuple first.) We have to update the local copy of the
* prefixTuple too, because that's what will be written to WAL.
*/
- spgUpdateNodeLink(prefixTuple, 0, postfixBlkno, postfixOffset);
+ spgUpdateNodeLink(prefixTuple, out->result.splitTuple.childNodeN,
+ postfixBlkno, postfixOffset);
prefixTuple = (SpGistInnerTuple) PageGetItem(current->page,
PageGetItemId(current->page, current->offnum));
- spgUpdateNodeLink(prefixTuple, 0, postfixBlkno, postfixOffset);
+ spgUpdateNodeLink(prefixTuple, out->result.splitTuple.childNodeN,
+ postfixBlkno, postfixOffset);
MarkBufferDirty(current->buffer);