//////////////////////////////////////////////////////////////////////////////// /// @brief Ahuacatl, collection /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 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 Jan Steemann /// @author Copyright 2012, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Ahuacatl/ahuacatl-collections.h" #include "Ahuacatl/ahuacatl-access-optimiser.h" #include "VocBase/index.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief get collection/index id as a string /// /// The caller must free the result string //////////////////////////////////////////////////////////////////////////////// static char* GetIndexIdString (TRI_aql_collection_hint_t* const hint) { TRI_string_buffer_t buffer; char* result; TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE); TRI_AppendUInt64StringBuffer(&buffer, hint->_collection->_collection->_cid); TRI_AppendCharStringBuffer(&buffer, '/'); TRI_AppendUInt64StringBuffer(&buffer, hint->_index->_idx->_iid); result = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, buffer._buffer); TRI_DestroyStringBuffer(&buffer); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief comparator function to sort collections by name //////////////////////////////////////////////////////////////////////////////// static int CollectionNameComparator (const void* l, const void* r) { TRI_aql_collection_t* left = *((TRI_aql_collection_t**) l); TRI_aql_collection_t* right = *((TRI_aql_collection_t**) r); return strcmp(left->_name, right->_name); } //////////////////////////////////////////////////////////////////////////////// /// @brief create a collection container //////////////////////////////////////////////////////////////////////////////// static TRI_aql_collection_t* CreateCollectionContainer (const char* const name) { TRI_aql_collection_t* collection; assert(name); collection = (TRI_aql_collection_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_collection_t), false); if (!collection) { return NULL; } collection->_name = (char*) name; collection->_collection = NULL; collection->_barrier = NULL; collection->_readLocked = false; return collection; } //////////////////////////////////////////////////////////////////////////////// /// @brief set up the collection names vector and order it //////////////////////////////////////////////////////////////////////////////// bool SetupCollections (TRI_aql_context_t* const context) { size_t i; size_t n; bool result = true; // each collection used is contained once in the assoc. array // so we do not have to care about duplicate names here n = context->_collectionNames._nrAlloc; for (i = 0; i < n; ++i) { char* name = context->_collectionNames._table[i]; TRI_aql_collection_t* collection; if (!name) { continue; } collection = CreateCollectionContainer(name); if (!collection) { result = false; TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); break; } TRI_PushBackVectorPointer(&context->_collections, (void*) collection); } if (result && n > 0) { qsort(context->_collections._buffer, context->_collections._length, sizeof(void*), &CollectionNameComparator); } // now collections contains the sorted list of collections return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief open all collections used //////////////////////////////////////////////////////////////////////////////// bool OpenCollections (TRI_aql_context_t* const context) { size_t i; size_t n; n = context->_collections._length; for (i = 0; i < n; ++i) { TRI_aql_collection_t* collection = context->_collections._buffer[i]; assert(collection); assert(collection->_name); assert(!collection->_collection); assert(!collection->_barrier); assert(!collection->_readLocked); LOG_TRACE("locking collection '%s'", collection->_name); collection->_collection = TRI_UseCollectionByNameVocBase(context->_vocbase, collection->_name); if (collection->_collection == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_COLLECTION_NOT_FOUND, collection->_name); return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief get the JSON representation of a collection hint //////////////////////////////////////////////////////////////////////////////// TRI_json_t* TRI_GetJsonCollectionHintAql (TRI_aql_collection_hint_t* const hint) { TRI_json_t* result; if (hint == NULL) { return NULL; } result = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); if (result == NULL) { return NULL; } if (hint->_index == NULL) { // full table scan TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result, "accessType", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "all")); } else { // index usage TRI_index_t* idx = hint->_index->_idx; TRI_json_t* indexDescription; TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result, "accessType", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "index")); indexDescription = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); if (indexDescription != NULL) { TRI_string_buffer_t* buffer; char* idString = GetIndexIdString(hint); // index id if (idString != NULL) { TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, indexDescription, "id", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, idString)); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, idString); } // index type TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, indexDescription, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_TypeNameIndex(idx))); // index attributes buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE); if (buffer != NULL) { size_t i; for (i = 0; i < idx->_fields._length; i++) { if (i > 0) { TRI_AppendStringStringBuffer(buffer, ", "); } TRI_AppendStringStringBuffer(buffer, idx->_fields._buffer[i]); } TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, indexDescription, "attributes", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, buffer->_buffer)); TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); } } TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result, "index", indexDescription); } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief create a collection hint //////////////////////////////////////////////////////////////////////////////// TRI_aql_collection_hint_t* TRI_CreateCollectionHintAql (void) { TRI_aql_collection_hint_t* hint; hint = (TRI_aql_collection_hint_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_collection_hint_t), false); if (hint == NULL) { return NULL; } hint->_ranges = NULL; hint->_index = NULL; hint->_collection = NULL; return hint; } //////////////////////////////////////////////////////////////////////////////// /// @brief free a collection hint //////////////////////////////////////////////////////////////////////////////// void TRI_FreeCollectionHintAql (TRI_aql_collection_hint_t* const hint) { assert(hint); if (hint->_ranges) { TRI_FreeAccessesAql(hint->_ranges); } if (hint->_index) { TRI_FreeIndexAql(hint->_index); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, hint); } //////////////////////////////////////////////////////////////////////////////// /// @brief lookup a collection in the internal vector //////////////////////////////////////////////////////////////////////////////// TRI_aql_collection_t* TRI_GetCollectionAql (const TRI_aql_context_t* const context, const char* const collectionName) { size_t i, n; assert(context); n = context->_collections._length; for (i = 0; i < n; ++i) { TRI_aql_collection_t* col = (TRI_aql_collection_t*) TRI_AtVectorPointer(&context->_collections, i); if (TRI_EqualString(col->_name, collectionName)) { return col; } } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief unlock all collections used //////////////////////////////////////////////////////////////////////////////// void TRI_UnlockCollectionsAql (TRI_aql_context_t* const context) { size_t i; // unlock in reverse order i = context->_collections._length; while (i--) { TRI_aql_collection_t* collection = context->_collections._buffer[i]; assert(collection); assert(collection->_name); if (collection->_collection == NULL) { // collection not yet opened continue; } LOG_TRACE("unlocking collection '%s'", collection->_name); TRI_ReleaseCollectionVocBase(context->_vocbase, collection->_collection); collection->_collection = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief lock all collections used //////////////////////////////////////////////////////////////////////////////// bool TRI_LockCollectionsAql (TRI_aql_context_t* const context) { if (!SetupCollections(context)) { return false; } if (!OpenCollections(context)) { TRI_UnlockCollectionsAql(context); return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief read-locks all collections used in a query //////////////////////////////////////////////////////////////////////////////// bool TRI_ReadLockCollectionsAql (TRI_aql_context_t* const context) { size_t i; size_t n; bool result = true; // lock in forward order n = context->_collections._length; for (i = 0; i < n; ++i) { int lockResult; TRI_aql_collection_t* collection = (TRI_aql_collection_t*) context->_collections._buffer[i]; TRI_primary_collection_t* primaryCollection; assert(collection); assert(collection->_name); assert(collection->_collection); assert(collection->_collection->_collection); assert(!collection->_readLocked); assert(!collection->_barrier); primaryCollection = (TRI_primary_collection_t*) collection->_collection->_collection; LOG_TRACE("read-locking collection '%s'", collection->_name); lockResult = primaryCollection->beginRead(primaryCollection); if (lockResult != TRI_ERROR_NO_ERROR) { // couldn't acquire the read lock LOG_WARNING("couldn't acquire read-lock on collection '%s'", collection->_name); result = false; TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_COLLECTION_LOCK_FAILED, collection->_name); break; } else { collection->_readLocked = true; LOG_TRACE("read-locked collection '%s'", collection->_name); } } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief read-unlocks all collections used in a query //////////////////////////////////////////////////////////////////////////////// void TRI_ReadUnlockCollectionsAql (TRI_aql_context_t* const context) { size_t i; // unlock in reverse order i = context->_collections._length; while (i--) { TRI_aql_collection_t* collection = (TRI_aql_collection_t*) context->_collections._buffer[i]; TRI_primary_collection_t* primaryCollection; assert(collection); assert(collection->_name); if (!collection->_collection) { // don't unlock collections we weren't able to lock at all continue; } if (!collection->_readLocked) { // don't unlock non-read-locked collections continue; } assert(collection->_collection->_collection); assert(!collection->_barrier); primaryCollection = (TRI_primary_collection_t*) collection->_collection->_collection; LOG_TRACE("read-unlocking collection '%s'", collection->_name); primaryCollection->endRead(primaryCollection); collection->_readLocked = false; LOG_TRACE("read-unlocked collection '%s'", collection->_name); } } //////////////////////////////////////////////////////////////////////////////// /// @brief adds a gc marker for all collections used in a query //////////////////////////////////////////////////////////////////////////////// bool TRI_AddBarrierCollectionsAql (TRI_aql_context_t* const context) { size_t i; size_t n; bool result = true; // iterate in forward order n = context->_collections._length; for (i = 0; i < n; ++i) { TRI_barrier_t* ce; TRI_aql_collection_t* collection = (TRI_aql_collection_t*) context->_collections._buffer[i]; TRI_primary_collection_t* primaryCollection; assert(collection); assert(collection->_name); assert(collection->_collection); assert(collection->_readLocked); assert(collection->_collection->_collection); assert(!collection->_barrier); primaryCollection = (TRI_primary_collection_t*) collection->_collection->_collection; LOG_TRACE("adding barrier for collection '%s'", collection->_name); ce = TRI_CreateBarrierElement(&primaryCollection->_barrierList); if (!ce) { // couldn't create the barrier result = false; TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); break; } else { collection->_barrier = ce; } } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief removes the gc markers for all collections used in a query //////////////////////////////////////////////////////////////////////////////// void TRI_RemoveBarrierCollectionsAql (TRI_aql_context_t* const context) { size_t i; // iterate in reverse order i = context->_collections._length; while (i--) { TRI_aql_collection_t* collection = (TRI_aql_collection_t*) context->_collections._buffer[i]; assert(collection); assert(collection->_name); if (!collection->_collection || !collection->_readLocked || !collection->_barrier) { // don't process collections we weren't able to lock at all continue; } assert(collection->_readLocked); assert(collection->_barrier); assert(collection->_collection->_collection); LOG_TRACE("removing barrier for collection '%s'", collection->_name); TRI_FreeBarrier(collection->_barrier); collection->_barrier = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief add a collection name to the list of collections used //////////////////////////////////////////////////////////////////////////////// bool TRI_AddCollectionAql (TRI_aql_context_t* const context, const char* const name) { assert(context); assert(name); // duplicates are not a problem here, we simply ignore them TRI_InsertKeyAssociativePointer(&context->_collectionNames, name, (void*) name, false); if (context->_collectionNames._nrUsed > AQL_MAX_COLLECTIONS) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_TOO_MANY_COLLECTIONS, NULL); return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: