mirror of https://gitee.com/bigwinds/arangodb
1538 lines
59 KiB
C
1538 lines
59 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief skiplistEx index
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright by triAGENS GmbH - All rights reserved.
|
|
///
|
|
/// The Programs (which include both the software and documentation)
|
|
/// contain proprietary information of triAGENS GmbH; they are
|
|
/// provided under a license agreement containing restrictions on use and
|
|
/// disclosure and are also protected by copyright, patent and other
|
|
/// intellectual and industrial property laws. Reverse engineering,
|
|
/// disassembly or decompilation of the Programs, except to the extent
|
|
/// required to obtain interoperability with other independently created
|
|
/// software or as specified by law, is prohibited.
|
|
///
|
|
/// The Programs are not intended for use in any nuclear, aviation, mass
|
|
/// transit, medical, or other inherently dangerous applications. It shall
|
|
/// be the licensee's responsibility to take all appropriate fail-safe,
|
|
/// backup, redundancy, and other measures to ensure the safe use of such
|
|
/// applications if the Programs are used for such purposes, and triAGENS
|
|
/// GmbH disclaims liability for any damages caused by such use of
|
|
/// the Programs.
|
|
///
|
|
/// This software is the confidential and proprietary information of
|
|
/// triAGENS GmbH. You shall not disclose such confidential and
|
|
/// proprietary information and shall use it only in accordance with the
|
|
/// terms of the license agreement you entered into with triAGENS GmbH.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. O
|
|
/// @author Copyright 2011, triagens GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "skiplistExIndex.h"
|
|
#include "VocBase/index-garbage-collector.h"
|
|
|
|
#ifdef TRI_SKIPLIST_EX
|
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
// Common private methods
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
// .............................................................................
|
|
// forward declaration - some helper methods
|
|
// .............................................................................
|
|
|
|
static bool skiplistExIndex_findHelperIntervalValid (SkiplistExIndex*, TRI_skiplistEx_iterator_interval_t*);
|
|
static bool multiSkiplistExIndex_findHelperIntervalValid (SkiplistExIndex*, TRI_skiplistEx_iterator_interval_t*);
|
|
|
|
|
|
// .............................................................................
|
|
// forward declaration of static functions which are used by the query engine
|
|
// .............................................................................
|
|
|
|
static int SkiplistExIndex_queryMethodCall (void*, TRI_index_operator_t*, TRI_index_challenge_t*, void*);
|
|
static TRI_index_iterator_t* SkiplistExIndex_resultMethodCall (void*, TRI_index_operator_t*, void*, bool (*filter) (TRI_index_iterator_t*));
|
|
static int SkiplistExIndex_freeMethodCall (void*, void*);
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Attempts to determine if there is a next document within an interval - without advancing the iterator.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool SkiplistExHasNextIterationCallback(TRI_skiplistEx_iterator_t* iterator) {
|
|
TRI_skiplistEx_iterator_interval_t* interval;
|
|
void* leftNode;
|
|
|
|
// .............................................................................
|
|
// Some simple checks.
|
|
// .............................................................................
|
|
|
|
if (iterator == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (iterator->_intervals._length == 0) {
|
|
return false;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// if we have more intervals than the one we are currently working on then of course we have a next doc
|
|
// .............................................................................
|
|
if (iterator->_intervals._length - 1 > iterator->_currentInterval) {
|
|
return true;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// Obtain the current interval -- in case we ever use more than one interval
|
|
// .............................................................................
|
|
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
|
|
|
|
// .............................................................................
|
|
// Obtain the left end point we are currently at
|
|
// .............................................................................
|
|
|
|
if (iterator->_cursor == NULL) {
|
|
leftNode = interval->_leftEndPoint;
|
|
}
|
|
else {
|
|
leftNode = iterator->_cursor;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// If the left == right end point AND there are no more intervals then we have
|
|
// no next.
|
|
// .............................................................................
|
|
|
|
if (leftNode == interval->_rightEndPoint) {
|
|
return false;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// interval of the type (a,b) -- but nothing between a and b
|
|
// such intervals are optimised out so will not be here
|
|
// ...........................................................................
|
|
if (iterator->_index->_unique) {
|
|
leftNode = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, leftNode, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
leftNode = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, leftNode, iterator->_thisTransID);
|
|
}
|
|
|
|
|
|
// ...........................................................................
|
|
// Check various possibilities
|
|
// ...........................................................................
|
|
|
|
if (leftNode == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (leftNode == interval->_rightEndPoint) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Attempts to determine if there is a previous document within an interval - without advancing the iterator.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool SkiplistExHasPrevIterationCallback(TRI_skiplistEx_iterator_t* iterator) {
|
|
TRI_skiplistEx_iterator_interval_t* interval;
|
|
void* rightNode;
|
|
|
|
// .............................................................................
|
|
// Some simple checks.
|
|
// .............................................................................
|
|
|
|
if (iterator == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (iterator->_intervals._length == 0) {
|
|
return false;
|
|
}
|
|
|
|
// .............................................................................
|
|
// this check is as follows if we have more intervals than the one
|
|
// we are currently working on -- then of course we have a prev doc
|
|
// .............................................................................
|
|
if (iterator->_currentInterval > 0 ) {
|
|
return true;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// Obtain the current interval -- in case we ever use more than one interval
|
|
// .............................................................................
|
|
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
|
|
|
|
// .............................................................................
|
|
// Obtain the left end point we are currently at
|
|
// .............................................................................
|
|
|
|
if (iterator->_cursor == NULL) {
|
|
rightNode = interval->_rightEndPoint;
|
|
}
|
|
else {
|
|
rightNode = iterator->_cursor;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// If the left == right end point AND there are no more intervals then we have
|
|
// no next.
|
|
// .............................................................................
|
|
|
|
if (rightNode == interval->_leftEndPoint) {
|
|
return false;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// interval of the type (a,b) -- but nothing between a and b
|
|
// such intervals are optimised out so will not be here
|
|
// ...........................................................................
|
|
if (iterator->_index->_unique) {
|
|
rightNode = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, rightNode, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
rightNode = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, rightNode, iterator->_thisTransID);
|
|
}
|
|
|
|
|
|
// ...........................................................................
|
|
// Check various possibilities
|
|
// ...........................................................................
|
|
|
|
if (rightNode == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (rightNode == interval->_leftEndPoint) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Jumps forwards or backwards by jumpSize and returns the document
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void* SkiplistExIteration(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) {
|
|
TRI_skiplistEx_iterator_interval_t* interval;
|
|
TRI_skiplistEx_node_t* currentNode;
|
|
int64_t j;
|
|
|
|
// .............................................................................
|
|
// Some simple checks.
|
|
// .............................................................................
|
|
|
|
if (iterator == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (iterator->_intervals._length == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor);
|
|
|
|
if (jumpSize == 0) {
|
|
if (currentNode == NULL) {
|
|
return NULL;
|
|
}
|
|
else {
|
|
return &(currentNode->_element);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// .............................................................................
|
|
// If the current cursor is NULL and jumpSize < 0, then start at the endpoint of
|
|
// the right most interval.
|
|
// .............................................................................
|
|
if (currentNode == NULL && jumpSize < 0) {
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_intervals._length - 1) );
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_rightEndPoint, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_rightEndPoint, iterator->_thisTransID);
|
|
}
|
|
|
|
currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor);
|
|
|
|
if (currentNode == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (currentNode == interval->_leftEndPoint) {
|
|
return NULL;
|
|
}
|
|
|
|
return &(currentNode->_element);
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// If the current cursor is NULL and jumpSize > 0, then start at the left point of
|
|
// the left most interval.
|
|
// .............................................................................
|
|
if (currentNode == NULL && jumpSize > 0) {
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), 0) );
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID);
|
|
}
|
|
|
|
currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor);
|
|
|
|
if (currentNode == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (currentNode == interval->_rightEndPoint) {
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID);
|
|
}
|
|
|
|
currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor);
|
|
return NULL;
|
|
}
|
|
|
|
return &(currentNode->_element);
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// Obtain the current interval we are at.
|
|
// .............................................................................
|
|
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
|
|
// .............................................................................
|
|
// use the current cursor and move jumpSize back.
|
|
// .............................................................................
|
|
if (jumpSize < 0) {
|
|
jumpSize = -jumpSize;
|
|
for (j = 0; j < jumpSize; ++j) {
|
|
if (iterator->_cursor == interval->_leftEndPoint) {
|
|
if (iterator->_currentInterval == 0) {
|
|
return NULL;
|
|
}
|
|
--iterator->_currentInterval;
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
iterator->_cursor = interval->_rightEndPoint;
|
|
}
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
|
|
}
|
|
|
|
if (iterator->_cursor == interval->_leftEndPoint) {
|
|
if (iterator->_currentInterval == 0) {
|
|
return NULL;
|
|
}
|
|
--iterator->_currentInterval;
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
iterator->_cursor = interval->_rightEndPoint;
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// use the current cursor and move jumpSize forward.
|
|
// .............................................................................
|
|
if (jumpSize > 0) {
|
|
|
|
for (j = 0; j < jumpSize; ++j) {
|
|
if (iterator->_cursor == interval->_rightEndPoint) {
|
|
if (iterator->_currentInterval == (iterator->_intervals._length - 1)) {
|
|
return NULL;
|
|
}
|
|
++iterator->_currentInterval;
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
iterator->_cursor = interval->_leftEndPoint;
|
|
}
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
}
|
|
|
|
if (iterator->_cursor == interval->_rightEndPoint) {
|
|
if (iterator->_currentInterval == (iterator->_intervals._length - 1)) {
|
|
return NULL;
|
|
}
|
|
++iterator->_currentInterval;
|
|
interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) );
|
|
iterator->_cursor = interval->_leftEndPoint;
|
|
|
|
if (iterator->_index->_unique) {
|
|
iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
else {
|
|
iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor);
|
|
if (currentNode == NULL) {
|
|
return NULL;
|
|
}
|
|
return &(currentNode->_element);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief default callback for jumping forward by 1
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void* SkiplistExNextIterationCallback(TRI_skiplistEx_iterator_t* iterator) {
|
|
return SkiplistExIteration(iterator,1);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief default callback for jumping forward by jumpSize docs
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void* SkiplistExNextsIterationCallback(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) {
|
|
return SkiplistExIteration(iterator,jumpSize);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief default callback for jumping backwards by 1
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void* SkiplistExPrevIterationCallback(TRI_skiplistEx_iterator_t* iterator) {
|
|
return SkiplistExIteration(iterator,-1);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief default callback for jumping backwards by jumpSize docs
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void* SkiplistExPrevsIterationCallback(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) {
|
|
return SkiplistExIteration(iterator,-jumpSize);
|
|
}
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- skiplistIndex common public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup skiplistIndex
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Assigns a static function call to a function pointer used by Query Engine
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int SkiplistExIndex_assignMethod(void* methodHandle, TRI_index_method_assignment_type_e methodType) {
|
|
switch (methodType) {
|
|
|
|
case TRI_INDEX_METHOD_ASSIGNMENT_FREE : {
|
|
TRI_index_query_free_method_call_t* call = (TRI_index_query_free_method_call_t*)(methodHandle);
|
|
*call = SkiplistExIndex_freeMethodCall;
|
|
break;
|
|
}
|
|
|
|
case TRI_INDEX_METHOD_ASSIGNMENT_QUERY : {
|
|
TRI_index_query_method_call_t* call = (TRI_index_query_method_call_t*)(methodHandle);
|
|
*call = SkiplistExIndex_queryMethodCall;
|
|
break;
|
|
}
|
|
|
|
case TRI_INDEX_METHOD_ASSIGNMENT_RESULT : {
|
|
TRI_index_query_result_method_call_t* call = (TRI_index_query_result_method_call_t*)(methodHandle);
|
|
*call = SkiplistExIndex_resultMethodCall;
|
|
break;
|
|
}
|
|
|
|
default : {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Free a skiplist iterator
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeSkiplistExIterator (TRI_skiplistEx_iterator_t* const iterator) {
|
|
assert(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 SkiplistExIndex_destroy(SkiplistExIndex* slIndex) {
|
|
if (slIndex == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (slIndex->_unique) {
|
|
TRI_FreeSkipListEx(slIndex->_skiplistEx.uniqueSkiplistEx);
|
|
slIndex->_skiplistEx.uniqueSkiplistEx = NULL;
|
|
}
|
|
else {
|
|
TRI_FreeSkipListExMulti(slIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
slIndex->_skiplistEx.nonUniqueSkiplistEx = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destroys a skip list index and frees the pointer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SkiplistExIndex_free(SkiplistExIndex* slIndex) {
|
|
if (slIndex == NULL) {
|
|
return;
|
|
}
|
|
SkiplistExIndex_destroy(slIndex);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, slIndex);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
// Unique Skiplists
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Methods Unique Skiplists
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Methods Unique Skiplists
|
|
//------------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new unique entry skiplist
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkiplistExIndex* SkiplistExIndex_new(TRI_transaction_context_t* transactionContext) {
|
|
SkiplistExIndex* skiplistExIndex;
|
|
int result;
|
|
uint64_t lastKnownTransID = 0;
|
|
|
|
skiplistExIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(SkiplistExIndex), true);
|
|
if (skiplistExIndex == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
skiplistExIndex->_unique = true;
|
|
skiplistExIndex->_skiplistEx.uniqueSkiplistEx = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_t), true);
|
|
if (skiplistExIndex->_skiplistEx.uniqueSkiplistEx == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex);
|
|
return NULL;
|
|
}
|
|
|
|
skiplistExIndex->_transactionContext = transactionContext;
|
|
|
|
// TODO ORESTE: determine the last know transaction id from the transaction context
|
|
result = TRI_InitSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx,
|
|
sizeof(SkiplistExIndexElement),
|
|
NULL,NULL,TRI_SKIPLIST_EX_PROB_HALF, 40,
|
|
lastKnownTransID);
|
|
|
|
if (result != TRI_ERROR_NO_ERROR) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex->_skiplistEx.uniqueSkiplistEx);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex);
|
|
return NULL;
|
|
}
|
|
|
|
return skiplistExIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds (inserts) a data element into a unique skip list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int SkiplistExIndex_add(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
int result;
|
|
result = TRI_InsertKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, element, element, false, thisTransID);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Locates one or more ranges within the unique 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 void debugElement(SkiplistIndex* skiplistIndex, TRI_skiplist_node_t* node) {
|
|
size_t numFields;
|
|
SkiplistIndexElement* element = (SkiplistIndexElement*)(&(node->_element));
|
|
TRI_shaper_t* shaper;
|
|
size_t j;
|
|
|
|
if (node == NULL) {
|
|
printf("%s:%u:node null\n",__FILE__,__LINE__);
|
|
return;
|
|
}
|
|
|
|
if (node == TRI_StartNodeSkipList(skiplistIndex->skiplist.uniqueSkiplist)) {
|
|
printf("%s:%u:start node\n",__FILE__,__LINE__);
|
|
}
|
|
|
|
if (node == TRI_EndNodeSkipList(skiplistIndex->skiplist.uniqueSkiplist)) {
|
|
printf("%s:%u:end node\n",__FILE__,__LINE__);
|
|
}
|
|
|
|
if (element == NULL) {
|
|
printf("%s:%u:element null\n",__FILE__,__LINE__);
|
|
return;
|
|
}
|
|
|
|
numFields = element->numFields;
|
|
shaper = ((TRI_primary_collection_t*)(element->collection))->_shaper;
|
|
|
|
for (j = 0; j < numFields; j++) {
|
|
printf("%s:%u:!!!:%f:%lu\n",__FILE__,__LINE__,
|
|
*((double*)((j + element->fields)->_data.data)),
|
|
(long unsigned int)(element->data) );
|
|
}
|
|
return;
|
|
}
|
|
*/
|
|
|
|
static bool skiplistExIndex_findHelperIntervalIntersectionValid(SkiplistExIndex* skiplistExIndex,
|
|
TRI_skiplistEx_iterator_interval_t* lInterval,
|
|
TRI_skiplistEx_iterator_interval_t* rInterval,
|
|
TRI_skiplistEx_iterator_interval_t* interval) {
|
|
int compareResult;
|
|
TRI_skiplistEx_node_t* lNode;
|
|
TRI_skiplistEx_node_t* rNode;
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(lInterval->_leftEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(rInterval->_leftEndPoint);
|
|
|
|
if (lNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx) || lNode == NULL ||
|
|
rNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx) || rNode == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (lNode == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
compareResult = -1;
|
|
}
|
|
else if (rNode == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
compareResult = 1;
|
|
}
|
|
else {
|
|
compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.uniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
}
|
|
|
|
if (compareResult < 1) {
|
|
interval->_leftEndPoint = rNode;
|
|
}
|
|
else {
|
|
interval->_leftEndPoint = lNode;
|
|
}
|
|
|
|
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(lInterval->_rightEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(rInterval->_rightEndPoint);
|
|
|
|
if (lNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
compareResult = 1;
|
|
}
|
|
else if (rNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
compareResult = -1;
|
|
}
|
|
else {
|
|
compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.uniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
}
|
|
|
|
if (compareResult < 1) {
|
|
interval->_rightEndPoint = lNode;
|
|
}
|
|
else {
|
|
interval->_rightEndPoint = rNode;
|
|
}
|
|
|
|
return skiplistExIndex_findHelperIntervalValid(skiplistExIndex, interval);
|
|
}
|
|
|
|
static bool skiplistExIndex_findHelperIntervalValid(SkiplistExIndex* skiplistExIndex, TRI_skiplistEx_iterator_interval_t* interval) {
|
|
int compareResult;
|
|
TRI_skiplistEx_node_t* lNode;
|
|
TRI_skiplistEx_node_t* rNode;
|
|
|
|
|
|
if ((interval->_leftEndPoint == NULL) || (interval->_rightEndPoint == NULL)) {
|
|
return false;
|
|
}
|
|
|
|
if (interval->_leftEndPoint == interval->_rightEndPoint) {
|
|
return false;
|
|
}
|
|
|
|
if ( (interval->_leftEndPoint == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) ||
|
|
(interval->_rightEndPoint == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx))) {
|
|
return true;
|
|
}
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(interval->_leftEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(interval->_rightEndPoint);
|
|
|
|
|
|
compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.uniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
return (compareResult == -1);
|
|
}
|
|
|
|
|
|
static void SkiplistExIndex_findHelper(SkiplistExIndex* skiplistExIndex,
|
|
TRI_vector_t* shapeList,
|
|
TRI_index_operator_t* indexOperator,
|
|
TRI_vector_t* resultIntervalList,
|
|
uint64_t thisTransID) {
|
|
|
|
SkiplistExIndexElement values;
|
|
TRI_vector_t leftResult;
|
|
TRI_vector_t rightResult;
|
|
TRI_relation_index_operator_t* relationOperator;
|
|
TRI_logical_index_operator_t* logicalOperator;
|
|
TRI_skiplistEx_iterator_interval_t interval;
|
|
TRI_skiplistEx_iterator_interval_t* tempLeftInterval;
|
|
TRI_skiplistEx_iterator_interval_t* tempRightInterval;
|
|
size_t j;
|
|
size_t i;
|
|
|
|
TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
|
|
relationOperator = (TRI_relation_index_operator_t*)(indexOperator);
|
|
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;
|
|
values.collection = relationOperator->_collection;
|
|
values.data = 0; // we do not have a document pointer
|
|
|
|
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_SL_OR_OPERATOR: {
|
|
SkiplistIndex_findHelper(skiplistIndex,shapeList,logicalOperator->_left,&leftResult);
|
|
SkiplistIndex_findHelper(skiplistIndex,shapeList,logicalOperator->_right,&leftResult);
|
|
i = 0;
|
|
while (i < leftResult._length - 1) {
|
|
tempLeftInterval = (TRI_skiplist_iterator_interval_t*) (TRI_AtVector(&leftResult, i));
|
|
tempRightInterval = (TRI_skiplist_iterator_interval_t*) (TRI_AtVector(&leftResult, i + 1));
|
|
// if intervals intersect, optimise and start again
|
|
}
|
|
assert(0);
|
|
}
|
|
*/
|
|
|
|
case TRI_AND_INDEX_OPERATOR: {
|
|
SkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_left,&leftResult, thisTransID);
|
|
SkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_right,&rightResult, thisTransID);
|
|
|
|
for (i = 0; i < leftResult._length; ++i) {
|
|
for (j = 0; j < rightResult._length; ++j) {
|
|
tempLeftInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&leftResult, i));
|
|
tempRightInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&rightResult, j));
|
|
if (!skiplistExIndex_findHelperIntervalIntersectionValid(skiplistExIndex, tempLeftInterval,
|
|
tempRightInterval, &interval)) {
|
|
continue;
|
|
}
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
}
|
|
TRI_DestroyVector(&leftResult);
|
|
TRI_DestroyVector(&rightResult);
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_EQ_INDEX_OPERATOR: {
|
|
// ............................................................................
|
|
// The index is constructed from n fields and the client has sent us n values
|
|
// ............................................................................
|
|
if (relationOperator->_numFields == shapeList->_length) {
|
|
interval._leftEndPoint = TRI_LookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
if (interval._leftEndPoint != NULL) {
|
|
interval._rightEndPoint = TRI_NextNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID);
|
|
interval._leftEndPoint = TRI_PrevNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID);
|
|
}
|
|
}
|
|
// ............................................................................
|
|
// The index is constructed from n fields and the client has sent us m values
|
|
// where m < n
|
|
// ............................................................................
|
|
else {
|
|
interval._leftEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
}
|
|
|
|
if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
case TRI_LE_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx);
|
|
interval._rightEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_LT_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx);
|
|
interval._rightEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
if (interval._rightEndPoint != TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
interval._rightEndPoint = TRI_NextNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._rightEndPoint, thisTransID);
|
|
}
|
|
if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList, &interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_GE_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx);
|
|
if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_GT_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx);
|
|
if (interval._leftEndPoint != TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) {
|
|
interval._leftEndPoint = TRI_PrevNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID);
|
|
}
|
|
if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
default: {
|
|
assert(0);
|
|
}
|
|
|
|
} // end of switch statement
|
|
|
|
|
|
}
|
|
|
|
TRI_skiplistEx_iterator_t* SkiplistExIndex_find(SkiplistExIndex* skiplistExIndex, TRI_vector_t* shapeList, TRI_index_operator_t* indexOperator, uint64_t thisTransID) {
|
|
TRI_skiplistEx_iterator_t* results;
|
|
|
|
results = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_t), true);
|
|
if (results == NULL) {
|
|
return NULL; // calling procedure needs to care when the iterator is null
|
|
}
|
|
results->_index = skiplistExIndex;
|
|
TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
results->_currentInterval = 0;
|
|
results->_cursor = NULL;
|
|
results->_thisTransID = thisTransID;
|
|
results->_hasNext = SkiplistExHasNextIterationCallback;
|
|
results->_next = SkiplistExNextIterationCallback;
|
|
results->_nexts = SkiplistExNextsIterationCallback;
|
|
results->_hasPrev = SkiplistExHasPrevIterationCallback;
|
|
results->_prev = SkiplistExPrevIterationCallback;
|
|
results->_prevs = SkiplistExPrevsIterationCallback;
|
|
|
|
SkiplistExIndex_findHelper(skiplistExIndex, shapeList, indexOperator, &(results->_intervals), thisTransID);
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief alias for addIndex
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int SkiplistExIndex_insert(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
return SkiplistExIndex_add(skiplistExIndex,element, thisTransID);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes an entry from the skip list
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int CollectSkiplistExGarbage(TRI_index_gc_t* indexGCData) {
|
|
int result = TRI_ERROR_NO_ERROR;
|
|
SkiplistExIndex* skiplistIndex;
|
|
TRI_skiplistEx_node_t* passNode;
|
|
TRI_skiplistEx_t* skiplist;
|
|
|
|
if (indexGCData == NULL) {
|
|
abort(); // remove after debugging
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
skiplistIndex = (SkiplistExIndex*)(indexGCData->_index);
|
|
if (skiplistIndex == NULL) {
|
|
abort(); // remove after debugging
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
skiplist = (skiplistIndex->_skiplistEx).uniqueSkiplistEx;
|
|
|
|
passNode = (TRI_skiplistEx_node_t*)(indexGCData->_data);
|
|
if (passNode == NULL) {
|
|
abort(); // remove after debugging
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
|
|
switch (indexGCData->_lastPass) {
|
|
case 1: { // the first call from the garbage collector
|
|
result = TRI_RemoveElementSkipListEx (skiplist, NULL, NULL, 2, passNode->_delTransID, &passNode);
|
|
break;
|
|
}
|
|
|
|
case 2: {
|
|
result = TRI_RemoveElementSkipListEx (skiplist, NULL, NULL, 3, passNode->_delTransID, &passNode);
|
|
break;
|
|
}
|
|
|
|
case 254: { // just before the node is excised from the skiplist
|
|
result = TRI_ERROR_NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
case 255: { // just AFTER the node is excised from the skiplist
|
|
result = TRI_ERROR_NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
default : {
|
|
abort();
|
|
}
|
|
|
|
} // end of switch statement
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int SkiplistExIndex_remove(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
int result;
|
|
TRI_skiplistEx_node_t* passNode;
|
|
TRI_index_gc_t indexGCData;
|
|
|
|
|
|
// ............................................................................
|
|
// This has been called from the database so it has a pass level of 1
|
|
// ............................................................................
|
|
|
|
result = TRI_RemoveElementSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, element,
|
|
NULL, 1, thisTransID, &passNode);
|
|
if (result == TRI_ERROR_NO_ERROR) {
|
|
|
|
// ..........................................................................
|
|
// add to garbage collection
|
|
// ..........................................................................
|
|
|
|
indexGCData._index = (void*)(skiplistExIndex);
|
|
indexGCData._passes = 2;
|
|
indexGCData._lastPass = 0; // will be assigned correctly by the GC
|
|
indexGCData._transID = 0; // will be assigned correctly by the GC
|
|
indexGCData._data = passNode; // the address of the node in the linked list which will eventually be excised
|
|
indexGCData._collectGarbage = CollectSkiplistExGarbage;
|
|
|
|
// ...........................................................................
|
|
// Adds an item to the rubbish collection linked list.
|
|
// This can fail if the GC is busy and the CAS statements in the GC fail.
|
|
// It is up to the calling procedure to determine what to do with this failure,
|
|
// generally a retry will suffice.
|
|
// ...........................................................................
|
|
result = TRI_AddToIndexGC(&indexGCData);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates a skiplist entry
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool SkiplistExIndex_update(SkiplistExIndex* skiplistExIndex, const SkiplistExIndexElement* beforeElement, const SkiplistExIndexElement* afterElement, uint64_t thisTransID) {
|
|
// updates an entry in the skip list, first removes beforeElement,
|
|
// then adds the afterElement -- should never be called here
|
|
// call SkiplistIndex_remove first and then SkiplistIndex_add
|
|
assert(false);
|
|
return false; // shuts the VC++ up
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
// Multi-skiplist non-unique skiplist indexes
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Methods Non-Unique Muilti Skiplists
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new non-uniqe (allows duplicates) multi skiplist
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkiplistExIndex* MultiSkiplistExIndex_new(TRI_transaction_context_t* transactionContext) {
|
|
SkiplistExIndex* skiplistExIndex;
|
|
int result;
|
|
uint64_t lastKnownTransID = 0;
|
|
|
|
skiplistExIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(SkiplistExIndex), true);
|
|
if (skiplistExIndex == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
skiplistExIndex->_unique = false;
|
|
skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_multi_t), true);
|
|
if (skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex);
|
|
return NULL;
|
|
}
|
|
|
|
skiplistExIndex->_transactionContext = transactionContext;
|
|
|
|
// TODO ORESTE: determine the last know transaction id from the transaction context
|
|
result = TRI_InitSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx,
|
|
sizeof(SkiplistExIndexElement),
|
|
NULL, NULL, NULL,TRI_SKIPLIST_EX_PROB_HALF, 40,
|
|
lastKnownTransID);
|
|
if (result != TRI_ERROR_NO_ERROR) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex);
|
|
return NULL;
|
|
}
|
|
return skiplistExIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds (inserts) a data element into a multi skiplist
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
int MultiSkiplistExIndex_add(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
int result;
|
|
result = TRI_InsertElementSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, element, false, thisTransID);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Locates one or more ranges within the unique skiplist and returns iterator
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool multiSkiplistExIndex_findHelperIntervalIntersectionValid(SkiplistExIndex* skiplistExIndex,
|
|
TRI_skiplistEx_iterator_interval_t* lInterval,
|
|
TRI_skiplistEx_iterator_interval_t* rInterval,
|
|
TRI_skiplistEx_iterator_interval_t* interval) {
|
|
int compareResult;
|
|
TRI_skiplistEx_node_t* lNode;
|
|
TRI_skiplistEx_node_t* rNode;
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(lInterval->_leftEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(rInterval->_leftEndPoint);
|
|
|
|
if (lNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx) || lNode == NULL ||
|
|
rNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx) || rNode == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (lNode == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
compareResult = -1;
|
|
}
|
|
else if (rNode == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
compareResult = 1;
|
|
}
|
|
else {
|
|
compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
}
|
|
|
|
if (compareResult < 1) {
|
|
interval->_leftEndPoint = rNode;
|
|
}
|
|
else {
|
|
interval->_leftEndPoint = lNode;
|
|
}
|
|
|
|
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(lInterval->_rightEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(rInterval->_rightEndPoint);
|
|
|
|
if (lNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
compareResult = 1;
|
|
}
|
|
else if (rNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
compareResult = -1;
|
|
}
|
|
else {
|
|
compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
}
|
|
|
|
if (compareResult < 1) {
|
|
interval->_rightEndPoint = lNode;
|
|
}
|
|
else {
|
|
interval->_rightEndPoint = rNode;
|
|
}
|
|
|
|
return multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, interval);
|
|
}
|
|
|
|
|
|
|
|
static bool multiSkiplistExIndex_findHelperIntervalValid(SkiplistExIndex* skiplistExIndex, TRI_skiplistEx_iterator_interval_t* interval) {
|
|
int compareResult;
|
|
TRI_skiplistEx_node_t* lNode;
|
|
TRI_skiplistEx_node_t* rNode;
|
|
|
|
|
|
if ((interval->_leftEndPoint == NULL) || (interval->_rightEndPoint == NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
if (interval->_leftEndPoint == interval->_rightEndPoint) {
|
|
return 0;
|
|
}
|
|
|
|
if ( (interval->_leftEndPoint == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) ||
|
|
(interval->_rightEndPoint == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx))) {
|
|
return -1;
|
|
}
|
|
|
|
lNode = (TRI_skiplistEx_node_t*)(interval->_leftEndPoint);
|
|
rNode = (TRI_skiplistEx_node_t*)(interval->_rightEndPoint);
|
|
|
|
|
|
compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement(
|
|
skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx,
|
|
&(lNode->_element), &(rNode->_element), 0);
|
|
|
|
|
|
return (compareResult == -1);
|
|
}
|
|
|
|
|
|
|
|
static void MultiSkiplistExIndex_findHelper(SkiplistExIndex* skiplistExIndex,
|
|
TRI_vector_t* shapeList,
|
|
TRI_index_operator_t* indexOperator,
|
|
TRI_vector_t* resultIntervalList,
|
|
uint64_t thisTransID) {
|
|
|
|
SkiplistExIndexElement values;
|
|
TRI_vector_t leftResult;
|
|
TRI_vector_t rightResult;
|
|
TRI_relation_index_operator_t* relationOperator;
|
|
TRI_logical_index_operator_t* logicalOperator;
|
|
TRI_skiplistEx_iterator_interval_t interval;
|
|
TRI_skiplistEx_iterator_interval_t* tempLeftInterval;
|
|
TRI_skiplistEx_iterator_interval_t* tempRightInterval;
|
|
size_t j;
|
|
size_t i;
|
|
|
|
TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
|
|
logicalOperator = (TRI_logical_index_operator_t*)(indexOperator);
|
|
relationOperator = (TRI_relation_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;
|
|
values.collection = relationOperator->_collection;
|
|
values.data = 0; // no document pointer available
|
|
|
|
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_SL_OR_OPERATOR: {
|
|
todo
|
|
}
|
|
*/
|
|
|
|
case TRI_AND_INDEX_OPERATOR: {
|
|
MultiSkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_left,&leftResult, thisTransID);
|
|
MultiSkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_right,&rightResult, thisTransID);
|
|
|
|
for (i = 0; i < leftResult._length; ++i) {
|
|
for (j = 0; j < rightResult._length; ++j) {
|
|
tempLeftInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&leftResult, i));
|
|
tempRightInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&rightResult, j));
|
|
if (!multiSkiplistExIndex_findHelperIntervalIntersectionValid(skiplistExIndex,tempLeftInterval,
|
|
tempRightInterval, &interval)) {
|
|
continue;
|
|
}
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
}
|
|
TRI_DestroyVector(&leftResult);
|
|
TRI_DestroyVector(&rightResult);
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_EQ_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList, &interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_LE_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
interval._rightEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList, &interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_LT_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
interval._rightEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
if (interval._rightEndPoint != TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
interval._rightEndPoint = TRI_NextNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, interval._rightEndPoint, thisTransID);
|
|
}
|
|
if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_GE_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList,&interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
case TRI_GT_INDEX_OPERATOR: {
|
|
interval._leftEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID);
|
|
interval._rightEndPoint = TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx);
|
|
if (interval._leftEndPoint != TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) {
|
|
interval._leftEndPoint = TRI_PrevNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, interval._leftEndPoint, thisTransID);
|
|
}
|
|
if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) {
|
|
TRI_PushBackVector(resultIntervalList, &interval);
|
|
}
|
|
return;
|
|
}
|
|
|
|
default: {
|
|
assert(0);
|
|
}
|
|
|
|
} // end of switch statement
|
|
}
|
|
|
|
|
|
TRI_skiplistEx_iterator_t* MultiSkiplistExIndex_find(SkiplistExIndex* skiplistExIndex, TRI_vector_t* shapeList, TRI_index_operator_t* indexOperator, uint64_t thisTransID) {
|
|
TRI_skiplistEx_iterator_t* results;
|
|
|
|
results = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_t), false);
|
|
if (results == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
results->_index = skiplistExIndex;
|
|
TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t));
|
|
results->_currentInterval = 0;
|
|
results->_cursor = NULL;
|
|
results->_thisTransID = thisTransID;
|
|
results->_hasNext = SkiplistExHasNextIterationCallback;
|
|
results->_next = SkiplistExNextIterationCallback;
|
|
results->_nexts = SkiplistExNextsIterationCallback;
|
|
results->_hasPrev = SkiplistExHasPrevIterationCallback;
|
|
results->_prev = SkiplistExPrevIterationCallback;
|
|
results->_prevs = SkiplistExPrevsIterationCallback;
|
|
|
|
MultiSkiplistExIndex_findHelper(skiplistExIndex, shapeList, indexOperator, &(results->_intervals), thisTransID);
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief alias for addIndex
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int MultiSkiplistExIndex_insert(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
return MultiSkiplistExIndex_add(skiplistExIndex, element, thisTransID);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes an entry from the skiplist
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int CollectSkiplistExMultiGarbage(TRI_index_gc_t* indexGCData) {
|
|
int result = TRI_ERROR_NO_ERROR;
|
|
return result;
|
|
}
|
|
|
|
|
|
int MultiSkiplistExIndex_remove(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) {
|
|
int result;
|
|
TRI_skiplistEx_node_t* passNode;
|
|
TRI_index_gc_t indexGCData;
|
|
|
|
// ............................................................................
|
|
// This has been called from the database so it has a pass level of 1
|
|
// ............................................................................
|
|
|
|
result = TRI_RemoveElementSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx,
|
|
element, NULL, 1, thisTransID, &passNode);
|
|
if (result == TRI_ERROR_NO_ERROR) {
|
|
|
|
// ..........................................................................
|
|
// add to garbage collection
|
|
// ..........................................................................
|
|
|
|
indexGCData._index = (void*)(skiplistExIndex);
|
|
indexGCData._passes = 2;
|
|
indexGCData._lastPass = 0; // will be assigned correctly by the GC
|
|
indexGCData._transID = 0; // will be assigned correctly by the GC
|
|
indexGCData._data = passNode; // the address of the node in the linked list which will eventually be excised
|
|
indexGCData._collectGarbage = CollectSkiplistExMultiGarbage;
|
|
result = TRI_AddToIndexGC(&indexGCData); // adds an item to the rubbish collection linked list
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates and entry in a multi skiplist
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MultiSkiplistExIndex_update(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* beforeElement, SkiplistExIndexElement* afterElement, uint64_t thisTransID) {
|
|
assert(false); // should never be called directly
|
|
return false; // shuts the VC++ up
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of forward declared query engine callback functions
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int SkiplistExIndex_queryMethodCall(void* theIndex, TRI_index_operator_t* indexOperator,
|
|
TRI_index_challenge_t* challenge, void* data) {
|
|
SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex);
|
|
if (slIndex == NULL || indexOperator == NULL) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
assert(false);
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
static TRI_index_iterator_t* SkiplistExIndex_resultMethodCall(void* theIndex, TRI_index_operator_t* indexOperator,
|
|
void* data, bool (*filter) (TRI_index_iterator_t*)) {
|
|
SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex);
|
|
if (slIndex == NULL || indexOperator == NULL) {
|
|
return NULL;
|
|
}
|
|
assert(false);
|
|
return NULL;
|
|
}
|
|
|
|
static int SkiplistExIndex_freeMethodCall(void* theIndex, void* data) {
|
|
SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex);
|
|
if (slIndex == NULL) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
assert(false);
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#endif
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
|
// End:
|
|
|
|
|