//////////////////////////////////////////////////////////////////////////////// /// @brief vocbase /// /// @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 "vocbase.h" #include #include #include #include #include #include #include #include #include #include #include #include // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief random server identifier (16 bit) //////////////////////////////////////////////////////////////////////////////// static uint16_t ServerIdentifier = 0; //////////////////////////////////////////////////////////////////////////////// /// @brief current tick identifier (48 bit) //////////////////////////////////////////////////////////////////////////////// static uint64_t CurrentTick = 0; //////////////////////////////////////////////////////////////////////////////// /// @brief tick lock //////////////////////////////////////////////////////////////////////////////// static TRI_spin_t TickLock; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- COLLECTION DICTIONARY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the collection id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyCid (TRI_associative_pointer_t* array, void const* key) { TRI_voc_cid_t const* k = key; return *k; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the collection id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementCid (TRI_associative_pointer_t* array, void const* element) { TRI_vocbase_col_t const* e = element; return e->_cid; } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a collection id and a collection //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyCid (TRI_associative_pointer_t* array, void const* key, void const* element) { TRI_voc_cid_t const* k = key; TRI_vocbase_col_t const* e = element; return *k == e->_cid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the collection name //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyCollectionName (TRI_associative_pointer_t* array, void const* key) { char const* k = (char const*) key; return TRI_FnvHashString(k); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the collection id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementCollectionName (TRI_associative_pointer_t* array, void const* element) { TRI_vocbase_col_t const* e = element; char const* name = e->_name; return TRI_FnvHashString(name); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a collection id and a collection //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyCollectionName (TRI_associative_pointer_t* array, void const* key, void const* element) { char const* k = (char const*) key; TRI_vocbase_col_t const* e = element; return TRI_EqualString(k, e->_name); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- VOCBASE // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief checks if a collection is allowed //////////////////////////////////////////////////////////////////////////////// static char IsAllowedCollectionName (char const* name) { bool ok; char const* ptr; for (ptr = name; *ptr; ++ptr) { if (name < ptr) { ok = (*ptr == '_') || ('0' <= *ptr && *ptr <= '9') || ('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z'); } else { ok = ('0' <= *ptr && *ptr <= '9') || ('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z'); } if (! ok) { return *ptr; } } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds a new collection //////////////////////////////////////////////////////////////////////////////// static TRI_vocbase_col_t* AddCollection (TRI_vocbase_t* vocbase, TRI_col_type_t type, char const* name, TRI_voc_cid_t cid, char const* path) { void const* found; TRI_vocbase_col_t* col; // create a new proxy col = TRI_Allocate(sizeof(TRI_vocbase_col_t)); col->_vocbase = vocbase; col->_type = type; TRI_CopyString(col->_name, name, sizeof(col->_name)); col->_path = (path == NULL ? NULL : TRI_DuplicateString(path)); col->_collection = NULL; col->_newBorn = 0; col->_loaded = 0; col->_corrupted = 0; col->_cid = cid; // check name found = TRI_InsertKeyAssociativePointer(&vocbase->_collectionsByName, name, col, false); if (found != NULL) { TRI_Free(col); LOG_ERROR("duplicate entry for name '%s'", name); return NULL; } // check collection identifier if (cid != 0) { found = TRI_InsertKeyAssociativePointer(&vocbase->_collectionsById, &cid, col, false); if (found != NULL) { TRI_Free(col); LOG_ERROR("duplicate entry for identifier '%s'", cid); return NULL; } } TRI_PushBackVectorPointer(&vocbase->_collections, col); return col; } //////////////////////////////////////////////////////////////////////////////// /// @brief scans a directory and loads all collections //////////////////////////////////////////////////////////////////////////////// static void ScanPath (TRI_vocbase_t* vocbase, char const* path) { TRI_vector_string_t files; TRI_col_type_e type; size_t n; size_t i; files = TRI_FilesDirectory(path); n = files._length; for (i = 0; i < n; ++i) { char* name; char* file; name = files._buffer[i]; if (name[0] == '\0' || name[0] == '_' || name[0] == '.') { continue; } file = TRI_Concatenate2File(path, name); if (TRI_IsDirectory(file)) { TRI_col_info_t info; bool ok; ok = TRI_LoadParameterInfo(file, &info); if (! ok) { LOG_DEBUG("ignoring directory '%s' without valid parameter file '%s'", file, TRI_COL_PARAMETER_FILE); } else { type = info._type; if (type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { AddCollection(vocbase, type, info._name, info._cid, file); LOG_INFO("added simple document collection from '%s'", file); } else { LOG_DEBUG("skiping collection of unknown type %d", (int) type); } } } else { LOG_DEBUG("ignoring non-directory '%s'", file); } TRI_FreeString(file); } } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief page size //////////////////////////////////////////////////////////////////////////////// size_t PageSize; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief create a new tick //////////////////////////////////////////////////////////////////////////////// TRI_voc_tick_t TRI_NewTickVocBase () { uint64_t tick = ServerIdentifier; TRI_LockSpin(&TickLock); tick |= (++CurrentTick) << 16; TRI_UnlockSpin(&TickLock); return tick; } //////////////////////////////////////////////////////////////////////////////// /// @brief updates the tick counter //////////////////////////////////////////////////////////////////////////////// void TRI_UpdateTickVocBase (TRI_voc_tick_t tick) { TRI_voc_tick_t s = tick >> 16; TRI_LockSpin(&TickLock); if (CurrentTick < s) { CurrentTick = s; } TRI_UnlockSpin(&TickLock); } //////////////////////////////////////////////////////////////////////////////// /// @brief msyncs a memory block between begin (incl) and end (excl) //////////////////////////////////////////////////////////////////////////////// bool TRI_msync (int fd, char const* begin, char const* end) { intptr_t p = (intptr_t) begin; intptr_t q = (intptr_t) end; intptr_t g = (intptr_t) PageSize; char* b = (char*)( (p / g) * g ); char* e = (char*)( ((q + g - 1) / g) * g ); int res = msync(b, e - b, MS_SYNC); #ifdef __APPLE__ if (res == 0) { res = fcntl(fd, F_FULLFSYNC, 0); } #endif if (res == 0) { return true; } else { TRI_set_errno(TRI_ERROR_SYS_ERROR); return false; } } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief opens an exiting database, scans all collections //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_t* TRI_OpenVocBase (char const* path) { TRI_vocbase_t* vocbase; if (! TRI_IsDirectory(path)) { LOG_ERROR("database path '%s' is not a directory", path); return NULL; } // setup vocbase structure vocbase = TRI_Allocate(sizeof(TRI_vocbase_t)); vocbase->_path = TRI_DuplicateString(path); TRI_InitReadWriteLock(&vocbase->_lock); TRI_InitVectorPointer(&vocbase->_collections); TRI_InitAssociativePointer(&vocbase->_collectionsById, HashKeyCid, HashElementCid, EqualKeyCid, NULL); TRI_InitAssociativePointer(&vocbase->_collectionsByName, HashKeyCollectionName, HashElementCollectionName, EqualKeyCollectionName, NULL); // scan directory for collections ScanPath(vocbase, vocbase->_path); // vocbase is now active vocbase->_active = 1; // start synchroniser thread TRI_InitThread(&vocbase->_synchroniser); TRI_StartThread(&vocbase->_synchroniser, TRI_SynchroniserVocBase, vocbase); // start compactor thread TRI_InitThread(&vocbase->_compactor); TRI_StartThread(&vocbase->_compactor, TRI_CompactorVocBase, vocbase); // we are done return vocbase; } //////////////////////////////////////////////////////////////////////////////// /// @brief closes a database and all collections //////////////////////////////////////////////////////////////////////////////// void TRI_CloseVocBase (TRI_vocbase_t* vocbase) { vocbase->_active = 0; // TODO unload collections TRI_JoinThread(&vocbase->_synchroniser); TRI_JoinThread(&vocbase->_compactor); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a (document) collection by name //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_LookupCollectionByNameVocBase (TRI_vocbase_t* vocbase, char const* name) { return TRI_LookupByKeyAssociativePointer(&vocbase->_collectionsByName, name); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a (document) collection by identifier //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_LookupCollectionByIdVocBase (TRI_vocbase_t* vocbase, TRI_voc_cid_t id) { return TRI_LookupByKeyAssociativePointer(&vocbase->_collectionsById, &id); } //////////////////////////////////////////////////////////////////////////////// /// @brief finds up a (document) collection by name //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_FindCollectionByNameVocBase (TRI_vocbase_t* vocbase, char const* name, bool bear) { TRI_vocbase_col_t const* found; found = TRI_LookupCollectionByNameVocBase(vocbase, name); if (found != NULL) { return found; } if (! bear) { return NULL; } return TRI_BearCollectionVocBase(vocbase, name); } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a new (document) collection //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_CreateCollectionVocBase (TRI_vocbase_t* vocbase, TRI_col_parameter_t* parameter) { TRI_doc_collection_t* collection; TRI_vocbase_col_t* vc; TRI_col_type_e type; char const* name; char wrong; void const* found; TRI_WriteLockReadWriteLock(&vocbase->_lock); // check that we have a new name name = parameter->_name; found = TRI_LookupByKeyAssociativePointer(&vocbase->_collectionsByName, name); if (found != NULL) { LOG_ERROR("collection named '%s' already exists", name); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } // check that the name does not contain any strange characters wrong = IsAllowedCollectionName(name); if (wrong != 0) { LOG_ERROR("found illegal character in name: %c", wrong); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } // ok, construct the collection collection = NULL; type = parameter->_type; if (type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { TRI_sim_collection_t* sim; sim = TRI_CreateSimCollection(vocbase->_path, parameter); if (sim != NULL) { collection = &sim->base; } } else { LOG_ERROR("unknown collection type: %d", parameter->_type); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } if (collection == NULL) { TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } vc = AddCollection(vocbase, collection->base._type, collection->base._name, collection->base._cid, collection->base._directory); if (vc == NULL) { if (type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { TRI_CloseSimCollection((TRI_sim_collection_t*) collection); TRI_FreeSimCollection((TRI_sim_collection_t*) collection); } TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } vc->_collection = collection; vc->_loaded = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return vc; } //////////////////////////////////////////////////////////////////////////////// /// @brief loads an existing (document) collection //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_LoadCollectionVocBase (TRI_vocbase_t* vocbase, char const* name) { TRI_col_type_e type; union { TRI_vocbase_col_t const* c; TRI_vocbase_col_t* v; } found; TRI_WriteLockReadWriteLock(&vocbase->_lock); // check that we have an existing name found.c = TRI_LookupByKeyAssociativePointer(&vocbase->_collectionsByName, name); if (found.c == NULL) { LOG_ERROR("unknown collection '%s'", name); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } // check if the collection is not already loaded if (found.c->_loaded || found.c->_corrupted) { TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return found.c; } // load the collection type = found.c->_type; if (type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { TRI_sim_collection_t* collection; collection = TRI_OpenSimCollection(found.c->_path); if (collection == NULL) { found.v->_corrupted = 1; LOG_ERROR("cannot load collection '%s'", name); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } found.v->_collection = &collection->base; found.v->_loaded = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return found.c; } else { LOG_ERROR("unknown collection type %d for '%s'", (int) found.c->_type, name); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief reserves a new collection or returns an existing //////////////////////////////////////////////////////////////////////////////// TRI_vocbase_col_t const* TRI_BearCollectionVocBase (TRI_vocbase_t* vocbase, char const* name) { union { TRI_vocbase_col_t const* c; TRI_vocbase_col_t* v; } found; char wrong; TRI_WriteLockReadWriteLock(&vocbase->_lock); // check that we have an existing name found.c = TRI_LookupByKeyAssociativePointer(&vocbase->_collectionsByName, name); if (found.c != NULL) { return found.c; } // check that the name does not contain any strange characters wrong = IsAllowedCollectionName(name); if (wrong != 0) { LOG_ERROR("found illegal character in name: %c", wrong); TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return NULL; } // check if the collection is not already loaded found.v = AddCollection(vocbase, TRI_COL_TYPE_SIMPLE_DOCUMENT, name, 0, NULL); found.v->_newBorn = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return found.c; } //////////////////////////////////////////////////////////////////////////////// /// @brief manifests a new born collection //////////////////////////////////////////////////////////////////////////////// bool TRI_ManifestCollectionVocBase (TRI_vocbase_t* vocbase, TRI_vocbase_col_t const* vc) { union { TRI_vocbase_col_t* v; TRI_vocbase_col_t const* c; } cnv; TRI_col_type_e type; TRI_doc_collection_t* collection; TRI_WriteLockReadWriteLock(&vocbase->_lock); cnv.c = vc; // maybe the collection is already manifested if (! vc->_newBorn) { if (vc->_corrupted) { TRI_set_errno(TRI_VOC_ERROR_CORRUPTED_DATAFILE); return false; } if (! vc->_loaded) { TRI_set_errno(TRI_VOC_ERROR_CORRUPTED_DATAFILE); return false; } if (vc->_collection == NULL) { TRI_set_errno(TRI_VOC_ERROR_CORRUPTED_DATAFILE); return false; } return true; } // ok, construct the collection collection = NULL; type = vc->_type; if (type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { TRI_sim_collection_t* sim; TRI_col_parameter_t parameter; TRI_InitParameterCollection(¶meter, vc->_name, DEFAULT_MAXIMAL_SIZE); parameter._type = type; parameter._syncAfterTime = 1; sim = TRI_CreateSimCollection(vocbase->_path, ¶meter); if (sim != NULL) { collection = &sim->base; } } else { TRI_set_errno(TRI_VOC_ERROR_UNKNOWN_TYPE); cnv.v->_newBorn = 0; cnv.v->_corrupted = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); LOG_ERROR("unknown collection type: %d", vc->_type); return false; } if (collection == NULL) { TRI_set_errno(TRI_VOC_ERROR_CORRUPTED_DATAFILE); cnv.v->_newBorn = 0; cnv.v->_corrupted = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return false; } cnv.v->_collection = collection; cnv.v->_newBorn = 0; cnv.v->_loaded = 1; TRI_WriteUnlockReadWriteLock(&vocbase->_lock); return true; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- MODULE // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief initialises the voc database components //////////////////////////////////////////////////////////////////////////////// void TRI_InitialiseVocBase () { TRI_InitialiseHashes(); TRI_InitialiseRandom(); ServerIdentifier = TRI_UInt16Random(); PageSize = getpagesize(); TRI_InitSpin(&TickLock); // general errors TRI_set_errno_string(TRI_VOC_ERROR_ILLEGAL_STATE, "illegal state"); TRI_set_errno_string(TRI_VOC_ERROR_SHAPER_FAILED, "illegal shaper"); TRI_set_errno_string(TRI_VOC_ERROR_CORRUPTED_DATAFILE, "corrupted datafile"); TRI_set_errno_string(TRI_VOC_ERROR_MMAP_FAILED, "mmap failed"); TRI_set_errno_string(TRI_VOC_ERROR_MSYNC_FAILED, "msync failed"); TRI_set_errno_string(TRI_VOC_ERROR_NO_JOURNAL, "no journal"); TRI_set_errno_string(TRI_VOC_ERROR_DATAFILE_SEALED, "datafile sealed"); TRI_set_errno_string(TRI_VOC_ERROR_CORRUPTED_COLLECTION, "corrupted collection"); TRI_set_errno_string(TRI_VOC_ERROR_UNKNOWN_TYPE, "unknown type"); TRI_set_errno_string(TRI_VOC_ERROR_ILLEGAL_PARAMETER, "illegal paramater"); TRI_set_errno_string(TRI_VOC_ERROR_INDEX_EXISTS, "index exists"); TRI_set_errno_string(TRI_VOC_ERROR_CONFLICT, "conflict"); // open errors TRI_set_errno_string(TRI_VOC_ERROR_WRONG_PATH, "wrong path"); // close errors TRI_set_errno_string(TRI_VOC_ERROR_CANNOT_RENAME, "cannot rename"); // write errors TRI_set_errno_string(TRI_VOC_ERROR_WRITE_FAILED, "write failed"); TRI_set_errno_string(TRI_VOC_ERROR_READ_ONLY, "read only"); TRI_set_errno_string(TRI_VOC_ERROR_DATAFILE_FULL, "datafile full"); TRI_set_errno_string(TRI_VOC_ERROR_FILESYSTEM_FULL, "filesystem full"); // read errors TRI_set_errno_string(TRI_VOC_ERROR_READ_FAILED, "read failed"); TRI_set_errno_string(TRI_VOC_ERROR_FILE_NOT_FOUND, "file not found"); TRI_set_errno_string(TRI_VOC_ERROR_FILE_NOT_ACCESSIBLE, "file not accessible"); // document errors TRI_set_errno_string(TRI_VOC_ERROR_DOCUMENT_NOT_FOUND, "document not found"); } //////////////////////////////////////////////////////////////////////////////// /// @brief shut downs the voc database components //////////////////////////////////////////////////////////////////////////////// void TRI_ShutdownVocBase () { TRI_DestroySpin(&TickLock); TRI_ShutdownRandom(); TRI_ShutdownHashes(); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\)" // End: