1
0
Fork 0
arangodb/arangod/SkipLists/skiplistIndex.cpp

1042 lines
38 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief skiplist index
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Oreste Costa-Panaia
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "skiplistIndex.h"
#include "Basics/Utf8Helper.h"
#include "ShapedJson/json-shaper.h"
#include "ShapedJson/shaped-json.h"
#include "VocBase/document-collection.h"
#include "VocBase/voc-shaper.h"
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Common private methods
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// .............................................................................
// recall for all of the following comparison functions:
//
// left < right return -1
// left > right return 1
// left == right return 0
//
// furthermore:
//
// the following order is currently defined for placing an order on documents
// undef < null < boolean < number < strings < lists < hash arrays
// note: undefined will be treated as NULL pointer not NULL JSON OBJECT
// within each type class we have the following order
// boolean: false < true
// number: natural order
// strings: lexicographical
// lists: lexicographically and within each slot according to these rules.
// ...........................................................................
////////////////////////////////////////////////////////////////////////////////
/// @brief compares a key with an element, version with proper types
////////////////////////////////////////////////////////////////////////////////
static int CompareKeyElement (TRI_shaped_json_t const* left,
TRI_skiplist_index_element_t const* right,
size_t rightPosition,
TRI_shaper_t* shaper) {
TRI_ASSERT(nullptr != left);
TRI_ASSERT(nullptr != right);
auto rightSubobjects = SkiplistIndex_Subobjects(right);
return TRI_CompareShapeTypes(nullptr,
nullptr,
left,
shaper,
right->_document->getShapedJsonPtr(),
&rightSubobjects[rightPosition],
nullptr,
shaper);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief compares elements, version with proper types
////////////////////////////////////////////////////////////////////////////////
static int CompareElementElement (TRI_skiplist_index_element_t const* left,
size_t leftPosition,
TRI_skiplist_index_element_t const* right,
size_t rightPosition,
TRI_shaper_t* shaper) {
TRI_ASSERT(nullptr != left);
TRI_ASSERT(nullptr != right);
auto leftSubobjects = SkiplistIndex_Subobjects(left);
auto rightSubobjects = SkiplistIndex_Subobjects(right);
return TRI_CompareShapeTypes(left->_document->getShapedJsonPtr(),
&leftSubobjects[leftPosition],
nullptr,
shaper,
right->_document->getShapedJsonPtr(),
&rightSubobjects[rightPosition],
nullptr,
shaper);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief compares two elements in a skip list, this is the generic callback
////////////////////////////////////////////////////////////////////////////////
static int CmpElmElm (void* sli,
void* left,
void* right,
triagens::basics::SkipListCmpType cmptype) {
auto leftElement = static_cast<TRI_skiplist_index_element_t const*>(left);
auto rightElement = static_cast<TRI_skiplist_index_element_t const*>(right);
TRI_shaper_t* shaper;
TRI_ASSERT(nullptr != left);
TRI_ASSERT(nullptr != right);
// ..........................................................................
// The document could be the same -- so no further comparison is required.
// ..........................................................................
if (leftElement == rightElement ||
leftElement->_document == rightElement->_document) {
return 0;
}
SkiplistIndex* skiplistindex = static_cast<SkiplistIndex*>(sli);
shaper = skiplistindex->_collection->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME
for (size_t j = 0; j < skiplistindex->_numFields; j++) {
int compareResult = CompareElementElement(leftElement,
j,
rightElement,
j,
shaper);
if (compareResult != 0) {
return compareResult;
}
}
// ...........................................................................
// This is where the difference between the preorder and the proper total
// order comes into play. Here if the 'keys' are the same,
// but the doc ptr is different (which it is since we are here), then
// we return 0 if we use the preorder and look at the _key attribute
// otherwise.
// ...........................................................................
if (triagens::basics::SKIPLIST_CMP_PREORDER == cmptype) {
return 0;
}
// We break this tie in the key comparison by looking at the key:
int compareResult = strcmp(TRI_EXTRACT_MARKER_KEY(leftElement->_document), // ONLY IN INDEX, PROTECTED by RUNTIME
TRI_EXTRACT_MARKER_KEY(rightElement->_document)); // ONLY IN INDEX, PROTECTED by RUNTIME
if (compareResult < 0) {
return -1;
}
else if (compareResult > 0) {
return 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief compares a key with an element in a skip list, generic callback
////////////////////////////////////////////////////////////////////////////////
static int CmpKeyElm (void* sli,
void* left,
void* right) {
auto leftKey = static_cast<TRI_skiplist_index_key_t const*>(left);
auto rightElement = static_cast<TRI_skiplist_index_element_t const*>(right);
TRI_ASSERT(nullptr != left);
TRI_ASSERT(nullptr != right);
SkiplistIndex* skiplistindex = static_cast<SkiplistIndex*>(sli);
TRI_shaper_t* shaper = skiplistindex->_collection->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME
// Note that the key might contain fewer fields than there are indexed
// attributes, therefore we only run the following loop to
// leftKey->_numFields.
for (size_t j = 0; j < leftKey->_numFields; j++) {
int compareResult = CompareKeyElement(&leftKey->_fields[j], rightElement, j, shaper);
if (compareResult != 0) {
return compareResult;
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief frees an element in the skiplist
////////////////////////////////////////////////////////////////////////////////
static void FreeElm (void* e) {
auto element = static_cast<TRI_skiplist_index_element_t*>(e);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the current interval that the iterator points at
////////////////////////////////////////////////////////////////////////////////
static inline TRI_skiplist_iterator_interval_t* GetInterval (TRI_skiplist_iterator_t const* iterator) {
return static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AtVector(&iterator->_intervals, iterator->_currentInterval));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Attempts to determine if there is a previous document within an
/// interval or before it - without advancing the iterator.
////////////////////////////////////////////////////////////////////////////////
static bool SkiplistHasPrevIterationCallback (TRI_skiplist_iterator_t const* iterator) {
// Note that iterator->_cursor == nullptr if we are before the largest
// document (i.e. the first one in the iterator)!
if (iterator == nullptr) {
return false;
}
// ...........................................................................
// if we have more intervals than the one we are currently working
// on then of course we have a previous doc, because intervals are nonempty.
// ...........................................................................
if (iterator->_currentInterval > 0) {
return true;
}
void const* leftNode
= iterator->_index->skiplist->prevNode(iterator->_cursor);
// Note that leftNode can be nullptr here!
// ...........................................................................
// If the leftNode == left end point AND there are no more intervals
// then we have no next.
// ...........................................................................
if (leftNode == GetInterval(iterator)->_leftEndPoint) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Attempts to determine if there is a next document within an
/// interval - without advancing the iterator.
////////////////////////////////////////////////////////////////////////////////
static bool SkiplistHasNextIterationCallback (TRI_skiplist_iterator_t const* iterator) {
if (iterator == nullptr ||
iterator->_cursor == nullptr) {
return false;
}
// ...........................................................................
// if we have more intervals than the one we are currently working
// on then of course we have a next doc, since intervals are nonempty.
// ...........................................................................
if (TRI_LengthVector(&iterator->_intervals) - 1 > iterator->_currentInterval) {
return true;
}
void const* leftNode = iterator->_cursor->nextNode();
// Note that leftNode can be nullptr here!
// ...........................................................................
// If the left == right end point AND there are no more intervals then we have
// no next.
// ...........................................................................
if (leftNode == GetInterval(iterator)->_rightEndPoint) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Jumps backwards by jumpSize and returns the document
////////////////////////////////////////////////////////////////////////////////
static TRI_skiplist_index_element_t* SkiplistPrevIterationCallback (
TRI_skiplist_iterator_t* iterator) {
static const int64_t jumpSize = 1;
TRI_ASSERT(jumpSize > 0);
if (iterator == nullptr) {
// In this case the iterator does not even have intervals.
return nullptr;
}
TRI_skiplist_iterator_interval_t* interval = GetInterval(iterator);
if (interval == nullptr) {
return nullptr;
}
// ...........................................................................
// use the current cursor and move jumpSize backward
// ...........................................................................
triagens::basics::SkipListNode* result = nullptr;
for (int64_t j = 0; j < jumpSize; ++j) {
while (true) { // will be left by break
result = iterator->_index->skiplist->prevNode(iterator->_cursor);
if (result == interval->_leftEndPoint) {
if (iterator->_currentInterval == 0) {
iterator->_cursor = nullptr; // exhausted
return nullptr;
}
--iterator->_currentInterval;
interval = GetInterval(iterator);
TRI_ASSERT(interval != nullptr);
iterator->_cursor = interval->_rightEndPoint;
result = iterator->_index->skiplist->prevNode(iterator->_cursor);
}
iterator->_cursor = result;
break; // we found a prev one
}
}
TRI_ASSERT(result != nullptr);
return static_cast<TRI_skiplist_index_element_t*>(result->document()); //iterator->_cursor->document());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Jumps forwards by jumpSize and returns the document
////////////////////////////////////////////////////////////////////////////////
static TRI_skiplist_index_element_t* SkiplistNextIterationCallback (
TRI_skiplist_iterator_t* iterator) {
static const int64_t jumpSize = 1;
TRI_ASSERT(jumpSize > 0);
if (iterator == nullptr ||
iterator->_cursor == nullptr) {
// In this case the iterator is exhausted or does not even have intervals.
return nullptr;
}
TRI_skiplist_iterator_interval_t* interval = GetInterval(iterator);
if (interval == nullptr) {
return nullptr;
}
// ...........................................................................
// use the current cursor and move jumpSize forward
// ...........................................................................
for (int64_t j = 0; j < jumpSize; ++j) {
while (true) { // will be left by break
iterator->_cursor = iterator->_cursor->nextNode();
if (iterator->_cursor != interval->_rightEndPoint) {
// Note that _cursor can be nullptr here!
break; // we found a next one
}
if (iterator->_currentInterval == (TRI_LengthVector(&iterator->_intervals) - 1)) {
iterator->_cursor = nullptr; // exhausted
return nullptr;
}
++iterator->_currentInterval;
interval = GetInterval(iterator);
TRI_ASSERT(interval != nullptr);
iterator->_cursor = interval->_leftEndPoint;
}
}
return static_cast<TRI_skiplist_index_element_t*>(iterator->_cursor->document());
}
// -----------------------------------------------------------------------------
// --SECTION-- skiplistIndex common public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Free a skiplist iterator
////////////////////////////////////////////////////////////////////////////////
void TRI_FreeSkiplistIterator (TRI_skiplist_iterator_t* iterator) {
TRI_ASSERT(nullptr != iterator);
TRI_DestroyVector(&iterator->_intervals);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, iterator);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a skip list index , but does not free the pointer
////////////////////////////////////////////////////////////////////////////////
void SkiplistIndex_destroy (SkiplistIndex* slIndex) {
if (slIndex == nullptr) {
return;
}
delete slIndex->skiplist;
slIndex->skiplist = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a skip list index and frees the pointer
////////////////////////////////////////////////////////////////////////////////
void SkiplistIndex_free (SkiplistIndex* slIndex) {
if (slIndex == nullptr) {
return;
}
SkiplistIndex_destroy(slIndex);
TRI_Free(TRI_CORE_MEM_ZONE, slIndex);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Skiplist indices
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Public Methods Skiplist Indices
//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a new skiplist index
////////////////////////////////////////////////////////////////////////////////
SkiplistIndex* SkiplistIndex_new (TRI_document_collection_t* document,
size_t numFields,
bool unique) {
SkiplistIndex* skiplistIndex = static_cast<SkiplistIndex*>(TRI_Allocate(TRI_CORE_MEM_ZONE, sizeof(SkiplistIndex), true));
if (skiplistIndex == nullptr) {
return nullptr;
}
skiplistIndex->_collection = document;
skiplistIndex->_numFields = numFields;
skiplistIndex->unique = unique;
try {
skiplistIndex->skiplist = new triagens::basics::SkipList(
CmpElmElm, CmpKeyElm, skiplistIndex,
FreeElm, unique);
}
catch (...) {
TRI_Free(TRI_CORE_MEM_ZONE, skiplistIndex);
return nullptr;
}
return skiplistIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Locates one or more ranges within the skiplist and returns iterator
////////////////////////////////////////////////////////////////////////////////
// .............................................................................
// Tests whether the LeftEndPoint is < than RightEndPoint (-1)
// Tests whether the LeftEndPoint is == to RightEndPoint (0) [empty]
// Tests whether the LeftEndPoint is > than RightEndPoint (1) [undefined]
// .............................................................................
static bool skiplistIndex_findHelperIntervalValid(
SkiplistIndex* skiplistIndex,
TRI_skiplist_iterator_interval_t const* interval) {
int compareResult;
triagens::basics::SkipListNode* lNode;
triagens::basics::SkipListNode* rNode;
lNode = interval->_leftEndPoint;
if (lNode == nullptr) {
return false;
}
// Note that the right end point can be nullptr to indicate the end of
// the index.
rNode = interval->_rightEndPoint;
if (lNode == rNode) {
return false;
}
if (lNode->nextNode() == rNode) {
// Interval empty, nothing to do with it.
return false;
}
if (nullptr != rNode && rNode->nextNode() == lNode) {
// Interval empty, nothing to do with it.
return false;
}
if (skiplistIndex->skiplist->getNrUsed() == 0) {
return false;
}
if ( lNode == skiplistIndex->skiplist->startNode() ||
nullptr == rNode ) {
// The index is not empty, the nodes are not neighbours, one of them
// is at the boundary, so the interval is valid and not empty.
return true;
}
compareResult = CmpElmElm( skiplistIndex,
lNode->document(), rNode->document(),
triagens::basics::SKIPLIST_CMP_TOTORDER );
return (compareResult == -1);
// Since we know that the nodes are not neighbours, we can guarantee
// at least one document in the interval.
}
static bool skiplistIndex_findHelperIntervalIntersectionValid (
SkiplistIndex* skiplistIndex,
TRI_skiplist_iterator_interval_t* lInterval,
TRI_skiplist_iterator_interval_t* rInterval,
TRI_skiplist_iterator_interval_t* interval) {
triagens::basics::SkipListNode* lNode;
triagens::basics::SkipListNode* rNode;
lNode = lInterval->_leftEndPoint;
rNode = rInterval->_leftEndPoint;
if (nullptr == lNode || nullptr == rNode) {
// At least one left boundary is the end, intersection is empty.
return false;
}
int compareResult;
// Now find the larger of the two start nodes:
if (lNode == skiplistIndex->skiplist->startNode()) {
// We take rNode, even if it is the start node as well.
compareResult = -1;
}
else if (rNode == skiplistIndex->skiplist->startNode()) {
// We take lNode
compareResult = 1;
}
else {
compareResult = CmpElmElm(skiplistIndex, lNode->document(),
rNode->document(),
triagens::basics::SKIPLIST_CMP_TOTORDER);
}
if (compareResult < 1) {
interval->_leftEndPoint = rNode;
}
else {
interval->_leftEndPoint = lNode;
}
lNode = lInterval->_rightEndPoint;
rNode = rInterval->_rightEndPoint;
// Now find the smaller of the two end nodes:
if (nullptr == lNode) {
// We take rNode, even is this also the end node.
compareResult = 1;
}
else if (nullptr == rNode) {
// We take lNode.
compareResult = -1;
}
else {
compareResult = CmpElmElm(skiplistIndex, lNode->document(),
rNode->document(),
triagens::basics::SKIPLIST_CMP_TOTORDER);
}
if (compareResult < 1) {
interval->_rightEndPoint = lNode;
}
else {
interval->_rightEndPoint = rNode;
}
return skiplistIndex_findHelperIntervalValid(skiplistIndex, interval);
}
static void SkiplistIndex_findHelper (SkiplistIndex* skiplistIndex,
TRI_vector_t const* shapeList,
TRI_index_operator_t const* indexOperator,
TRI_vector_t* resultIntervalList) {
TRI_skiplist_index_key_t values;
TRI_vector_t leftResult;
TRI_vector_t rightResult;
TRI_skiplist_iterator_interval_t interval;
triagens::basics::SkipListNode* temp;
TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_interval_t));
TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_interval_t));
TRI_relation_index_operator_t* relationOperator = (TRI_relation_index_operator_t*) indexOperator;
TRI_logical_index_operator_t* logicalOperator = (TRI_logical_index_operator_t*) indexOperator;
switch (indexOperator->_type) {
case TRI_EQ_INDEX_OPERATOR:
case TRI_LE_INDEX_OPERATOR:
case TRI_LT_INDEX_OPERATOR:
case TRI_GE_INDEX_OPERATOR:
case TRI_GT_INDEX_OPERATOR:
values._fields = relationOperator->_fields;
values._numFields = relationOperator->_numFields;
break; // this is to silence a compiler warning
default: {
// must not access relationOperator->xxx if the operator is not a
// relational one otherwise we'll get invalid reads and the prog
// might crash
}
}
switch (indexOperator->_type) {
case TRI_AND_INDEX_OPERATOR: {
SkiplistIndex_findHelper(skiplistIndex, shapeList, logicalOperator->_left, &leftResult);
SkiplistIndex_findHelper(skiplistIndex, shapeList, logicalOperator->_right, &rightResult);
size_t nl = TRI_LengthVector(&leftResult);
size_t nr = TRI_LengthVector(&rightResult);
for (size_t i = 0; i < nl; ++i) {
for (size_t j = 0; j < nr; ++j) {
auto tempLeftInterval = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AddressVector(&leftResult, i));
auto tempRightInterval = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AddressVector(&rightResult, j));
if (skiplistIndex_findHelperIntervalIntersectionValid(
skiplistIndex,
tempLeftInterval,
tempRightInterval,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
}
TRI_DestroyVector(&leftResult);
TRI_DestroyVector(&rightResult);
return;
}
case TRI_EQ_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
TRI_ASSERT(nullptr != temp);
interval._leftEndPoint = temp;
bool const allAttributesCoveredByCondition = (values._numFields == skiplistIndex->_numFields);
if (skiplistIndex->unique && allAttributesCoveredByCondition) {
// At most one hit:
temp = temp->nextNode();
if (nullptr != temp) {
if (0 == CmpKeyElm(skiplistIndex, &values, temp->document())) {
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
}
}
else {
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
return;
}
case TRI_LE_INDEX_OPERATOR: {
interval._leftEndPoint = skiplistIndex->skiplist->startNode();
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_LT_INDEX_OPERATOR: {
interval._leftEndPoint = skiplistIndex->skiplist->startNode();
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_GE_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
interval._leftEndPoint = temp;
interval._rightEndPoint = skiplistIndex->skiplist->endNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_GT_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._leftEndPoint = temp;
interval._rightEndPoint = skiplistIndex->skiplist->endNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
default: {
TRI_ASSERT(false);
}
} // end of switch statement
}
static void SkiplistIndex_findHelper (SkiplistIndex* skiplistIndex,
TRI_index_operator_t const* indexOperator,
TRI_vector_t* resultIntervalList) {
TRI_skiplist_index_key_t values;
TRI_vector_t leftResult;
TRI_vector_t rightResult;
TRI_skiplist_iterator_interval_t interval;
triagens::basics::SkipListNode* temp;
TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_interval_t));
TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_interval_t));
TRI_relation_index_operator_t* relationOperator = (TRI_relation_index_operator_t*) indexOperator;
TRI_logical_index_operator_t* logicalOperator = (TRI_logical_index_operator_t*) indexOperator;
switch (indexOperator->_type) {
case TRI_EQ_INDEX_OPERATOR:
case TRI_LE_INDEX_OPERATOR:
case TRI_LT_INDEX_OPERATOR:
case TRI_GE_INDEX_OPERATOR:
case TRI_GT_INDEX_OPERATOR:
values._fields = relationOperator->_fields;
values._numFields = relationOperator->_numFields;
break; // this is to silence a compiler warning
default: {
// must not access relationOperator->xxx if the operator is not a
// relational one otherwise we'll get invalid reads and the prog
// might crash
}
}
switch (indexOperator->_type) {
case TRI_AND_INDEX_OPERATOR: {
SkiplistIndex_findHelper(skiplistIndex, logicalOperator->_left, &leftResult);
SkiplistIndex_findHelper(skiplistIndex, logicalOperator->_right, &rightResult);
size_t nl = TRI_LengthVector(&leftResult);
size_t nr = TRI_LengthVector(&rightResult);
for (size_t i = 0; i < nl; ++i) {
for (size_t j = 0; j < nr; ++j) {
auto tempLeftInterval = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AddressVector(&leftResult, i));
auto tempRightInterval = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AddressVector(&rightResult, j));
if (skiplistIndex_findHelperIntervalIntersectionValid(
skiplistIndex,
tempLeftInterval,
tempRightInterval,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
}
TRI_DestroyVector(&leftResult);
TRI_DestroyVector(&rightResult);
return;
}
case TRI_EQ_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
TRI_ASSERT(nullptr != temp);
interval._leftEndPoint = temp;
bool const allAttributesCoveredByCondition = (values._numFields == skiplistIndex->_numFields);
if (skiplistIndex->unique && allAttributesCoveredByCondition) {
// At most one hit:
temp = temp->nextNode();
if (nullptr != temp) {
if (0 == CmpKeyElm(skiplistIndex, &values, temp->document())) {
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
}
}
else {
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex,
&interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
}
return;
}
case TRI_LE_INDEX_OPERATOR: {
interval._leftEndPoint = skiplistIndex->skiplist->startNode();
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_LT_INDEX_OPERATOR: {
interval._leftEndPoint = skiplistIndex->skiplist->startNode();
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
interval._rightEndPoint = temp->nextNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_GE_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->leftKeyLookup(&values);
interval._leftEndPoint = temp;
interval._rightEndPoint = skiplistIndex->skiplist->endNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
case TRI_GT_INDEX_OPERATOR: {
temp = skiplistIndex->skiplist->rightKeyLookup(&values);
interval._leftEndPoint = temp;
interval._rightEndPoint = skiplistIndex->skiplist->endNode();
if (skiplistIndex_findHelperIntervalValid(skiplistIndex, &interval)) {
TRI_PushBackVector(resultIntervalList, &interval);
}
return;
}
default: {
TRI_ASSERT(false);
}
} // end of switch statement
}
TRI_skiplist_iterator_t* SkiplistIndex_find (
SkiplistIndex* skiplistIndex,
TRI_vector_t const* shapeList,
TRI_index_operator_t const* indexOperator,
bool reverse) {
auto results = static_cast<TRI_skiplist_iterator_t*>(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_t), true));
if (results == nullptr) {
return nullptr; // calling procedure needs to care when the iterator is null
}
results->_index = skiplistIndex;
TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE,
sizeof(TRI_skiplist_iterator_interval_t));
results->_currentInterval = 0;
results->_cursor = nullptr;
if (reverse) {
// reverse iteration intentionally assigns the reverse traversal
// methods to hasNext() and next() so the interface remains the same
// for the caller!
results->hasNext = SkiplistHasPrevIterationCallback;
results->next = SkiplistPrevIterationCallback;
}
else {
results->hasNext = SkiplistHasNextIterationCallback;
results->next = SkiplistNextIterationCallback;
}
SkiplistIndex_findHelper(skiplistIndex, shapeList, indexOperator, &(results->_intervals));
size_t const n = TRI_LengthVector(&results->_intervals);
// Finally initialise _cursor if the result is not empty:
if (0 < n) {
if (reverse) {
// start at last interval, right endpoint
results->_currentInterval = n - 1;
auto tmp = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AtVector(&results->_intervals, n - 1));
results->_cursor = tmp->_rightEndPoint;
}
else {
// start at first interval, left endpoint
auto tmp = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AtVector(&results->_intervals, 0));
results->_cursor = tmp->_leftEndPoint;
}
}
return results;
}
TRI_skiplist_iterator_t* SkiplistIndex_find (SkiplistIndex* skiplistIndex,
TRI_index_operator_t const* indexOperator,
bool reverse) {
auto results = static_cast<TRI_skiplist_iterator_t*>(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_t), true));
if (results == nullptr) {
return nullptr; // calling procedure needs to care when the iterator is null
}
results->_index = skiplistIndex;
TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE,
sizeof(TRI_skiplist_iterator_interval_t));
results->_currentInterval = 0;
results->_cursor = nullptr;
if (reverse) {
// reverse iteration intentionally assigns the reverse traversal
// methods to hasNext() and next() so the interface remains the same
// for the caller!
results->hasNext = SkiplistHasPrevIterationCallback;
results->next = SkiplistPrevIterationCallback;
}
else {
results->hasNext = SkiplistHasNextIterationCallback;
results->next = SkiplistNextIterationCallback;
}
SkiplistIndex_findHelper(skiplistIndex, indexOperator, &(results->_intervals));
size_t const n = TRI_LengthVector(&results->_intervals);
// Finally initialise _cursor if the result is not empty:
if (0 < n) {
if (reverse) {
// start at last interval, right endpoint
results->_currentInterval = n - 1;
auto tmp = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AtVector(&results->_intervals, n - 1));
results->_cursor = tmp->_rightEndPoint;
}
else {
// start at first interval, left endpoint
auto tmp = static_cast<TRI_skiplist_iterator_interval_t*>(TRI_AtVector(&results->_intervals, 0));
results->_cursor = tmp->_leftEndPoint;
}
}
return results;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief inserts a data element into the skip list
/// ownership for the element is transferred to the index
////////////////////////////////////////////////////////////////////////////////
int SkiplistIndex_insert (SkiplistIndex* skiplistIndex,
TRI_skiplist_index_element_t* element) {
int res = skiplistIndex->skiplist->insert(element);
if (res != TRI_ERROR_NO_ERROR) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element);
}
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an entry from the skip list
/// ownership for the element is transferred to the index
////////////////////////////////////////////////////////////////////////////////
int SkiplistIndex_remove (SkiplistIndex* skiplistIndex,
TRI_skiplist_index_element_t* element) {
int res = skiplistIndex->skiplist->remove(element);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element);
if (res == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
// This is for the case of a rollback in an aborted transaction.
// We silently ignore the fact that the document was not there.
// This could also be useful for the case of a sparse index.
return TRI_ERROR_NO_ERROR;
}
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the number of elements in the skip list index
////////////////////////////////////////////////////////////////////////////////
uint64_t SkiplistIndex_getNrUsed (SkiplistIndex* skiplistIndex) {
return skiplistIndex->skiplist->getNrUsed();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the memory used by the index
////////////////////////////////////////////////////////////////////////////////
size_t SkiplistIndex_memoryUsage (SkiplistIndex const* skiplistIndex) {
return sizeof(SkiplistIndex) +
skiplistIndex->skiplist->memoryUsage() +
static_cast<size_t>(skiplistIndex->skiplist->getNrUsed()) * SkiplistIndex_ElementSize(skiplistIndex);
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: