mirror of https://gitee.com/bigwinds/arangodb
4007 lines
130 KiB
C
4007 lines
130 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief document collection with global read-write lock
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2010-2011 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 triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. Frank Celler
|
|
/// @author Copyright 2011, triagens GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "simple-collection.h"
|
|
|
|
#include "BasicsC/conversions.h"
|
|
#include "BasicsC/files.h"
|
|
#include "BasicsC/hashes.h"
|
|
#include "BasicsC/logging.h"
|
|
#include "BasicsC/strings.h"
|
|
#include "ShapedJson/shape-accessor.h"
|
|
#include "VocBase/index.h"
|
|
#include "VocBase/voc-shaper.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- forward declarations
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static int CreateImmediateIndexes (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t* header);
|
|
|
|
static int UpdateImmediateIndexes (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_doc_mptr_t const* update);
|
|
|
|
static int DeleteImmediateIndexes (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_voc_tick_t);
|
|
|
|
static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_doc_document_marker_t* marker,
|
|
size_t markerSize,
|
|
void const* body,
|
|
TRI_voc_size_t bodySize,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
TRI_df_marker_t** result,
|
|
bool release,
|
|
bool allowRollback);
|
|
|
|
static int DeleteDocument (TRI_sim_collection_t* collection,
|
|
TRI_doc_deletion_marker_t* marker,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
bool release);
|
|
|
|
static int DeleteShapedJson (TRI_doc_collection_t* doc,
|
|
TRI_voc_did_t did,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
bool release);
|
|
|
|
static TRI_index_t* CreateCapConstraintSimCollection (TRI_sim_collection_t* sim,
|
|
size_t size,
|
|
TRI_idx_iid_t iid,
|
|
bool* created);
|
|
|
|
static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collection,
|
|
char const* location,
|
|
char const* latitude,
|
|
char const* longitude,
|
|
bool geoJson,
|
|
bool constraint,
|
|
bool ignoreNull,
|
|
TRI_idx_iid_t iid,
|
|
bool* created);
|
|
|
|
static TRI_index_t* CreateHashIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created);
|
|
|
|
static TRI_index_t* CreatePriorityQueueIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created);
|
|
|
|
static TRI_index_t* CreateSkiplistIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created);
|
|
|
|
static uint64_t HashKeyHeader (TRI_associative_pointer_t* array, void const* key);
|
|
|
|
static uint64_t HashElementDocument (TRI_associative_pointer_t* array, void const* element);
|
|
|
|
static bool IsEqualKeyDocument (TRI_associative_pointer_t* array, void const* key, void const* element);
|
|
|
|
static int InsertPrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc);
|
|
static int UpdatePrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc, TRI_shaped_json_t const* old);
|
|
static int RemovePrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc);
|
|
static TRI_json_t* JsonPrimary (TRI_index_t* idx, TRI_doc_collection_t const* collection);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- JOURNALS
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief selects a journal, possibly waits until a journal appears
|
|
///
|
|
/// Note that the function grabs a lock. We have to release this lock, in order
|
|
/// to allow the gc to start when waiting for a journal to appear.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_datafile_t* SelectJournal (TRI_sim_collection_t* sim,
|
|
TRI_voc_size_t size,
|
|
TRI_df_marker_t** result) {
|
|
TRI_datafile_t* datafile;
|
|
int res;
|
|
size_t i;
|
|
size_t n;
|
|
|
|
TRI_LOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
|
|
while (sim->base.base._state == TRI_COL_STATE_WRITE) {
|
|
n = sim->base.base._journals._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
// select datafile
|
|
datafile = sim->base.base._journals._buffer[i];
|
|
|
|
// try to reserve space
|
|
res = TRI_ReserveElementDatafile(datafile, size, result);
|
|
|
|
// in case of full datafile, try next
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
TRI_UNLOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
return datafile;
|
|
}
|
|
else if (res != TRI_ERROR_AVOCADO_DATAFILE_FULL) {
|
|
TRI_UNLOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
TRI_WAIT_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
}
|
|
|
|
TRI_UNLOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief waits for synchronisation
|
|
///
|
|
/// Note that a datafile is never freed. If the datafile is closed the state
|
|
/// is set to TRI_DF_STATE_CLOSED - but the datafile pointer is still valid.
|
|
/// If a datafile is closed - then the data has been copied to some other
|
|
/// datafile and has been synced.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void WaitSync (TRI_sim_collection_t* sim,
|
|
TRI_datafile_t* journal,
|
|
char const* position) {
|
|
TRI_collection_t* base;
|
|
|
|
base = &sim->base.base;
|
|
|
|
// no condition at all. Do NOT acquire a lock, in the worst
|
|
// case we will miss a parameter change.
|
|
|
|
if (! base->_waitForSync) {
|
|
return;
|
|
}
|
|
|
|
TRI_LOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
|
|
// wait until the sync condition is fullfilled
|
|
while (true) {
|
|
|
|
// check for error
|
|
if (journal->_state == TRI_DF_STATE_WRITE_ERROR) {
|
|
break;
|
|
}
|
|
|
|
// check for close
|
|
if (journal->_state == TRI_DF_STATE_CLOSED) {
|
|
break;
|
|
}
|
|
|
|
// always sync
|
|
if (position <= journal->_synced) {
|
|
break;
|
|
}
|
|
|
|
// we have to wait a bit longer
|
|
TRI_WAIT_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
}
|
|
|
|
TRI_UNLOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief writes data to the journal and updates the barriers
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int WriteElement (TRI_sim_collection_t* sim,
|
|
TRI_datafile_t* journal,
|
|
TRI_df_marker_t* marker,
|
|
TRI_voc_size_t markerSize,
|
|
void const* body,
|
|
TRI_voc_size_t bodySize,
|
|
TRI_df_marker_t* result) {
|
|
int res;
|
|
|
|
res = TRI_WriteElementDatafile(journal,
|
|
result,
|
|
marker, markerSize,
|
|
body, bodySize,
|
|
false);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return res;
|
|
}
|
|
|
|
TRI_LOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
|
|
journal->_written = ((char*) result) + marker->_size;
|
|
journal->_nWritten++;
|
|
|
|
TRI_UNLOCK_JOURNAL_ENTRIES_SIM_COLLECTION(sim);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- DOCUMENT CRUD
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new header
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CreateHeader (TRI_doc_collection_t* c,
|
|
TRI_datafile_t* datafile,
|
|
TRI_df_marker_t const* m,
|
|
size_t markerSize,
|
|
TRI_doc_mptr_t* header,
|
|
void const* additional) {
|
|
TRI_doc_document_marker_t const* marker;
|
|
|
|
marker = (TRI_doc_document_marker_t const*) m;
|
|
|
|
header->_did = marker->_did;
|
|
header->_rid = marker->_rid;
|
|
header->_fid = datafile->_fid;
|
|
header->_deletion = 0;
|
|
header->_data = marker;
|
|
header->_document._sid = marker->_shape;
|
|
header->_document._data.length = marker->base._size - markerSize;
|
|
header->_document._data.data = ((char*) marker) + markerSize;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new document splitted into marker and body to file
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim,
|
|
TRI_doc_document_marker_t* marker,
|
|
size_t markerSize,
|
|
void const* body,
|
|
TRI_voc_size_t bodySize,
|
|
TRI_df_marker_t** result,
|
|
void const* additional,
|
|
bool release) {
|
|
|
|
TRI_datafile_t* journal;
|
|
TRI_doc_mptr_t* header;
|
|
TRI_doc_mptr_t mptr;
|
|
TRI_voc_size_t total;
|
|
TRI_doc_datafile_info_t* dfi;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// create header
|
|
// .............................................................................
|
|
|
|
// get a new header pointer
|
|
header = sim->_headers->request(sim->_headers);
|
|
|
|
// generate a new tick
|
|
marker->_rid = marker->_did = marker->base._tick = TRI_NewTickVocBase();
|
|
|
|
// find and select a journal
|
|
total = markerSize + bodySize;
|
|
journal = SelectJournal(sim, total, result);
|
|
|
|
if (journal == NULL) {
|
|
if (release) {
|
|
sim->base.endWrite(&sim->base);
|
|
}
|
|
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
// .............................................................................
|
|
// write document blob
|
|
// .............................................................................
|
|
|
|
// verify the header pointer
|
|
header = sim->_headers->verify(sim->_headers, header);
|
|
|
|
// generate crc
|
|
TRI_FillCrcMarkerDatafile(&marker->base, markerSize, body, bodySize);
|
|
|
|
// and write marker and blob
|
|
res = WriteElement(sim, journal, &marker->base, markerSize, body, bodySize, *result);
|
|
|
|
// .............................................................................
|
|
// update indexes
|
|
// .............................................................................
|
|
|
|
// generate create header
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
|
|
// fill the header
|
|
sim->base.createHeader(&sim->base, journal, *result, markerSize, header, 0);
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&sim->base, journal->_fid);
|
|
|
|
dfi->_numberAlive += 1;
|
|
dfi->_sizeAlive += header->_document._data.length;
|
|
|
|
// update immediate indexes
|
|
res = CreateImmediateIndexes(sim, header);
|
|
|
|
// check for constraint error, rollback if necessary
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
int resRollback;
|
|
|
|
LOG_DEBUG("encountered index violation during create, deleting newly created document");
|
|
|
|
// rollback, ignore any additional errors
|
|
resRollback = DeleteShapedJson(&sim->base, header->_did, header->_rid, 0, TRI_DOC_UPDATE_LAST_WRITE, false);
|
|
|
|
if (resRollback != TRI_ERROR_NO_ERROR) {
|
|
LOG_ERROR("encountered error '%s' during rollback of create", TRI_last_error());
|
|
}
|
|
|
|
TRI_set_errno(res);
|
|
}
|
|
|
|
// .............................................................................
|
|
// create result
|
|
// .............................................................................
|
|
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
mptr = *header;
|
|
|
|
// check cap constraint
|
|
if (sim->base._capConstraint != NULL) {
|
|
while (sim->base._capConstraint->_size < sim->base._capConstraint->_array._array._nrUsed) {
|
|
TRI_doc_mptr_t const* oldest;
|
|
int remRes;
|
|
|
|
oldest = TRI_PopFrontLinkedArray(&sim->base._capConstraint->_array);
|
|
|
|
if (oldest == NULL) {
|
|
LOG_WARNING("cap collection is empty, but collection '%ld' contains elements",
|
|
(unsigned long) sim->base.base._cid);
|
|
break;
|
|
}
|
|
|
|
LOG_DEBUG("removing document '%lu' because of cap constraint",
|
|
(unsigned long) oldest->_did);
|
|
|
|
remRes = DeleteShapedJson(&sim->base, oldest->_did, 0, NULL, TRI_DOC_UPDATE_LAST_WRITE, false);
|
|
|
|
if (remRes != TRI_ERROR_NO_ERROR) {
|
|
LOG_WARNING("cannot cap collection: %s", TRI_last_error());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// release lock, header might be invalid after this
|
|
if (release) {
|
|
sim->base.endWrite(&sim->base);
|
|
}
|
|
|
|
// wait for sync
|
|
WaitSync(sim, journal, ((char const*) *result) + markerSize + bodySize);
|
|
|
|
// and return
|
|
return mptr;
|
|
}
|
|
else {
|
|
if (release) {
|
|
sim->base.endWrite(&sim->base);
|
|
}
|
|
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
}
|
|
else {
|
|
if (release) {
|
|
sim->base.endWrite(&sim->base);
|
|
}
|
|
|
|
LOG_ERROR("cannot write element: %s", TRI_last_error());
|
|
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates an existing header
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void UpdateHeader (TRI_doc_collection_t* c,
|
|
TRI_datafile_t* datafile,
|
|
TRI_df_marker_t const* m,
|
|
size_t markerSize,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_doc_mptr_t* update) {
|
|
TRI_doc_document_marker_t const* marker;
|
|
|
|
marker = (TRI_doc_document_marker_t const*) m;
|
|
*update = *header;
|
|
|
|
update->_rid = marker->_rid;
|
|
update->_fid = datafile->_fid;
|
|
update->_data = marker;
|
|
update->_document._sid = marker->_shape;
|
|
update->_document._data.length = marker->base._size - markerSize;
|
|
update->_document._data.data = ((char*) marker) + markerSize;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief roll backs an update
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t RollbackUpdate (TRI_sim_collection_t* sim,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_df_marker_t const* originalMarker,
|
|
TRI_df_marker_t** result) {
|
|
TRI_doc_document_marker_t* marker;
|
|
TRI_doc_document_marker_t documentUpdate;
|
|
TRI_doc_edge_marker_t edgeUpdate;
|
|
char* data;
|
|
size_t dataLength;
|
|
size_t markerLength;
|
|
|
|
if (originalMarker->_type == TRI_DOC_MARKER_DOCUMENT) {
|
|
memcpy(&documentUpdate, originalMarker, sizeof(TRI_doc_document_marker_t));
|
|
marker = &documentUpdate;
|
|
markerLength = sizeof(TRI_doc_document_marker_t);
|
|
data = ((char*) originalMarker) + sizeof(TRI_doc_document_marker_t);
|
|
dataLength = originalMarker->_size - sizeof(TRI_doc_document_marker_t);
|
|
}
|
|
else if (originalMarker->_type == TRI_DOC_MARKER_EDGE) {
|
|
memcpy(&edgeUpdate, originalMarker, sizeof(TRI_doc_document_marker_t));
|
|
marker = &edgeUpdate.base;
|
|
markerLength = sizeof(TRI_doc_edge_marker_t);
|
|
data = ((char*) originalMarker) + sizeof(TRI_doc_edge_marker_t);
|
|
dataLength = originalMarker->_size - sizeof(TRI_doc_edge_marker_t);
|
|
}
|
|
else {
|
|
TRI_doc_mptr_t mptr;
|
|
TRI_set_errno(TRI_ERROR_INTERNAL);
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
return UpdateDocument(sim,
|
|
header,
|
|
marker, markerLength,
|
|
data, dataLength,
|
|
header->_rid,
|
|
NULL,
|
|
TRI_DOC_UPDATE_LAST_WRITE,
|
|
result,
|
|
false,
|
|
false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates an existing document splitted into marker and body to file
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_doc_document_marker_t* marker,
|
|
size_t markerSize,
|
|
void const* body,
|
|
TRI_voc_size_t bodySize,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
TRI_df_marker_t** result,
|
|
bool release,
|
|
bool allowRollback) {
|
|
TRI_doc_mptr_t mptr;
|
|
TRI_datafile_t* journal;
|
|
TRI_df_marker_t const* originalMarker;
|
|
TRI_doc_mptr_t resUpd;
|
|
TRI_voc_size_t total;
|
|
int res;
|
|
|
|
originalMarker = header->_data;
|
|
|
|
// .............................................................................
|
|
// check the revision
|
|
// .............................................................................
|
|
|
|
if (oldRid != NULL) {
|
|
*oldRid = header->_rid;
|
|
}
|
|
|
|
switch (policy) {
|
|
case TRI_DOC_UPDATE_ERROR:
|
|
if (rid != 0) {
|
|
if (rid != header->_rid) {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
TRI_set_errno(TRI_ERROR_AVOCADO_CONFLICT);
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TRI_DOC_UPDATE_LAST_WRITE:
|
|
break;
|
|
|
|
case TRI_DOC_UPDATE_CONFLICT:
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
TRI_set_errno(TRI_ERROR_NOT_IMPLEMENTED);
|
|
mptr._did = 0;
|
|
return mptr;
|
|
|
|
case TRI_DOC_UPDATE_ILLEGAL:
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
TRI_set_errno(TRI_ERROR_INTERNAL);
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
// .............................................................................
|
|
// update header
|
|
// .............................................................................
|
|
|
|
// generate a new tick
|
|
marker->_rid = marker->base._tick = TRI_NewTickVocBase();
|
|
|
|
// find and select a journal
|
|
total = markerSize + bodySize;
|
|
journal = SelectJournal(collection, total, result);
|
|
|
|
if (journal == NULL) {
|
|
collection->base.base._lastError = TRI_set_errno(TRI_ERROR_AVOCADO_NO_JOURNAL);
|
|
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
// .............................................................................
|
|
// write document blob
|
|
// .............................................................................
|
|
|
|
// generate crc
|
|
TRI_FillCrcMarkerDatafile(&marker->base, markerSize, body, bodySize);
|
|
|
|
// and write marker and blob
|
|
res = WriteElement(collection, journal, &marker->base, markerSize, body, bodySize, *result);
|
|
|
|
// .............................................................................
|
|
// update indexes
|
|
// .............................................................................
|
|
|
|
// update the header
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
TRI_doc_mptr_t update;
|
|
TRI_doc_datafile_info_t* dfi;
|
|
|
|
// update the header
|
|
collection->base.updateHeader(&collection->base, journal, *result, markerSize, header, &update);
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, header->_fid);
|
|
|
|
dfi->_numberAlive -= 1;
|
|
dfi->_sizeAlive -= header->_document._data.length;
|
|
|
|
dfi->_numberDead += 1;
|
|
dfi->_sizeDead += header->_document._data.length;
|
|
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, journal->_fid);
|
|
|
|
dfi->_numberAlive += 1;
|
|
dfi->_sizeAlive += update._document._data.length;
|
|
|
|
// update immediate indexes
|
|
res = UpdateImmediateIndexes(collection, header, &update);
|
|
|
|
// check for constraint error
|
|
if (allowRollback && res != TRI_ERROR_NO_ERROR) {
|
|
LOG_DEBUG("encountered index violating during update, rolling back");
|
|
|
|
resUpd = RollbackUpdate(collection, header, originalMarker, result);
|
|
|
|
if (resUpd._did == 0) {
|
|
LOG_ERROR("encountered error '%s' during rollback of update", TRI_last_error());
|
|
}
|
|
|
|
TRI_set_errno(res);
|
|
}
|
|
|
|
// .............................................................................
|
|
// create result
|
|
// .............................................................................
|
|
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
mptr = *header;
|
|
|
|
// release lock, header might be invalid after this
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
// wait for sync
|
|
WaitSync(collection, journal, ((char const*) *result) + markerSize + bodySize);
|
|
|
|
// and return
|
|
return mptr;
|
|
}
|
|
else {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
}
|
|
else {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
LOG_ERROR("cannot write element");
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief deletes an element and removes it from the index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int DeleteDocument (TRI_sim_collection_t* collection,
|
|
TRI_doc_deletion_marker_t* marker,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
bool release) {
|
|
TRI_datafile_t* journal;
|
|
TRI_df_marker_t* result;
|
|
TRI_doc_mptr_t const* header;
|
|
TRI_voc_size_t total;
|
|
int res;
|
|
|
|
// get an existing header pointer
|
|
header = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &marker->_did);
|
|
|
|
if (header == NULL || header->_deletion != 0) {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_DOCUMENT_NOT_FOUND);
|
|
}
|
|
|
|
// check the revision
|
|
if (oldRid != NULL) {
|
|
*oldRid = header->_rid;
|
|
}
|
|
|
|
switch (policy) {
|
|
case TRI_DOC_UPDATE_ERROR:
|
|
if (rid != 0) {
|
|
if (rid != header->_rid) {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_CONFLICT);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TRI_DOC_UPDATE_LAST_WRITE:
|
|
break;
|
|
|
|
case TRI_DOC_UPDATE_CONFLICT:
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
return TRI_set_errno(TRI_ERROR_NOT_IMPLEMENTED);
|
|
|
|
case TRI_DOC_UPDATE_ILLEGAL:
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
return TRI_set_errno(TRI_ERROR_INTERNAL);
|
|
}
|
|
|
|
// generate a new tick
|
|
marker->base._tick = TRI_NewTickVocBase();
|
|
|
|
// find and select a journal
|
|
total = sizeof(TRI_doc_deletion_marker_t);
|
|
journal = SelectJournal(collection, total, &result);
|
|
|
|
if (journal == NULL) {
|
|
collection->base.base._lastError = TRI_set_errno(TRI_ERROR_AVOCADO_NO_JOURNAL);
|
|
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
return TRI_ERROR_AVOCADO_NO_JOURNAL;
|
|
}
|
|
|
|
// generate crc
|
|
TRI_FillCrcMarkerDatafile(&marker->base, sizeof(TRI_doc_deletion_marker_t), 0, 0);
|
|
|
|
// and write marker and blob
|
|
res = WriteElement(collection, journal, &marker->base, sizeof(TRI_doc_deletion_marker_t), 0, 0, result);
|
|
|
|
// update the header
|
|
if (res == TRI_ERROR_NO_ERROR) {
|
|
TRI_doc_datafile_info_t* dfi;
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, header->_fid);
|
|
|
|
dfi->_numberAlive -= 1;
|
|
dfi->_sizeAlive -= header->_document._data.length;
|
|
|
|
dfi->_numberDead += 1;
|
|
dfi->_sizeDead += header->_document._data.length;
|
|
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, journal->_fid);
|
|
|
|
dfi->_numberDeletion += 1;
|
|
|
|
// update immediate indexes
|
|
DeleteImmediateIndexes(collection, header, marker->base._tick);
|
|
|
|
// release lock
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
// wait for sync
|
|
WaitSync(collection, journal, ((char const*) result) + sizeof(TRI_doc_deletion_marker_t));
|
|
}
|
|
else {
|
|
if (release) {
|
|
collection->base.endWrite(&collection->base);
|
|
}
|
|
|
|
LOG_ERROR("cannot delete element");
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- DOCUMENT COLLECTION
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief debug output for datafile information
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DebugDatafileInfoDatafile (TRI_doc_collection_t* collection,
|
|
TRI_datafile_t* datafile) {
|
|
TRI_doc_datafile_info_t* dfi;
|
|
|
|
dfi = TRI_FindDatafileInfoDocCollection(collection, datafile->_fid);
|
|
|
|
printf("DATAFILE '%s'\n", datafile->_filename);
|
|
|
|
if (dfi == NULL) {
|
|
printf(" no info\n\n");
|
|
return;
|
|
}
|
|
|
|
printf(" number alive: %ld\n", (long) dfi->_numberAlive);
|
|
printf(" size alive: %ld\n", (long) dfi->_sizeAlive);
|
|
printf(" number dead: %ld\n", (long) dfi->_numberDead);
|
|
printf(" size dead: %ld\n", (long) dfi->_sizeDead);
|
|
printf(" deletion: %ld\n\n", ( long) dfi->_numberDeletion);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief debug output for datafile information
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DebugDatafileInfoDocCollection (TRI_doc_collection_t* collection) {
|
|
TRI_datafile_t* datafile;
|
|
size_t n;
|
|
size_t i;
|
|
|
|
// journals
|
|
n = collection->base._journals._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
datafile = collection->base._journals._buffer[i];
|
|
DebugDatafileInfoDatafile(collection, datafile);
|
|
}
|
|
|
|
// compactor journals
|
|
n = collection->base._compactors._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
datafile = collection->base._compactors._buffer[i];
|
|
DebugDatafileInfoDatafile(collection, datafile);
|
|
}
|
|
|
|
// datafiles
|
|
n = collection->base._datafiles._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
datafile = collection->base._datafiles._buffer[i];
|
|
DebugDatafileInfoDatafile(collection, datafile);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief debug output for datafile information
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DebugHeaderSimCollection (TRI_sim_collection_t* collection) {
|
|
void** end;
|
|
void** ptr;
|
|
|
|
// update index
|
|
ptr = collection->_primaryIndex._table;
|
|
end = collection->_primaryIndex._table + collection->_primaryIndex._nrAlloc;
|
|
|
|
for (; ptr < end; ++ptr) {
|
|
if (*ptr) {
|
|
TRI_doc_mptr_t* d;
|
|
|
|
d = *ptr;
|
|
|
|
printf("fid %lu, did %lu, rid %lu, eid %lu, del %lu\n",
|
|
(unsigned long) d->_fid,
|
|
(unsigned long) d->_did,
|
|
(unsigned long) d->_rid,
|
|
(unsigned long) d->_eid,
|
|
(unsigned long) d->_deletion);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new document in the collection from shaped json
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t CreateShapedJson (TRI_doc_collection_t* document,
|
|
TRI_df_marker_type_e type,
|
|
TRI_shaped_json_t const* json,
|
|
void const* data,
|
|
bool release) {
|
|
TRI_df_marker_t* result;
|
|
TRI_sim_collection_t* collection;
|
|
|
|
collection = (TRI_sim_collection_t*) document;
|
|
|
|
if (type == TRI_DOC_MARKER_DOCUMENT) {
|
|
TRI_doc_document_marker_t marker;
|
|
|
|
memset(&marker, 0, sizeof(marker));
|
|
|
|
marker.base._size = sizeof(marker) + json->_data.length;
|
|
marker.base._type = type;
|
|
|
|
marker._sid = 0;
|
|
marker._shape = json->_sid;
|
|
|
|
return CreateDocument(collection,
|
|
&marker, sizeof(marker),
|
|
json->_data.data, json->_data.length,
|
|
&result,
|
|
data,
|
|
release);
|
|
}
|
|
else if (type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_doc_edge_marker_t marker;
|
|
TRI_sim_edge_t const* edge;
|
|
|
|
edge = data;
|
|
|
|
memset(&marker, 0, sizeof(marker));
|
|
|
|
marker.base.base._size = sizeof(marker) + json->_data.length;
|
|
marker.base.base._type = type;
|
|
|
|
marker.base._sid = 0;
|
|
marker.base._shape = json->_sid;
|
|
|
|
marker._fromCid = edge->_fromCid;
|
|
marker._fromDid = edge->_fromDid;
|
|
marker._toCid = edge->_toCid;
|
|
marker._toDid = edge->_toDid;
|
|
|
|
return CreateDocument(collection,
|
|
&marker.base, sizeof(marker),
|
|
json->_data.data, json->_data.length,
|
|
&result,
|
|
data,
|
|
release);
|
|
}
|
|
else {
|
|
LOG_FATAL("unknown marker type %lu", (unsigned long) type);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief reads an element from the document collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t ReadShapedJson (TRI_doc_collection_t* document,
|
|
TRI_voc_did_t did) {
|
|
TRI_sim_collection_t* collection;
|
|
TRI_doc_mptr_t result;
|
|
TRI_doc_mptr_t const* header;
|
|
|
|
collection = (TRI_sim_collection_t*) document;
|
|
|
|
header = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &did);
|
|
|
|
if (header == NULL || header->_deletion != 0) {
|
|
result._did = 0;
|
|
return result;
|
|
}
|
|
else {
|
|
return *header;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates a document in the collection from shaped json
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_doc_mptr_t UpdateShapedJson (TRI_doc_collection_t* document,
|
|
TRI_shaped_json_t const* json,
|
|
TRI_voc_did_t did,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
bool release) {
|
|
TRI_df_marker_t const* original;
|
|
TRI_df_marker_t* result;
|
|
TRI_doc_mptr_t mptr;
|
|
TRI_doc_mptr_t const* header;
|
|
TRI_sim_collection_t* collection;
|
|
|
|
collection = (TRI_sim_collection_t*) document;
|
|
|
|
// get an existing header pointer
|
|
header = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &did);
|
|
|
|
if (header == NULL || header->_deletion != 0) {
|
|
if (release) {
|
|
document->endWrite(&collection->base);
|
|
}
|
|
|
|
TRI_set_errno(TRI_ERROR_AVOCADO_DOCUMENT_NOT_FOUND);
|
|
mptr._did = 0;
|
|
return mptr;
|
|
}
|
|
|
|
original = header->_data;
|
|
|
|
// the original is an document
|
|
if (original->_type == TRI_DOC_MARKER_DOCUMENT) {
|
|
TRI_doc_document_marker_t marker;
|
|
|
|
// create an update
|
|
memset(&marker, 0, sizeof(marker));
|
|
|
|
marker.base._size = sizeof(marker) + json->_data.length;
|
|
marker.base._type = original->_type;
|
|
|
|
marker._did = did;
|
|
marker._sid = 0;
|
|
marker._shape = json->_sid;
|
|
|
|
return UpdateDocument(collection,
|
|
header,
|
|
&marker, sizeof(marker),
|
|
json->_data.data, json->_data.length,
|
|
rid,
|
|
oldRid,
|
|
policy,
|
|
&result,
|
|
release,
|
|
true);
|
|
}
|
|
|
|
// the original is an edge
|
|
else if (original->_type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_doc_edge_marker_t marker;
|
|
TRI_doc_edge_marker_t const* originalEdge;
|
|
|
|
originalEdge = header->_data;
|
|
|
|
// create an update
|
|
memset(&marker, 0, sizeof(marker));
|
|
|
|
marker.base.base._size = sizeof(marker) + json->_data.length;
|
|
marker.base.base._type = original->_type;
|
|
|
|
marker.base._did = did;
|
|
marker.base._sid = 0;
|
|
marker.base._shape = json->_sid;
|
|
|
|
marker._fromCid = originalEdge->_fromCid;
|
|
marker._fromDid = originalEdge->_fromDid;
|
|
marker._toCid = originalEdge->_toCid;
|
|
marker._toDid = originalEdge->_toDid;
|
|
|
|
return UpdateDocument(collection,
|
|
header,
|
|
&marker.base, sizeof(marker),
|
|
json->_data.data, json->_data.length,
|
|
rid,
|
|
oldRid,
|
|
policy,
|
|
&result,
|
|
release,
|
|
true);
|
|
}
|
|
|
|
// do not know
|
|
else {
|
|
if (release) {
|
|
document->endWrite(&collection->base);
|
|
}
|
|
|
|
LOG_FATAL("unknown marker type %lu", (unsigned long) original->_type);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief deletes a json document given the identifier
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int DeleteShapedJson (TRI_doc_collection_t* doc,
|
|
TRI_voc_did_t did,
|
|
TRI_voc_rid_t rid,
|
|
TRI_voc_rid_t* oldRid,
|
|
TRI_doc_update_policy_e policy,
|
|
bool release) {
|
|
TRI_sim_collection_t* sim;
|
|
TRI_doc_deletion_marker_t marker;
|
|
|
|
sim = (TRI_sim_collection_t*) doc;
|
|
|
|
memset(&marker, 0, sizeof(marker));
|
|
|
|
marker.base._size = sizeof(marker);
|
|
marker.base._type = TRI_DOC_MARKER_DELETION;
|
|
|
|
marker._did = did;
|
|
marker._sid = 0;
|
|
|
|
return DeleteDocument(sim, &marker, rid, oldRid, policy, release);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief read locks a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int BeginRead (TRI_doc_collection_t* doc) {
|
|
TRI_sim_collection_t* sim;
|
|
|
|
sim = (TRI_sim_collection_t*) doc;
|
|
TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief read unlocks a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int EndRead (TRI_doc_collection_t* doc) {
|
|
TRI_sim_collection_t* sim;
|
|
|
|
sim = (TRI_sim_collection_t*) doc;
|
|
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief write locks a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int BeginWrite (TRI_doc_collection_t* doc) {
|
|
TRI_sim_collection_t* sim;
|
|
|
|
sim = (TRI_sim_collection_t*) doc;
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief write unlocks a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int EndWrite (TRI_doc_collection_t* document) {
|
|
TRI_sim_collection_t* sim;
|
|
|
|
sim = (TRI_sim_collection_t*) document;
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief size of a document collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_voc_size_t SizeSimCollection (TRI_doc_collection_t* doc) {
|
|
TRI_doc_mptr_t const* mptr;
|
|
TRI_sim_collection_t* sim;
|
|
TRI_voc_size_t result;
|
|
void** end;
|
|
void** ptr;
|
|
|
|
sim = (TRI_sim_collection_t*) doc;
|
|
|
|
TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
ptr = sim->_primaryIndex._table;
|
|
end = sim->_primaryIndex._table + sim->_primaryIndex._nrAlloc;
|
|
result = 0;
|
|
|
|
for (; ptr < end; ++ptr) {
|
|
if (*ptr != NULL) {
|
|
mptr = *ptr;
|
|
|
|
if (mptr->_deletion == 0) {
|
|
++result;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- SIMPLE COLLECTION
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief iterator for open
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool OpenIterator (TRI_df_marker_t const* marker, void* data, TRI_datafile_t* datafile, bool journal) {
|
|
TRI_sim_collection_t* collection = data;
|
|
TRI_doc_mptr_t const* found;
|
|
TRI_doc_datafile_info_t* dfi;
|
|
|
|
// new or updated document
|
|
if (marker->_type == TRI_DOC_MARKER_DOCUMENT || marker->_type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_doc_document_marker_t const* d = (TRI_doc_document_marker_t const*) marker;
|
|
size_t markerSize;
|
|
|
|
if (marker->_type == TRI_DOC_MARKER_DOCUMENT) {
|
|
LOG_TRACE("document: fid %lu, did %lu, rid %lu",
|
|
(unsigned long) datafile->_fid,
|
|
(unsigned long) d->_did,
|
|
(unsigned long) d->_rid);
|
|
|
|
markerSize = sizeof(TRI_doc_document_marker_t);
|
|
}
|
|
else if (marker->_type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_doc_edge_marker_t const* e = (TRI_doc_edge_marker_t const*) marker;
|
|
|
|
LOG_TRACE("edge: fid %lu, did %lu, rid %lu, edge (%lu,%lu) -> (%lu,%lu)",
|
|
(unsigned long) datafile->_fid,
|
|
(unsigned long) d->_did,
|
|
(unsigned long) d->_rid,
|
|
(unsigned long) e->_fromCid,
|
|
(unsigned long) e->_fromDid,
|
|
(unsigned long) e->_toCid,
|
|
(unsigned long) e->_toDid);
|
|
|
|
markerSize = sizeof(TRI_doc_edge_marker_t);
|
|
}
|
|
else {
|
|
LOG_FATAL("unknown marker type %lu", (unsigned long) marker->_type);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
found = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &d->_did);
|
|
|
|
// it is a new entry
|
|
if (found == NULL) {
|
|
TRI_doc_mptr_t* header;
|
|
|
|
header = collection->_headers->request(collection->_headers);
|
|
header = collection->_headers->verify(collection->_headers, header);
|
|
|
|
// fill the header
|
|
collection->base.createHeader(&collection->base, datafile, marker, markerSize, header, 0);
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, datafile->_fid);
|
|
|
|
dfi->_numberAlive += 1;
|
|
dfi->_sizeAlive += header->_document._data.length;
|
|
|
|
// update immediate indexes
|
|
CreateImmediateIndexes(collection, header);
|
|
}
|
|
|
|
// it is an delete
|
|
else if (found->_deletion != 0) {
|
|
LOG_TRACE("skipping already deleted document: %lu", (unsigned long) d->_did);
|
|
}
|
|
|
|
// it is an update, but only if found has a smaller revision identifier
|
|
else if (found->_rid < d->_rid || (found->_rid == d->_rid && found->_fid <= datafile->_fid)) {
|
|
TRI_doc_mptr_t update;
|
|
|
|
// update the header info
|
|
collection->base.updateHeader(&collection->base, datafile, marker, markerSize, found, &update);
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, found->_fid);
|
|
|
|
dfi->_numberAlive -= 1;
|
|
dfi->_sizeAlive -= found->_document._data.length;
|
|
|
|
dfi->_numberDead += 1;
|
|
dfi->_sizeDead += found->_document._data.length;
|
|
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, datafile->_fid);
|
|
|
|
dfi->_numberAlive += 1;
|
|
dfi->_sizeAlive += update._document._data.length;
|
|
|
|
// update immediate indexes
|
|
UpdateImmediateIndexes(collection, found, &update);
|
|
}
|
|
|
|
// it is a stale update
|
|
else {
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, datafile->_fid);
|
|
|
|
dfi->_numberDead += 1;
|
|
dfi->_sizeDead += found->_document._data.length;
|
|
}
|
|
}
|
|
|
|
// deletion
|
|
else if (marker->_type == TRI_DOC_MARKER_DELETION) {
|
|
TRI_doc_deletion_marker_t const* d;
|
|
|
|
d = (TRI_doc_deletion_marker_t const*) marker;
|
|
|
|
LOG_TRACE("deletion: fid %lu, did %lu, rid %lu, deletion %lu",
|
|
(unsigned long) datafile->_fid,
|
|
(unsigned long) d->_did,
|
|
(unsigned long) d->_rid,
|
|
(unsigned long) marker->_tick);
|
|
|
|
found = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &d->_did);
|
|
|
|
// it is a new entry, so we missed the create
|
|
if (found == NULL) {
|
|
TRI_doc_mptr_t* header;
|
|
|
|
header = collection->_headers->request(collection->_headers);
|
|
header = collection->_headers->verify(collection->_headers, header);
|
|
|
|
header->_did = d->_did;
|
|
header->_rid = d->_rid;
|
|
header->_deletion = marker->_tick;
|
|
header->_data = 0;
|
|
header->_document._data.length = 0;
|
|
header->_document._data.data = 0;
|
|
|
|
// update immediate indexes
|
|
CreateImmediateIndexes(collection, header);
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, datafile->_fid);
|
|
|
|
dfi->_numberDeletion += 1;
|
|
}
|
|
|
|
// it is a real delete
|
|
else if (found->_deletion == 0) {
|
|
union { TRI_doc_mptr_t const* c; TRI_doc_mptr_t* v; } change;
|
|
|
|
// mark element as deleted
|
|
change.c = found;
|
|
change.v->_deletion = marker->_tick;
|
|
|
|
// update the datafile info
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, found->_fid);
|
|
|
|
dfi->_numberAlive -= 1;
|
|
dfi->_sizeAlive -= found->_document._data.length;
|
|
|
|
dfi->_numberDead += 1;
|
|
dfi->_sizeDead += found->_document._data.length;
|
|
|
|
dfi = TRI_FindDatafileInfoDocCollection(&collection->base, datafile->_fid);
|
|
|
|
dfi->_numberDeletion += 1;
|
|
}
|
|
|
|
// it is a double delete
|
|
else {
|
|
LOG_TRACE("skipping deletion of already deleted document: %lu", (unsigned long) d->_did);
|
|
}
|
|
}
|
|
else {
|
|
LOG_TRACE("skipping marker %lu", (unsigned long) marker->_type);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief iterator for index open
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool OpenIndexIterator (char const* filename, void* data) {
|
|
TRI_idx_iid_t iid;
|
|
TRI_index_t* idx;
|
|
TRI_json_t* fieldStr;
|
|
TRI_json_t* fld;
|
|
TRI_json_t* bv;
|
|
TRI_json_t* iis;
|
|
TRI_json_t* json;
|
|
TRI_json_t* type;
|
|
TRI_sim_collection_t* doc;
|
|
TRI_vector_t attributes;
|
|
bool uniqueIndex;
|
|
char const* typeStr;
|
|
char* error;
|
|
int fieldCount;
|
|
int j;
|
|
|
|
// load json description of the index
|
|
json = TRI_JsonFile(TRI_UNKNOWN_MEM_ZONE, filename, &error);
|
|
|
|
// simple collection of the index
|
|
doc = (TRI_sim_collection_t*) data;
|
|
|
|
// json must be a index description
|
|
if (json == NULL) {
|
|
LOG_ERROR("cannot read index definition from '%s': %s", filename, error);
|
|
return false;
|
|
}
|
|
|
|
if (json->_type != TRI_JSON_ARRAY) {
|
|
LOG_ERROR("cannot read index definition from '%s': expecting an array", filename);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
// extract the type
|
|
type = TRI_LookupArrayJson(json, "type");
|
|
|
|
if (type->_type != TRI_JSON_STRING) {
|
|
LOG_ERROR("cannot read index definition from '%s': expecting a string for type", filename);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
typeStr = type->_value._string.data;
|
|
|
|
// extract the index identifier
|
|
iis = TRI_LookupArrayJson(json, "id");
|
|
|
|
if (iis != NULL && iis->_type == TRI_JSON_NUMBER) {
|
|
iid = iis->_value._number;
|
|
TRI_UpdateTickVocBase(iid);
|
|
}
|
|
else {
|
|
LOG_ERROR("ignoring index, index identifier could not be located");
|
|
return false;
|
|
}
|
|
|
|
// extract the fields
|
|
if (TRI_EqualString(typeStr, "cap")) {
|
|
fieldCount = 0;
|
|
fld = NULL;
|
|
}
|
|
else {
|
|
fld = TRI_LookupArrayJson(json, "fields");
|
|
|
|
if (fld == NULL || fld->_type != TRI_JSON_LIST) {
|
|
LOG_ERROR("ignoring %s-index %lu, 'fields' must be a list",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
fieldCount = fld->_value._objects._length;
|
|
|
|
for (j = 0; j < fieldCount; ++j) {
|
|
TRI_json_t* sub = TRI_AtVector(&fld->_value._objects, j);
|
|
|
|
if (sub->_type != TRI_JSON_STRING) {
|
|
LOG_ERROR("ignoring %s-index %lu, 'fields' must be a list of attribute paths",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ...........................................................................
|
|
// CAP CONSTRAINT
|
|
// ...........................................................................
|
|
|
|
if (TRI_EqualString(typeStr, "cap")) {
|
|
TRI_json_t* num;
|
|
size_t size;
|
|
|
|
num = TRI_LookupArrayJson(json, "size");
|
|
|
|
if (num == NULL || num->_type != TRI_JSON_NUMBER) {
|
|
LOG_ERROR("ignoring cap constraint %lu, 'size' missing", (unsigned long) iid);
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
if (num->_value._number < 1.0) {
|
|
LOG_ERROR("ignoring cap constraint %lu, 'size' %f must be at least 1", (unsigned long) iid, num->_value._number);
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
size = (size_t) num->_value._number;
|
|
|
|
CreateCapConstraintSimCollection(doc, size, iid, NULL);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return true;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// GEO INDEX (list or attribute)
|
|
// ...........................................................................
|
|
|
|
else if (TRI_EqualString(typeStr, "geo1") || TRI_EqualString(typeStr, "geo2")) {
|
|
bool constraint;
|
|
bool ignoreNull;
|
|
|
|
bv = TRI_LookupArrayJson(json, "constraint");
|
|
constraint = false;
|
|
|
|
if (bv != NULL && bv->_type == TRI_JSON_BOOLEAN) {
|
|
constraint = bv->_value._boolean;
|
|
}
|
|
|
|
bv = TRI_LookupArrayJson(json, "ignoreNull");
|
|
ignoreNull = false;
|
|
|
|
if (bv != NULL && bv->_type == TRI_JSON_BOOLEAN) {
|
|
ignoreNull = bv->_value._boolean;
|
|
}
|
|
|
|
if (TRI_EqualString(typeStr, "geo1")) {
|
|
bool geoJson;
|
|
|
|
bv = TRI_LookupArrayJson(json, "geoJson");
|
|
geoJson = false;
|
|
|
|
if (bv != NULL && bv->_type == TRI_JSON_BOOLEAN) {
|
|
geoJson = bv->_value._boolean;
|
|
}
|
|
|
|
if (fieldCount == 1) {
|
|
TRI_json_t* loc;
|
|
|
|
loc = TRI_AtVector(&fld->_value._objects, 0);
|
|
|
|
CreateGeoIndexSimCollection(doc,
|
|
loc->_value._string.data,
|
|
NULL,
|
|
NULL,
|
|
geoJson,
|
|
constraint,
|
|
ignoreNull,
|
|
iid,
|
|
NULL);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return true;
|
|
}
|
|
else {
|
|
LOG_ERROR("ignoring %s-index %lu, 'fields' must be a list with 1 entries",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
else if (TRI_EqualString(typeStr, "geo2")) {
|
|
if (fieldCount == 2) {
|
|
TRI_json_t* lat;
|
|
TRI_json_t* lon;
|
|
|
|
lat = TRI_AtVector(&fld->_value._objects, 0);
|
|
lon = TRI_AtVector(&fld->_value._objects, 1);
|
|
|
|
CreateGeoIndexSimCollection(doc,
|
|
NULL,
|
|
lat->_value._string.data,
|
|
lon->_value._string.data,
|
|
false,
|
|
constraint,
|
|
ignoreNull,
|
|
iid,
|
|
NULL);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return true;
|
|
}
|
|
else {
|
|
LOG_ERROR("ignoring %s-index %lu, 'fields' must be a list with 2 entries",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// ...........................................................................
|
|
// HASH INDEX OR SKIPLIST INDEX
|
|
// ...........................................................................
|
|
|
|
else if ( TRI_EqualString(typeStr, "hash")
|
|
|| TRI_EqualString(typeStr, "skiplist")
|
|
|| TRI_EqualString(typeStr, "priorityqueue")) {
|
|
|
|
// Determine if the hash index is unique or non-unique
|
|
bv = TRI_LookupArrayJson(json, "unique");
|
|
uniqueIndex = false;
|
|
|
|
if (bv != NULL && bv->_type == TRI_JSON_BOOLEAN) {
|
|
uniqueIndex = bv->_value._boolean;
|
|
}
|
|
else {
|
|
LOG_ERROR("ignoring %s-index %lu, could not determine if unique or non-unique",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
// extract the list of fields
|
|
if (fieldCount < 1) {
|
|
LOG_ERROR("ignoring %s-index %lu, need at least von attribute path",
|
|
typeStr, (unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
|
|
// Initialise the vector in which we store the fields on which the hashing
|
|
// will be based.
|
|
TRI_InitVector(&attributes, TRI_UNKNOWN_MEM_ZONE, sizeof(char*)); // TODO FIXME use VectorPointer
|
|
|
|
// find fields
|
|
for (j = 0; j < fieldCount; ++j) {
|
|
fieldStr = TRI_AtVector(&fld->_value._objects, j);
|
|
|
|
TRI_PushBackVector(&attributes, &(fieldStr->_value._string.data));
|
|
}
|
|
|
|
// create the index
|
|
if (TRI_EqualString(typeStr, "hash")) {
|
|
idx = CreateHashIndexSimCollection(doc, &attributes, iid, uniqueIndex, NULL);
|
|
}
|
|
else if (TRI_EqualString(typeStr, "priorityqueue")) {
|
|
idx = CreatePriorityQueueIndexSimCollection(doc, &attributes, iid, uniqueIndex, NULL);
|
|
}
|
|
else if (TRI_EqualString(typeStr, "skiplist")) {
|
|
idx = CreateSkiplistIndexSimCollection(doc, &attributes, iid, uniqueIndex, NULL);
|
|
}
|
|
else {
|
|
LOG_ERROR("internal error for hash type '%s'", typeStr);
|
|
idx = NULL;
|
|
}
|
|
|
|
TRI_DestroyVector(&attributes);
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
|
|
if (idx == NULL) {
|
|
LOG_ERROR("cannot create hash index %lu", (unsigned long) iid);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// .........................................................................
|
|
// oops, unknown index type
|
|
// .........................................................................
|
|
|
|
else {
|
|
LOG_ERROR("ignoring unknown index type '%s' for index %lu",
|
|
typeStr,
|
|
(unsigned long) iid);
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/// @brief hashes an edge header
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static uint64_t HashElementEdge (TRI_multi_pointer_t* array, void const* data) {
|
|
TRI_edge_header_t const* h;
|
|
uint64_t hash[3];
|
|
|
|
h = data;
|
|
|
|
hash[0] = h->_direction;
|
|
hash[1] = h->_cid;
|
|
hash[2] = h->_did;
|
|
|
|
return TRI_FnvHashPointer(hash, sizeof(hash));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks if key and element match
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool IsEqualKeyEdge (TRI_multi_pointer_t* array, void const* left, void const* right) {
|
|
TRI_edge_header_t const* l;
|
|
TRI_edge_header_t const* r;
|
|
|
|
l = left;
|
|
r = right;
|
|
|
|
return l->_direction == r->_direction && l->_cid == r->_cid && l->_did == r->_did;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks for elements are equal
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool IsEqualElementEdge (TRI_multi_pointer_t* array, void const* left, void const* right) {
|
|
TRI_edge_header_t const* l;
|
|
TRI_edge_header_t const* r;
|
|
|
|
l = left;
|
|
r = right;
|
|
|
|
return l->_mptr == r->_mptr && l->_direction == r->_direction && l->_cid == r->_cid && l->_did == r->_did;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief initialises a document collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool InitSimCollection (TRI_sim_collection_t* collection,
|
|
TRI_shaper_t* shaper) {
|
|
TRI_index_t* primary;
|
|
char* id;
|
|
|
|
// create primary index
|
|
primary = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_index_t), false);
|
|
/* TODO FIXME: memory allocation might fail */
|
|
|
|
id = TRI_DuplicateString("_id");
|
|
|
|
TRI_InitDocCollection(&collection->base, shaper);
|
|
|
|
TRI_InitReadWriteLock(&collection->_lock);
|
|
|
|
collection->_headers = TRI_CreateSimpleHeaders(sizeof(TRI_doc_mptr_t));
|
|
|
|
if (collection->_headers == NULL) {
|
|
TRI_DestroyDocCollection(&collection->base);
|
|
TRI_DestroyReadWriteLock(&collection->_lock);
|
|
return false;
|
|
}
|
|
|
|
TRI_InitAssociativePointer(&collection->_primaryIndex,
|
|
TRI_UNKNOWN_MEM_ZONE,
|
|
HashKeyHeader,
|
|
HashElementDocument,
|
|
IsEqualKeyDocument,
|
|
0);
|
|
|
|
TRI_InitMultiPointer(&collection->_edgesIndex,
|
|
TRI_UNKNOWN_MEM_ZONE,
|
|
HashElementEdge,
|
|
HashElementEdge,
|
|
IsEqualKeyEdge,
|
|
IsEqualElementEdge);
|
|
|
|
TRI_InitCondition(&collection->_journalsCondition);
|
|
|
|
TRI_InitVectorPointer(&collection->_indexes, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
TRI_InitVectorString(&primary->_fields, TRI_UNKNOWN_MEM_ZONE);
|
|
TRI_PushBackVectorString(&primary->_fields, id);
|
|
|
|
primary->_iid = 0;
|
|
primary->_type = TRI_IDX_TYPE_PRIMARY_INDEX;
|
|
primary->_unique = true;
|
|
|
|
primary->insert = InsertPrimary;
|
|
primary->remove = RemovePrimary;
|
|
primary->update = UpdatePrimary;
|
|
primary->json = JsonPrimary;
|
|
|
|
TRI_PushBackVectorPointer(&collection->_indexes, primary);
|
|
|
|
// setup methods
|
|
collection->base.createHeader = CreateHeader;
|
|
collection->base.updateHeader = UpdateHeader;
|
|
|
|
collection->base.beginRead = BeginRead;
|
|
collection->base.endRead = EndRead;
|
|
|
|
collection->base.beginWrite = BeginWrite;
|
|
collection->base.endWrite = EndWrite;
|
|
|
|
collection->base.create = CreateShapedJson;
|
|
collection->base.read = ReadShapedJson;
|
|
collection->base.update = UpdateShapedJson;
|
|
collection->base.destroy = DeleteShapedJson;
|
|
|
|
collection->base.size = SizeSimCollection;
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_sim_collection_t* TRI_CreateSimCollection (TRI_vocbase_t* vocbase,
|
|
char const* path,
|
|
TRI_col_parameter_t* parameter,
|
|
TRI_voc_cid_t cid) {
|
|
TRI_col_info_t info;
|
|
TRI_collection_t* collection;
|
|
TRI_shaper_t* shaper;
|
|
TRI_sim_collection_t* doc;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info._version = TRI_COL_VERSION;
|
|
info._type = parameter->_type;
|
|
info._cid = cid == 0 ? TRI_NewTickVocBase() : cid;
|
|
TRI_CopyString(info._name, parameter->_name, sizeof(info._name));
|
|
info._waitForSync = parameter->_waitForSync;
|
|
info._maximalSize = parameter->_maximalSize;
|
|
|
|
// first create the document collection
|
|
doc = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_sim_collection_t), false);
|
|
|
|
if (doc == NULL) {
|
|
LOG_ERROR("cannot create document");
|
|
return NULL;
|
|
}
|
|
|
|
collection = TRI_CreateCollection(vocbase, &doc->base.base, path, &info);
|
|
|
|
if (collection == NULL) {
|
|
LOG_ERROR("cannot create document collection");
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, doc);
|
|
return NULL;
|
|
}
|
|
|
|
// then the shape collection
|
|
shaper = TRI_CreateVocShaper(vocbase, collection->_directory, "SHAPES");
|
|
|
|
if (shaper == NULL) {
|
|
LOG_ERROR("cannot create shapes collection");
|
|
|
|
TRI_CloseCollection(collection);
|
|
TRI_FreeCollection(collection); // will free doc
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// create document collection and shaper
|
|
InitSimCollection(doc, shaper);
|
|
|
|
return doc;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief frees the memory allocated, but does not free the pointer
|
|
///
|
|
/// Note that the collection must be closed first.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_DestroySimCollection (TRI_sim_collection_t* collection) {
|
|
size_t i;
|
|
size_t n;
|
|
|
|
TRI_DestroyCondition(&collection->_journalsCondition);
|
|
|
|
TRI_DestroyAssociativePointer(&collection->_primaryIndex);
|
|
|
|
// free all elements in the edges index
|
|
n = collection->_edgesIndex._nrAlloc;
|
|
for (i = 0; i < n; ++i) {
|
|
void* element = collection->_edgesIndex._table[i];
|
|
if (element) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element);
|
|
}
|
|
}
|
|
TRI_DestroyMultiPointer(&collection->_edgesIndex);
|
|
|
|
TRI_FreeSimpleHeaders(collection->_headers);
|
|
|
|
TRI_DestroyReadWriteLock(&collection->_lock);
|
|
|
|
// free memory allocated for index field names
|
|
n = collection->_indexes._length;
|
|
for (i = 0 ; i < n ; ++i) {
|
|
TRI_index_t* idx = (TRI_index_t*) collection->_indexes._buffer[i];
|
|
|
|
TRI_FreeIndex(idx);
|
|
}
|
|
// free index vector
|
|
TRI_DestroyVectorPointer(&collection->_indexes);
|
|
|
|
TRI_DestroyDocCollection(&collection->base);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief frees the memory allocated and frees the pointer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeSimCollection (TRI_sim_collection_t* collection) {
|
|
TRI_DestroySimCollection(collection);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, collection);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new journal
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_datafile_t* TRI_CreateJournalSimCollection (TRI_sim_collection_t* collection) {
|
|
return TRI_CreateJournalDocCollection(&collection->base);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief closes an existing journal
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_CloseJournalSimCollection (TRI_sim_collection_t* collection,
|
|
size_t position) {
|
|
return TRI_CloseJournalDocCollection(&collection->base, position);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief opens an existing collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_sim_collection_t* TRI_OpenSimCollection (TRI_vocbase_t* vocbase, char const* path) {
|
|
TRI_collection_t* collection;
|
|
TRI_shaper_t* shaper;
|
|
TRI_sim_collection_t* doc;
|
|
char* shapes;
|
|
|
|
// first open the document collection
|
|
doc = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_sim_collection_t), false);
|
|
if (!doc) {
|
|
return NULL;
|
|
}
|
|
|
|
collection = TRI_OpenCollection(vocbase, &doc->base.base, path);
|
|
|
|
if (collection == NULL) {
|
|
LOG_ERROR("cannot open document collection");
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, doc);
|
|
return NULL;
|
|
}
|
|
|
|
// then the shape collection
|
|
shapes = TRI_Concatenate2File(collection->_directory, "SHAPES");
|
|
if (!shapes) {
|
|
TRI_CloseCollection(collection);
|
|
TRI_FreeCollection(collection);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, doc);
|
|
return NULL;
|
|
}
|
|
|
|
shaper = TRI_OpenVocShaper(vocbase, shapes);
|
|
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, shapes);
|
|
|
|
if (shaper == NULL) {
|
|
LOG_ERROR("cannot open shapes collection");
|
|
|
|
TRI_CloseCollection(collection);
|
|
TRI_FreeCollection(collection);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// create document collection and shaper
|
|
InitSimCollection(doc, shaper);
|
|
|
|
// read all documents and fill indexes
|
|
TRI_IterateCollection(collection, OpenIterator, collection);
|
|
TRI_IterateIndexCollection(collection, OpenIndexIterator, collection);
|
|
|
|
// output infomations about datafiles and journals
|
|
if (TRI_IsTraceLogging(__FILE__)) {
|
|
DebugDatafileInfoDocCollection(&doc->base);
|
|
DebugHeaderSimCollection(doc);
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief closes an open collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int TRI_CloseSimCollection (TRI_sim_collection_t* collection) {
|
|
int res;
|
|
|
|
res = TRI_CloseCollection(&collection->base.base);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return res;
|
|
}
|
|
|
|
res = TRI_CloseVocShaper(collection->base._shaper);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return res;
|
|
}
|
|
|
|
// this does also destroy the shaper's underlying blob collection
|
|
TRI_FreeVocShaper(collection->base._shaper);
|
|
|
|
collection->base._shaper = NULL;
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- INDEXES
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new entry in the immediate indexes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int CreateImmediateIndexes (TRI_sim_collection_t* sim,
|
|
TRI_doc_mptr_t* header) {
|
|
TRI_df_marker_t const* marker;
|
|
TRI_doc_mptr_t* found;
|
|
size_t n;
|
|
size_t i;
|
|
int result;
|
|
bool constraint;
|
|
|
|
// .............................................................................
|
|
// update primary index
|
|
// .............................................................................
|
|
|
|
// add a new header
|
|
found = TRI_InsertKeyAssociativePointer(&sim->_primaryIndex, &header->_did, header, false);
|
|
|
|
if (found != NULL) {
|
|
LOG_ERROR("document %lu already existed with revision %lu while creating revision %lu",
|
|
(unsigned long) header->_did,
|
|
(unsigned long) found->_rid,
|
|
(unsigned long) header->_rid);
|
|
|
|
sim->_headers->release(sim->_headers, header);
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_UNIQUE_CONSTRAINT_VIOLATED);
|
|
}
|
|
|
|
// return in case of a deleted document
|
|
if (header->_deletion != 0) {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
// .............................................................................
|
|
// update edges index
|
|
// .............................................................................
|
|
|
|
// check the document type
|
|
marker = header->_data;
|
|
|
|
// add edges
|
|
if (marker->_type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_edge_header_t* entry;
|
|
TRI_doc_edge_marker_t const* edge;
|
|
|
|
edge = header->_data;
|
|
|
|
// IN
|
|
entry = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_edge_header_t), true);
|
|
/* TODO FIXME: memory allocation might fail */
|
|
|
|
entry->_mptr = header;
|
|
entry->_direction = TRI_EDGE_IN;
|
|
entry->_cid = edge->_toCid;
|
|
entry->_did = edge->_toDid;
|
|
|
|
TRI_InsertElementMultiPointer(&sim->_edgesIndex, entry, true);
|
|
|
|
// OUT
|
|
entry = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_edge_header_t), true);
|
|
/* TODO FIXME: memory allocation might fail */
|
|
|
|
entry->_mptr = header;
|
|
entry->_direction = TRI_EDGE_OUT;
|
|
entry->_cid = edge->_fromCid;
|
|
entry->_did = edge->_fromDid;
|
|
|
|
TRI_InsertElementMultiPointer(&sim->_edgesIndex, entry, true);
|
|
|
|
// ANY
|
|
entry = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_edge_header_t), true);
|
|
/* TODO FIXME: memory allocation might fail */
|
|
|
|
entry->_mptr = header;
|
|
entry->_direction = TRI_EDGE_ANY;
|
|
entry->_cid = edge->_toCid;
|
|
entry->_did = edge->_toDid;
|
|
|
|
TRI_InsertElementMultiPointer(&sim->_edgesIndex, entry, true);
|
|
|
|
if (edge->_toCid != edge->_fromCid || edge->_toDid != edge->_fromDid) {
|
|
entry = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_edge_header_t), true);
|
|
/* TODO FIXME: memory allocation might fail */
|
|
|
|
entry->_mptr = header;
|
|
entry->_direction = TRI_EDGE_ANY;
|
|
entry->_cid = edge->_fromCid;
|
|
entry->_did = edge->_fromDid;
|
|
|
|
TRI_InsertElementMultiPointer(&sim->_edgesIndex, entry, true);
|
|
}
|
|
}
|
|
|
|
// .............................................................................
|
|
// update all the other indices
|
|
// .............................................................................
|
|
|
|
n = sim->_indexes._length;
|
|
result = TRI_ERROR_NO_ERROR;
|
|
constraint = false;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
idx = sim->_indexes._buffer[i];
|
|
res = idx->insert(idx, header);
|
|
|
|
// in case of no-memory, return immediately
|
|
if (res == TRI_ERROR_OUT_OF_MEMORY) {
|
|
return res;
|
|
}
|
|
|
|
// "prefer" unique constraint violated
|
|
if (res == TRI_ERROR_AVOCADO_UNIQUE_CONSTRAINT_VIOLATED) {
|
|
constraint = true;
|
|
}
|
|
else if (res != TRI_ERROR_NO_ERROR) {
|
|
result = res;
|
|
}
|
|
}
|
|
|
|
if (constraint) {
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_UNIQUE_CONSTRAINT_VIOLATED);
|
|
}
|
|
|
|
if (result != TRI_ERROR_NO_ERROR) {
|
|
return TRI_set_errno(result);
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief updates the immediate indexes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int UpdateImmediateIndexes (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_doc_mptr_t const* update) {
|
|
|
|
union { TRI_doc_mptr_t const* c; TRI_doc_mptr_t* v; } change;
|
|
TRI_shaped_json_t old;
|
|
bool constraint;
|
|
int result;
|
|
size_t i;
|
|
size_t n;
|
|
|
|
// get the old document
|
|
old = header->_document;
|
|
|
|
// .............................................................................
|
|
// update primary index
|
|
// .............................................................................
|
|
|
|
// update all fields, the document identifier stays the same
|
|
change.c = header;
|
|
|
|
change.v->_rid = update->_rid;
|
|
change.v->_eid = update->_eid;
|
|
change.v->_fid = update->_fid;
|
|
change.v->_deletion = update->_deletion;
|
|
|
|
change.v->_data = update->_data;
|
|
change.v->_document = update->_document;
|
|
|
|
// .............................................................................
|
|
// update all the other indices
|
|
// .............................................................................
|
|
|
|
n = collection->_indexes._length;
|
|
result = TRI_ERROR_NO_ERROR;
|
|
constraint = false;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
idx = collection->_indexes._buffer[i];
|
|
res = idx->update(idx, header, &old);
|
|
|
|
// in case of no-memory, return immediately
|
|
if (res == TRI_ERROR_OUT_OF_MEMORY) {
|
|
return res;
|
|
}
|
|
|
|
// "prefer" unique constraint violated
|
|
if (res == TRI_ERROR_AVOCADO_UNIQUE_CONSTRAINT_VIOLATED) {
|
|
constraint = true;
|
|
}
|
|
else if (res != TRI_ERROR_NO_ERROR) {
|
|
result = res;
|
|
}
|
|
}
|
|
|
|
if (constraint) {
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_UNIQUE_CONSTRAINT_VIOLATED);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief deletes an entry from the immediate indexes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int DeleteImmediateIndexes (TRI_sim_collection_t* collection,
|
|
TRI_doc_mptr_t const* header,
|
|
TRI_voc_tick_t deletion) {
|
|
union { TRI_doc_mptr_t const* c; TRI_doc_mptr_t* v; } change;
|
|
TRI_df_marker_t const* marker;
|
|
TRI_doc_mptr_t* found;
|
|
size_t n;
|
|
size_t i;
|
|
bool result;
|
|
|
|
// set the deletion flag
|
|
change.c = header;
|
|
change.v->_deletion = deletion;
|
|
|
|
// .............................................................................
|
|
// remove from main index
|
|
// .............................................................................
|
|
|
|
found = TRI_RemoveKeyAssociativePointer(&collection->_primaryIndex, &header->_did);
|
|
|
|
if (found == NULL) {
|
|
return TRI_set_errno(TRI_ERROR_AVOCADO_DOCUMENT_NOT_FOUND);
|
|
}
|
|
|
|
// .............................................................................
|
|
// update edges index
|
|
// .............................................................................
|
|
|
|
// check the document type
|
|
marker = header->_data;
|
|
|
|
// add edges
|
|
if (marker->_type == TRI_DOC_MARKER_EDGE) {
|
|
TRI_edge_header_t entry;
|
|
TRI_edge_header_t* old;
|
|
TRI_doc_edge_marker_t const* edge;
|
|
|
|
edge = header->_data;
|
|
|
|
memset(&entry, 0, sizeof(entry));
|
|
entry._mptr = header;
|
|
|
|
// IN
|
|
entry._direction = TRI_EDGE_IN;
|
|
entry._cid = edge->_toCid;
|
|
entry._did = edge->_toDid;
|
|
old = TRI_RemoveElementMultiPointer(&collection->_edgesIndex, &entry);
|
|
|
|
if (old != NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, old);
|
|
}
|
|
|
|
// OUT
|
|
entry._direction = TRI_EDGE_OUT;
|
|
entry._cid = edge->_fromCid;
|
|
entry._did = edge->_fromDid;
|
|
old = TRI_RemoveElementMultiPointer(&collection->_edgesIndex, &entry);
|
|
|
|
if (old != NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, old);
|
|
}
|
|
|
|
// ANY
|
|
entry._direction = TRI_EDGE_ANY;
|
|
entry._cid = edge->_toCid;
|
|
entry._did = edge->_toDid;
|
|
old = TRI_RemoveElementMultiPointer(&collection->_edgesIndex, &entry);
|
|
|
|
if (old != NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, old);
|
|
}
|
|
|
|
if (edge->_toCid != edge->_fromCid || edge->_toDid != edge->_fromDid) {
|
|
entry._direction = TRI_EDGE_ANY;
|
|
entry._cid = edge->_fromCid;
|
|
entry._did = edge->_fromDid;
|
|
old = TRI_RemoveElementMultiPointer(&collection->_edgesIndex, &entry);
|
|
|
|
if (old != NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, old);
|
|
}
|
|
}
|
|
}
|
|
|
|
// .............................................................................
|
|
// remove from all other indexes
|
|
// .............................................................................
|
|
|
|
n = collection->_indexes._length;
|
|
result = TRI_ERROR_NO_ERROR;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
idx = collection->_indexes._buffer[i];
|
|
res = idx->remove(idx, header);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
result = res;
|
|
}
|
|
}
|
|
|
|
// and release the header pointer
|
|
collection->_headers->release(collection->_headers, change.v);
|
|
|
|
// that's it
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief initialises an index with all existing documents
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool FillIndex (TRI_sim_collection_t* collection,
|
|
TRI_index_t* idx) {
|
|
TRI_doc_mptr_t const* mptr;
|
|
size_t n;
|
|
size_t scanned;
|
|
void** end;
|
|
void** ptr;
|
|
int res;
|
|
|
|
// update index
|
|
n = collection->_primaryIndex._nrUsed;
|
|
ptr = collection->_primaryIndex._table;
|
|
end = collection->_primaryIndex._table + collection->_primaryIndex._nrAlloc;
|
|
|
|
scanned = 0;
|
|
|
|
for (; ptr < end; ++ptr) {
|
|
if (*ptr != NULL) {
|
|
mptr = *ptr;
|
|
|
|
++scanned;
|
|
|
|
if (mptr->_deletion == 0) {
|
|
res = idx->insert(idx, *ptr);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
LOG_WARNING("failed to insert document '%lu:%lu' for index '%lu'",
|
|
(unsigned long) collection->base.base._cid,
|
|
(unsigned long) mptr->_did,
|
|
(unsigned long) idx->_iid);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (scanned % 10000 == 0) {
|
|
LOG_INFO("indexed %ld of %ld documents", scanned, n);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns a description of all indexes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t* sim) {
|
|
TRI_vector_pointer_t* vector;
|
|
size_t n;
|
|
size_t i;
|
|
|
|
vector = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_vector_pointer_t), false);
|
|
if (!vector) {
|
|
return NULL;
|
|
}
|
|
|
|
TRI_InitVectorPointer(vector, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
// .............................................................................
|
|
// inside read-lock
|
|
// .............................................................................
|
|
|
|
TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
n = sim->_indexes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
TRI_json_t* json;
|
|
|
|
idx = sim->_indexes._buffer[i];
|
|
|
|
json = idx->json(idx, &sim->base);
|
|
|
|
if (json != NULL) {
|
|
TRI_PushBackVectorPointer(vector, json);
|
|
}
|
|
}
|
|
|
|
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside read-lock
|
|
// .............................................................................
|
|
|
|
return vector;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns a description of anindex
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_IndexSimCollection (TRI_sim_collection_t* sim, TRI_idx_iid_t iid) {
|
|
TRI_index_t* idx = NULL;
|
|
size_t n;
|
|
size_t i;
|
|
|
|
// .............................................................................
|
|
// inside read-lock
|
|
// .............................................................................
|
|
|
|
TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
n = sim->_indexes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
idx = sim->_indexes._buffer[i];
|
|
|
|
if (idx->_iid == iid) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside read-lock
|
|
// .............................................................................
|
|
|
|
return i < n ? idx : NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief drops an index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_DropIndexSimCollection (TRI_sim_collection_t* sim, TRI_idx_iid_t iid) {
|
|
TRI_index_t* found;
|
|
bool removeResult;
|
|
size_t n;
|
|
size_t i;
|
|
|
|
if (iid == 0) {
|
|
return true;
|
|
}
|
|
|
|
found = NULL;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
n = sim->_indexes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
|
|
idx = sim->_indexes._buffer[i];
|
|
|
|
if (idx->_iid == iid) {
|
|
found = TRI_RemoveVectorPointer(&sim->_indexes, i);
|
|
|
|
if (found != NULL) {
|
|
found->removeIndex(found, &sim->base);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (found != NULL) {
|
|
removeResult = TRI_RemoveIndexFile(&sim->base, found);
|
|
TRI_FreeIndex(found);
|
|
return removeResult;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- PRIMARY INDEX
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief hashs the document id
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static uint64_t HashKeyHeader (TRI_associative_pointer_t* array, void const* key) {
|
|
TRI_voc_did_t const* k = key;
|
|
|
|
return TRI_FnvHashPointer(k, sizeof(TRI_voc_did_t));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief hashs the document header
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static uint64_t HashElementDocument (TRI_associative_pointer_t* array, void const* element) {
|
|
TRI_doc_mptr_t const* e = element;
|
|
|
|
return TRI_FnvHashPointer(&e->_did, sizeof(TRI_voc_did_t));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief compares a document id and a document
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool IsEqualKeyDocument (TRI_associative_pointer_t* array, void const* key, void const* element) {
|
|
TRI_voc_did_t const* k = key;
|
|
TRI_doc_mptr_t const* e = element;
|
|
|
|
return *k == e->_did;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief insert methods does nothing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int InsertPrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc) {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief update methods does nothing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int UpdatePrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc, TRI_shaped_json_t const* old) {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief remove methods does nothing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int RemovePrimary (TRI_index_t* idx, TRI_doc_mptr_t const* doc) {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief JSON description of a geo index, location is a list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_json_t* JsonPrimary (TRI_index_t* idx, TRI_doc_collection_t const* collection) {
|
|
TRI_json_t* json;
|
|
TRI_json_t* fields;
|
|
|
|
json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
|
|
fields = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, fields, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "_id"));
|
|
|
|
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "id", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 0));
|
|
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "primary"));
|
|
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "fields", fields);
|
|
|
|
return json;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- EDGES INDEX
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief looks up edges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_vector_pointer_t TRI_LookupEdgesSimCollection (TRI_sim_collection_t* edges,
|
|
TRI_edge_direction_e direction,
|
|
TRI_voc_cid_t cid,
|
|
TRI_voc_did_t did) {
|
|
union { TRI_doc_mptr_t* v; TRI_doc_mptr_t const* c; } cnv;
|
|
TRI_vector_pointer_t result;
|
|
TRI_edge_header_t entry;
|
|
TRI_vector_pointer_t found;
|
|
size_t i;
|
|
|
|
TRI_InitVectorPointer(&result, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
entry._direction = direction;
|
|
entry._cid = cid;
|
|
entry._did = did;
|
|
|
|
found = TRI_LookupByKeyMultiPointer(TRI_UNKNOWN_MEM_ZONE, &edges->_edgesIndex, &entry);
|
|
|
|
for (i = 0; i < found._length; ++i) {
|
|
cnv.c = ((TRI_edge_header_t*) found._buffer[i])->_mptr;
|
|
|
|
TRI_PushBackVectorPointer(&result, cnv.v);
|
|
}
|
|
|
|
TRI_DestroyVectorPointer(&found);
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- CAP CONSTRAINT
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds a cap constraint to a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_t* CreateCapConstraintSimCollection (TRI_sim_collection_t* sim,
|
|
size_t size,
|
|
TRI_idx_iid_t iid,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
bool ok;
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
// check if we already know a cap constraint
|
|
if (sim->base._capConstraint != NULL) {
|
|
if (sim->base._capConstraint->_size == size) {
|
|
return &sim->base._capConstraint->base;
|
|
}
|
|
else {
|
|
TRI_set_errno(TRI_ERROR_AVOCADO_CAP_CONSTRAINT_ALREADY_DEFINED);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// create a new index
|
|
idx = TRI_CreateCapConstraint(&sim->base, size);
|
|
|
|
if (iid) {
|
|
idx->_iid = iid;
|
|
}
|
|
|
|
// initialises the index with all existing documents
|
|
ok = FillIndex(sim, idx);
|
|
|
|
if (! ok) {
|
|
TRI_FreeCapConstraint(idx);
|
|
return NULL;
|
|
}
|
|
|
|
// and store index
|
|
TRI_PushBackVectorPointer(&sim->_indexes, idx);
|
|
sim->base._capConstraint = (TRI_cap_constraint_t*) idx;
|
|
|
|
if (created != NULL) {
|
|
*created = true;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a cap constraint exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsureCapConstraintSimCollection (TRI_sim_collection_t* sim,
|
|
size_t size,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
idx = CreateCapConstraintSimCollection(sim, size, 0, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- GEO INDEX
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds a geo index to a collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* sim,
|
|
char const* location,
|
|
char const* latitude,
|
|
char const* longitude,
|
|
bool geoJson,
|
|
bool constraint,
|
|
bool ignoreNull,
|
|
TRI_idx_iid_t iid,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
TRI_shape_pid_t lat;
|
|
TRI_shape_pid_t loc;
|
|
TRI_shape_pid_t lon;
|
|
TRI_shaper_t* shaper;
|
|
bool ok;
|
|
|
|
lat = 0;
|
|
lon = 0;
|
|
loc = 0;
|
|
idx = NULL;
|
|
|
|
shaper = sim->base._shaper;
|
|
|
|
if (location != NULL) {
|
|
loc = shaper->findAttributePathByName(shaper, location);
|
|
|
|
if (loc == 0) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (latitude != NULL) {
|
|
lat = shaper->findAttributePathByName(shaper, latitude);
|
|
|
|
if (lat == 0) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (longitude != NULL) {
|
|
lon = shaper->findAttributePathByName(shaper, longitude);
|
|
|
|
if (lon == 0) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// check, if we know the index
|
|
if (location != NULL) {
|
|
idx = TRI_LookupGeoIndex1SimCollection(sim, loc, geoJson, constraint, ignoreNull);
|
|
}
|
|
else if (longitude != NULL && latitude != NULL) {
|
|
idx = TRI_LookupGeoIndex2SimCollection(sim, lat, lon, constraint, ignoreNull);
|
|
}
|
|
else {
|
|
TRI_set_errno(TRI_ERROR_INTERNAL);
|
|
LOG_TRACE("expecting either 'location' or 'latitude' and 'longitude'");
|
|
return NULL;
|
|
}
|
|
|
|
if (idx != NULL) {
|
|
LOG_TRACE("geo-index already created for location '%s'", location);
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
// create a new index
|
|
if (location != NULL) {
|
|
idx = TRI_CreateGeo1Index(&sim->base, location, loc, geoJson, constraint, ignoreNull);
|
|
|
|
LOG_TRACE("created geo-index for location '%s': %d",
|
|
location,
|
|
(unsigned long) loc);
|
|
}
|
|
else if (longitude != NULL && latitude != NULL) {
|
|
idx = TRI_CreateGeo2Index(&sim->base, latitude, lat, longitude, lon, constraint, ignoreNull);
|
|
|
|
LOG_TRACE("created geo-index for location '%s': %d, %d",
|
|
location,
|
|
(unsigned long) lat,
|
|
(unsigned long) lon);
|
|
}
|
|
|
|
if (iid) {
|
|
idx->_iid = iid;
|
|
}
|
|
|
|
// initialises the index with all existing documents
|
|
ok = FillIndex(sim, idx);
|
|
|
|
if (! ok) {
|
|
TRI_FreeGeoIndex(idx);
|
|
return NULL;
|
|
}
|
|
|
|
// and store index
|
|
TRI_PushBackVectorPointer(&sim->_indexes, idx);
|
|
|
|
if (created != NULL) {
|
|
*created = true;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds a hash index to the collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_t* CreateHashIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
TRI_shaper_t* shaper;
|
|
TRI_vector_pointer_t fields;
|
|
TRI_vector_t paths;
|
|
bool ok;
|
|
size_t j;
|
|
|
|
idx = NULL;
|
|
shaper = collection->base._shaper;
|
|
|
|
TRI_InitVector(&paths, TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_shape_pid_t));
|
|
TRI_InitVectorPointer(&fields, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
// ...........................................................................
|
|
// Determine the shape ids for the attributes
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < attributes->_length; ++j) {
|
|
char* path;
|
|
TRI_shape_pid_t pid;
|
|
|
|
path = *((char**)(TRI_AtVector(attributes,j)));
|
|
pid = shaper->findAttributePathByName(shaper, path);
|
|
|
|
if (pid == 0) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TRI_PushBackVectorPointer(&fields, path);
|
|
TRI_PushBackVector(&paths, &pid);
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Attempt to find an existing index which matches the attributes above.
|
|
// If a suitable index is found, return that one otherwise we need to create
|
|
// a new one.
|
|
// ...........................................................................
|
|
|
|
idx = TRI_LookupHashIndexSimCollection(collection, &paths);
|
|
|
|
if (idx != NULL) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
LOG_TRACE("hash-index already created");
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Create the hash index
|
|
// ...........................................................................
|
|
|
|
idx = TRI_CreateHashIndex(&collection->base, &fields, &paths, unique);
|
|
|
|
// ...........................................................................
|
|
// release memory allocated to vector
|
|
// ...........................................................................
|
|
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
|
|
// ...........................................................................
|
|
// If index id given, use it otherwise use the default.
|
|
// ...........................................................................
|
|
|
|
if (iid) {
|
|
idx->_iid = iid;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// initialises the index with all existing documents
|
|
// ...........................................................................
|
|
|
|
ok = FillIndex(collection, idx);
|
|
|
|
if (! ok) {
|
|
TRI_FreeHashIndex(idx);
|
|
return NULL;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// store index and return
|
|
// ...........................................................................
|
|
|
|
TRI_PushBackVectorPointer(&collection->_indexes, idx);
|
|
|
|
if (created != NULL) {
|
|
*created = true;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds a priroity queue index to the collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_t* CreatePriorityQueueIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx = NULL;
|
|
TRI_shaper_t* shaper = collection->base._shaper;
|
|
TRI_vector_t paths;
|
|
TRI_vector_pointer_t fields;
|
|
bool ok;
|
|
size_t j;
|
|
|
|
|
|
TRI_InitVector(&paths, TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_shape_pid_t));
|
|
TRI_InitVectorPointer(&fields, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
// ...........................................................................
|
|
// Determine the shape ids for the attributes
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < attributes->_length; ++j) {
|
|
char* path = *((char**)(TRI_AtVector(attributes,j)));
|
|
TRI_shape_pid_t shape = shaper->findAttributePathByName(shaper, path);
|
|
|
|
if (shape == 0) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_PushBackVector(&paths, &shape);
|
|
TRI_PushBackVectorPointer(&fields, path);
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Attempt to find an existing index which matches the attributes above.
|
|
// If a suitable index is found, return that one otherwise we need to create
|
|
// a new one.
|
|
// ...........................................................................
|
|
|
|
idx = TRI_LookupPriorityQueueIndexSimCollection(collection, &paths);
|
|
|
|
if (idx != NULL) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
|
|
LOG_TRACE("priority queue index already created");
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Create the priority queue index
|
|
// ...........................................................................
|
|
|
|
idx = TRI_CreatePriorityQueueIndex(&collection->base, &fields, &paths, unique);
|
|
|
|
// ...........................................................................
|
|
// If index id given, use it otherwise use the default.
|
|
// ...........................................................................
|
|
|
|
if (iid) {
|
|
idx->_iid = iid;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// initialises the index with all existing documents
|
|
// ...........................................................................
|
|
|
|
ok = FillIndex(collection, idx);
|
|
|
|
if (! ok) {
|
|
TRI_FreeSkiplistIndex(idx);
|
|
return NULL;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// store index
|
|
// ...........................................................................
|
|
|
|
TRI_PushBackVectorPointer(&collection->_indexes, idx);
|
|
|
|
// ...........................................................................
|
|
// release memory allocated to vector
|
|
// ...........................................................................
|
|
|
|
TRI_DestroyVector(&paths);
|
|
|
|
if (created != NULL) {
|
|
*created = true;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief adds a skiplist index to the collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_index_t* CreateSkiplistIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* attributes,
|
|
TRI_idx_iid_t iid,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx = NULL;
|
|
TRI_shaper_t* shaper = collection->base._shaper;
|
|
TRI_vector_t paths;
|
|
TRI_vector_pointer_t fields;
|
|
bool ok;
|
|
size_t j;
|
|
|
|
TRI_InitVector(&paths, TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_shape_pid_t));
|
|
TRI_InitVectorPointer(&fields, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
// ...........................................................................
|
|
// Determine the shape ids for the attributes
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < attributes->_length; ++j) {
|
|
char* path = *((char**)(TRI_AtVector(attributes,j)));
|
|
TRI_shape_pid_t shape = shaper->findAttributePathByName(shaper, path);
|
|
|
|
if (shape == 0) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TRI_PushBackVector(&paths, &shape);
|
|
TRI_PushBackVectorPointer(&fields, path);
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Attempt to find an existing index which matches the attributes above.
|
|
// If a suitable index is found, return that one otherwise we need to create
|
|
// a new one.
|
|
// ...........................................................................
|
|
|
|
idx = TRI_LookupSkiplistIndexSimCollection(collection, &paths);
|
|
|
|
if (idx != NULL) {
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
LOG_TRACE("skiplist-index already created");
|
|
|
|
if (created != NULL) {
|
|
*created = false;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Create the skiplist index
|
|
// ...........................................................................
|
|
|
|
idx = TRI_CreateSkiplistIndex(&collection->base, &fields, &paths, unique);
|
|
|
|
// ...........................................................................
|
|
// If index id given, use it otherwise use the default.
|
|
// ...........................................................................
|
|
|
|
if (iid) {
|
|
idx->_iid = iid;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// initialises the index with all existing documents
|
|
// ...........................................................................
|
|
|
|
ok = FillIndex(collection, idx);
|
|
|
|
if (! ok) {
|
|
TRI_FreeSkiplistIndex(idx);
|
|
return NULL;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// store index
|
|
// ...........................................................................
|
|
|
|
TRI_PushBackVectorPointer(&collection->_indexes, idx);
|
|
|
|
// ...........................................................................
|
|
// release memory allocated to vector
|
|
// ...........................................................................
|
|
|
|
TRI_DestroyVector(&paths);
|
|
TRI_DestroyVectorPointer(&fields);
|
|
|
|
if (created != NULL) {
|
|
*created = true;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- SELECT BY EXAMPLE QUERY
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks for match of an example
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool IsExampleMatch (TRI_shaper_t* shaper,
|
|
TRI_doc_mptr_t const* doc,
|
|
size_t len,
|
|
TRI_shape_pid_t* pids,
|
|
TRI_shaped_json_t** values) {
|
|
TRI_shape_access_t const* accessor;
|
|
TRI_shaped_json_t const* document;
|
|
TRI_shaped_json_t* example;
|
|
TRI_shaped_json_t result;
|
|
bool ok;
|
|
size_t i;
|
|
|
|
document = &doc->_document;
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
example = values[i];
|
|
accessor = TRI_FindAccessorVocShaper(shaper, document->_sid, pids[i]);
|
|
|
|
if (accessor == NULL) {
|
|
LOG_TRACE("failed to get accessor for sid %lu and path %lu",
|
|
(unsigned long) document->_sid,
|
|
(unsigned long) pids[i]);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (accessor->_shape == NULL) {
|
|
LOG_TRACE("expecting an array for path %lu",
|
|
(unsigned long) pids[i]);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (accessor->_shape->_sid != example->_sid) {
|
|
LOG_TRACE("expecting sid %lu for path %lu, got sid %lu",
|
|
(unsigned long) example->_sid,
|
|
(unsigned long) pids[i],
|
|
(unsigned long) accessor->_shape->_sid);
|
|
|
|
return false;
|
|
}
|
|
|
|
ok = TRI_ExecuteShapeAccessor(accessor, document, &result);
|
|
|
|
if (! ok) {
|
|
LOG_TRACE("failed to get accessor for sid %lu and path %lu",
|
|
(unsigned long) document->_sid,
|
|
(unsigned long) pids[i]);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (result._data.length != example->_data.length) {
|
|
LOG_TRACE("expecting length %lu, got length %lu for path %lu",
|
|
(unsigned long) result._data.length,
|
|
(unsigned long) example->_data.length,
|
|
(unsigned long) pids[i]);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(result._data.data, example->_data.data, example->_data.length) != 0) {
|
|
LOG_TRACE("data mismatch at path %lu",
|
|
(unsigned long) pids[i]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief executes a select-by-example query
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_vector_t TRI_SelectByExample (TRI_sim_collection_t* sim,
|
|
size_t length,
|
|
TRI_shape_pid_t* pids,
|
|
TRI_shaped_json_t** values) {
|
|
TRI_shaper_t* shaper;
|
|
TRI_doc_mptr_t const** ptr;
|
|
TRI_doc_mptr_t const** end;
|
|
TRI_vector_t filtered;
|
|
|
|
// use filtered to hold copies of the master pointer
|
|
TRI_InitVector(&filtered, TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_doc_mptr_t));
|
|
|
|
// do a full scan
|
|
shaper = sim->base._shaper;
|
|
|
|
ptr = (TRI_doc_mptr_t const**) (sim->_primaryIndex._table);
|
|
end = (TRI_doc_mptr_t const**) (sim->_primaryIndex._table + sim->_primaryIndex._nrAlloc);
|
|
|
|
for (; ptr < end; ++ptr) {
|
|
if (*ptr && (*ptr)->_deletion == 0) {
|
|
if (IsExampleMatch(shaper, *ptr, length, pids, values)) {
|
|
TRI_PushBackVector(&filtered, *ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup VocBase
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds a geo index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_LookupGeoIndex1SimCollection (TRI_sim_collection_t* collection,
|
|
TRI_shape_pid_t location,
|
|
bool geoJson,
|
|
bool constraint,
|
|
bool ignoreNull) {
|
|
size_t n;
|
|
size_t i;
|
|
|
|
n = collection->_indexes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
|
|
idx = collection->_indexes._buffer[i];
|
|
|
|
if (idx->_type == TRI_IDX_TYPE_GEO1_INDEX) {
|
|
TRI_geo_index_t* geo = (TRI_geo_index_t*) idx;
|
|
|
|
if (geo->_location != 0 && geo->_location == location && geo->_geoJson == geoJson && geo->_constraint == constraint) {
|
|
if (! constraint || geo->base._ignoreNull == ignoreNull) {
|
|
return idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds a geo index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_LookupGeoIndex2SimCollection (TRI_sim_collection_t* collection,
|
|
TRI_shape_pid_t latitude,
|
|
TRI_shape_pid_t longitude,
|
|
bool constraint,
|
|
bool ignoreNull) {
|
|
size_t n;
|
|
size_t i;
|
|
|
|
n = collection->_indexes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_index_t* idx;
|
|
|
|
idx = collection->_indexes._buffer[i];
|
|
|
|
if (idx->_type == TRI_IDX_TYPE_GEO2_INDEX) {
|
|
TRI_geo_index_t* geo = (TRI_geo_index_t*) idx;
|
|
|
|
if (geo->_latitude != 0 && geo->_longitude != 0 && geo->_latitude == latitude && geo->_longitude == longitude && geo->_constraint == constraint) {
|
|
if (! constraint || geo->base._ignoreNull == ignoreNull) {
|
|
return idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds a hash index (unique or non-unique)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_LookupHashIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* paths) {
|
|
TRI_index_t* matchedIndex = NULL;
|
|
size_t j, k;
|
|
|
|
// ...........................................................................
|
|
// Note: This function does NOT differentiate between non-unique and unique
|
|
// hash indexes. The first hash index which matches the attributes
|
|
// (paths parameter) will be returned.
|
|
// ...........................................................................
|
|
|
|
|
|
// ...........................................................................
|
|
// go through every index and see if we have a match
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < collection->_indexes._length; ++j) {
|
|
TRI_index_t* idx = collection->_indexes._buffer[j];
|
|
TRI_hash_index_t* hashIndex = (TRI_hash_index_t*) idx;
|
|
bool found = true;
|
|
|
|
// .........................................................................
|
|
// check that the type of the index is in fact a hash index
|
|
// .........................................................................
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_HASH_INDEX) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// check that the number of paths (fields) in the hash index matches that
|
|
// of the number of attributes
|
|
// .........................................................................
|
|
|
|
if (paths->_length != hashIndex->_paths._length) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// Go through all the attributes and see if they match
|
|
// .........................................................................
|
|
|
|
for (k = 0; k < paths->_length; ++k) {
|
|
TRI_shape_pid_t field = *((TRI_shape_pid_t*)(TRI_AtVector(&hashIndex->_paths,k)));
|
|
TRI_shape_pid_t shape = *((TRI_shape_pid_t*)(TRI_AtVector(paths,k)));
|
|
|
|
if (field != shape) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (found) {
|
|
matchedIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return matchedIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds a priority queue index (non-unique)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_LookupPriorityQueueIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* paths) {
|
|
TRI_index_t* matchedIndex = NULL;
|
|
size_t j, k;
|
|
|
|
// ...........................................................................
|
|
// Note: This function does NOT differentiate between non-unique and unique
|
|
// skiplist indexes. The first index which matches the attributes
|
|
// (paths parameter) will be returned.
|
|
// ...........................................................................
|
|
|
|
|
|
// ...........................................................................
|
|
// go through every index and see if we have a match
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < collection->_indexes._length; ++j) {
|
|
TRI_index_t* idx = collection->_indexes._buffer[j];
|
|
TRI_priorityqueue_index_t* pqIndex = (TRI_priorityqueue_index_t*) idx;
|
|
bool found = true;
|
|
|
|
// .........................................................................
|
|
// check that the type of the index is in fact a skiplist index
|
|
// .........................................................................
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_PRIORITY_QUEUE_INDEX) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// check that the number of paths (fields) in the index matches that
|
|
// of the number of attributes
|
|
// .........................................................................
|
|
|
|
if (paths->_length != pqIndex->_paths._length) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// Go through all the attributes and see if they match
|
|
// .........................................................................
|
|
|
|
for (k = 0; k < paths->_length; ++k) {
|
|
TRI_shape_pid_t field = *((TRI_shape_pid_t*)(TRI_AtVector(&pqIndex->_paths,k)));
|
|
TRI_shape_pid_t shape = *((TRI_shape_pid_t*)(TRI_AtVector(paths,k)));
|
|
|
|
if (field != shape) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (found) {
|
|
matchedIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return matchedIndex;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds a skiplist index (unique or non-unique)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_LookupSkiplistIndexSimCollection (TRI_sim_collection_t* collection,
|
|
const TRI_vector_t* paths) {
|
|
TRI_index_t* matchedIndex = NULL;
|
|
size_t j, k;
|
|
|
|
// ...........................................................................
|
|
// Note: This function does NOT differentiate between non-unique and unique
|
|
// skiplist indexes. The first index which matches the attributes
|
|
// (paths parameter) will be returned.
|
|
// ...........................................................................
|
|
|
|
|
|
// ...........................................................................
|
|
// go through every index and see if we have a match
|
|
// ...........................................................................
|
|
|
|
for (j = 0; j < collection->_indexes._length; ++j) {
|
|
TRI_index_t* idx = collection->_indexes._buffer[j];
|
|
TRI_skiplist_index_t* skiplistIndex = (TRI_skiplist_index_t*) idx;
|
|
bool found = true;
|
|
|
|
// .........................................................................
|
|
// check that the type of the index is in fact a skiplist index
|
|
// .........................................................................
|
|
|
|
if (idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// check that the number of paths (fields) in the index matches that
|
|
// of the number of attributes
|
|
// .........................................................................
|
|
|
|
if (paths->_length != skiplistIndex->_paths._length) {
|
|
continue;
|
|
}
|
|
|
|
// .........................................................................
|
|
// Go through all the attributes and see if they match
|
|
// .........................................................................
|
|
|
|
for (k = 0; k < paths->_length; ++k) {
|
|
TRI_shape_pid_t field = *((TRI_shape_pid_t*)(TRI_AtVector(&skiplistIndex->_paths,k)));
|
|
TRI_shape_pid_t shape = *((TRI_shape_pid_t*)(TRI_AtVector(paths,k)));
|
|
|
|
if (field != shape) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (found) {
|
|
matchedIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return matchedIndex;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a geo index exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsureGeoIndex1SimCollection (TRI_sim_collection_t* sim,
|
|
char const* location,
|
|
bool geoJson,
|
|
bool constraint,
|
|
bool ignoreNull,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
idx = CreateGeoIndexSimCollection(sim, location, NULL, NULL, geoJson, constraint, ignoreNull, 0, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a geo index exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsureGeoIndex2SimCollection (TRI_sim_collection_t* sim,
|
|
char const* latitude,
|
|
char const* longitude,
|
|
bool constraint,
|
|
bool ignoreNull,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
idx = CreateGeoIndexSimCollection(sim, NULL, latitude, longitude, false, constraint, ignoreNull, 0, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a hash index exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsureHashIndexSimCollection(TRI_sim_collection_t* sim,
|
|
const TRI_vector_t* attributes,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// Given the list of attributes (as strings)
|
|
// .............................................................................
|
|
|
|
idx = CreateHashIndexSimCollection(sim, attributes, 0, unique, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a priority queue index exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsurePriorityQueueIndexSimCollection(TRI_sim_collection_t* sim,
|
|
const TRI_vector_t* attributes,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// Given the list of attributes (as strings)
|
|
// .............................................................................
|
|
|
|
idx = CreatePriorityQueueIndexSimCollection(sim, attributes, 0, unique, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ensures that a skiplist index exists
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_index_t* TRI_EnsureSkiplistIndexSimCollection(TRI_sim_collection_t* sim,
|
|
const TRI_vector_t* attributes,
|
|
bool unique,
|
|
bool* created) {
|
|
TRI_index_t* idx;
|
|
int res;
|
|
|
|
// .............................................................................
|
|
// inside write-lock the collection
|
|
// .............................................................................
|
|
|
|
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// Given the list of attributes (as strings)
|
|
// .............................................................................
|
|
|
|
idx = CreateSkiplistIndexSimCollection(sim, attributes, 0, unique, created);
|
|
|
|
if (idx == NULL) {
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
return NULL;
|
|
}
|
|
|
|
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim);
|
|
|
|
// .............................................................................
|
|
// outside write-lock
|
|
// .............................................................................
|
|
|
|
if (created) {
|
|
res = TRI_SaveIndex(&sim->base, idx);
|
|
|
|
return res == TRI_ERROR_NO_ERROR ? idx : NULL;
|
|
}
|
|
else {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
|
// End:
|