diff --git a/Rest/JsonContainer.cpp b/Rest/JsonContainer.cpp new file mode 100644 index 0000000000..79b4884a10 --- /dev/null +++ b/Rest/JsonContainer.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief JSON data container +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2004-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 "Rest/JsonContainer.h" + +namespace triagens { + namespace rest { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a JSON data container +//////////////////////////////////////////////////////////////////////////////// + + JsonContainer::JsonContainer (TRI_json_t* data) : _data(data) { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a JSON container +//////////////////////////////////////////////////////////////////////////////// + + JsonContainer::~JsonContainer () { + if (_data) { + TRI_FreeJson(_data); + } + } + } +} diff --git a/Rest/JsonContainer.h b/Rest/JsonContainer.h new file mode 100644 index 0000000000..8287a30df6 --- /dev/null +++ b/Rest/JsonContainer.h @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief JSON data container +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2004-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 +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TRIAGENS_FYN_REST_JSON_CONTAINER_H +#define TRIAGENS_FYN_REST_JSON_CONTAINER_H 1 + +#include "BasicsC/json.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- class JsonContainer +// ----------------------------------------------------------------------------- + +namespace triagens { + namespace rest { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief JSON container +//////////////////////////////////////////////////////////////////////////////// + + class JsonContainer { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a JSON data container +//////////////////////////////////////////////////////////////////////////////// + + JsonContainer (TRI_json_t* data); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a JSON container +//////////////////////////////////////////////////////////////////////////////// + + ~JsonContainer (); + + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the json data +//////////////////////////////////////////////////////////////////////////////// + + TRI_json_t* _data; + + }; + } + +} + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/RestHandler/RestDocumentHandler.cpp b/RestHandler/RestDocumentHandler.cpp index fdbb80b851..612908ca4d 100644 --- a/RestHandler/RestDocumentHandler.cpp +++ b/RestHandler/RestDocumentHandler.cpp @@ -30,6 +30,7 @@ #include "Basics/StringUtils.h" #include "BasicsC/string-buffer.h" #include "Rest/HttpRequest.h" +#include "Rest/JsonContainer.h" #include "VocBase/simple-collection.h" #include "VocBase/vocbase.h" @@ -247,6 +248,9 @@ bool RestDocumentHandler::createDocument () { if (json == 0) { return false; } + + // auto-ptr that will free JSON data when scope is left + JsonContainer container(json); // find and load collection given by name oder identifier int res = useCollection(collection, create); @@ -274,7 +278,6 @@ bool RestDocumentHandler::createDocument () { // release collection and free json releaseCollection(); - TRI_FreeJson(json); // generate result if (mptr._did != 0) { @@ -659,6 +662,9 @@ bool RestDocumentHandler::updateDocument () { return false; } + // auto-ptr that will free JSON data when scope is left + JsonContainer container(json); + // extract document identifier TRI_voc_did_t did = StringUtils::uint64(didStr); @@ -695,7 +701,7 @@ bool RestDocumentHandler::updateDocument () { // release collection releaseCollection(); - + // generate result if (mptr._did != 0) { generateUpdated(cid, did, mptr._rid); diff --git a/VocBase/barrier.c b/VocBase/barrier.c index 2b3c5ef9cf..978589e57d 100644 --- a/VocBase/barrier.c +++ b/VocBase/barrier.c @@ -127,6 +127,9 @@ TRI_barrier_t* TRI_CreateBarrierDatafile (TRI_barrier_list_t* container, TRI_barrier_datafile_cb_t* element; element = TRI_Allocate(sizeof(TRI_barrier_datafile_cb_t)); + if (!element) { + return NULL; + } element->base._type = TRI_BARRIER_DATAFILE_CALLBACK; element->base._container = container; @@ -172,6 +175,9 @@ TRI_barrier_t* TRI_CreateBarrierCollection (TRI_barrier_list_t* container, TRI_barrier_collection_cb_t* element; element = TRI_Allocate(sizeof(TRI_barrier_collection_cb_t)); + if (!element) { + return NULL; + } element->base._type = TRI_BARRIER_COLLECTION_CALLBACK; element->base._container = container; diff --git a/VocBase/simple-collection.c b/VocBase/simple-collection.c index e6154a80a4..ad04c73f7e 100644 --- a/VocBase/simple-collection.c +++ b/VocBase/simple-collection.c @@ -1677,6 +1677,22 @@ static bool InitSimCollection (TRI_sim_collection_t* collection, TRI_shaper_t* shaper) { TRI_index_t* primary; char* id; + + // create primary index + primary = TRI_Allocate(sizeof(TRI_index_t)); + if (primary == NULL) { + TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + + return false; + } + + id = TRI_DuplicateString("_id"); + if (id == NULL) { + TRI_Free(primary); + TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + + return false; + } TRI_InitDocCollection(&collection->base, shaper); @@ -1706,22 +1722,6 @@ static bool InitSimCollection (TRI_sim_collection_t* collection, TRI_InitVectorPointer(&collection->_indexes); - // create primary index - primary = TRI_Allocate(sizeof(TRI_index_t)); - if (primary == NULL) { - TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); - - return false; - } - - id = TRI_DuplicateString("_id"); - if (id == NULL) { - TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); - TRI_Free(primary); - - return false; - } - TRI_InitVectorString(&primary->_fields); TRI_PushBackVectorString(&primary->_fields, id); @@ -1832,18 +1832,34 @@ TRI_sim_collection_t* TRI_CreateSimCollection (char const* path, //////////////////////////////////////////////////////////////////////////////// void TRI_DestroySimCollection (TRI_sim_collection_t* collection) { + size_t i; + size_t n; + TRI_DestroyCondition(&collection->_journalsCondition); TRI_DestroyAssociativePointer(&collection->_primaryIndex); + 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_DestroyVectorString(&idx->_fields); + } + // free index vector + TRI_DestroyVectorPointer(&collection->_indexes); if (collection->base._shaper != NULL) { TRI_FreeVocShaper(collection->base._shaper); } - + + /* FIXME: DestroyDocCollection does also free the shaper?? */ TRI_DestroyDocCollection(&collection->base); } diff --git a/VocBase/vocbase.c b/VocBase/vocbase.c index ea89ae9d2f..ff495b4cb5 100644 --- a/VocBase/vocbase.c +++ b/VocBase/vocbase.c @@ -288,9 +288,13 @@ static bool DropCollectionCallback (TRI_collection_t* col, void* data) { break; } } + + // we need to clean up the pointers later so we insert it into this vector + TRI_PushBackVectorPointer(&vocbase->_deadCollections, collection); TRI_WRITE_UNLOCK_COLLECTIONS_VOCBASE(vocbase); + // ............................................................................. // rename collection directory // ............................................................................. @@ -363,6 +367,29 @@ static bool DropCollectionCallback (TRI_collection_t* col, void* data) { /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief free the path buffer allocated for a collection +//////////////////////////////////////////////////////////////////////////////// + +static inline void FreeCollectionPath (TRI_vocbase_col_t* const collection) { + if (collection->_path) { + TRI_Free((char*) collection->_path); + } + collection->_path = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief free the memory associated with a collection +//////////////////////////////////////////////////////////////////////////////// + +static void FreeCollection (TRI_vocbase_t* vocbase, TRI_vocbase_col_t* collection) { + FreeCollectionPath(collection); + + TRI_DestroyReadWriteLock(&collection->_lock); + + TRI_Free(collection); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a new collection //////////////////////////////////////////////////////////////////////////////// @@ -386,9 +413,18 @@ static TRI_vocbase_col_t* AddCollection (TRI_vocbase_t* vocbase, collection->_vocbase = vocbase; collection->_type = type; TRI_CopyString(collection->_name, name, sizeof(collection->_name)); - collection->_path = (path == NULL ? NULL : TRI_DuplicateString(path)); - /* FIXME: memory allocation might fail */ - /* FIXME: collection->_path is never freed after being assigned to */ + if (path == NULL) { + collection->_path = NULL; + } + else { + collection->_path = TRI_DuplicateString(path); + if (!collection->_path) { + TRI_Free(collection); + TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + + return NULL; + } + } collection->_collection = NULL; collection->_status = TRI_VOC_COL_STATUS_CORRUPTED; @@ -398,6 +434,7 @@ static TRI_vocbase_col_t* AddCollection (TRI_vocbase_t* vocbase, found = TRI_InsertKeyAssociativePointer(&vocbase->_collectionsByName, name, collection, false); if (found != NULL) { + FreeCollectionPath(collection); TRI_Free(collection); LOG_ERROR("duplicate entry for name '%s'", name); @@ -411,6 +448,7 @@ static TRI_vocbase_col_t* AddCollection (TRI_vocbase_t* vocbase, if (found != NULL) { TRI_RemoveKeyAssociativePointer(&vocbase->_collectionsByName, name); + FreeCollectionPath(collection); TRI_Free(collection); LOG_ERROR("duplicate collection identifier '%lu' for name '%s'", (unsigned long) cid, name); @@ -455,6 +493,10 @@ static int ScanPath (TRI_vocbase_t* vocbase, char const* path) { } file = TRI_Concatenate2File(path, name); + if (!file) { + LOG_FATAL("out of memory"); + return TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); + } if (TRI_IsDirectory(file)) { TRI_col_info_t info; @@ -478,6 +520,9 @@ static int ScanPath (TRI_vocbase_t* vocbase, char const* path) { if (c == NULL) { LOG_FATAL("failed to add simple document collection from '%s'", file); + TRI_FreeString(file); + regfree(&re); + TRI_DestroyVectorString(&files); return TRI_set_errno(TRI_ERROR_AVOCADO_CORRUPTED_COLLECTION); } @@ -623,6 +668,7 @@ static int ManifestCollectionVocBase (TRI_vocbase_t* vocbase, TRI_vocbase_col_t* collection->_status = TRI_VOC_COL_STATUS_LOADED; collection->_collection = &sim->base; + FreeCollectionPath(collection); collection->_path = TRI_DuplicateString(sim->base.base._directory); TRI_WRITE_UNLOCK_STATUS_VOCBASE_COL(collection); @@ -738,6 +784,7 @@ static int LoadCollectionVocBase (TRI_vocbase_t* vocbase, TRI_vocbase_col_t* col collection->_collection = &sim->base; collection->_status = TRI_VOC_COL_STATUS_LOADED; + FreeCollectionPath(collection); collection->_path = TRI_DuplicateString(sim->base.base._directory); // release the WRITE lock and try again @@ -963,6 +1010,7 @@ TRI_vocbase_t* TRI_OpenVocBase (char const* path) { } TRI_InitVectorPointer(&vocbase->_collections); + TRI_InitVectorPointer(&vocbase->_deadCollections); TRI_InitAssociativePointer(&vocbase->_collectionsById, HashKeyCid, @@ -988,6 +1036,7 @@ TRI_vocbase_t* TRI_OpenVocBase (char const* path) { TRI_DestroyAssociativePointer(&vocbase->_collectionsByName); TRI_DestroyAssociativePointer(&vocbase->_collectionsById); TRI_DestroyVectorPointer(&vocbase->_collections); + TRI_DestroyVectorPointer(&vocbase->_deadCollections); TRI_DestroyLockFile(vocbase->_lockFile); TRI_FreeString(vocbase->_lockFile); TRI_FreeShadowStore(vocbase->_cursors); @@ -1032,22 +1081,39 @@ void TRI_DestroyVocBase (TRI_vocbase_t* vocbase) { // stop synchroniser and compactor TRI_JoinThread(&vocbase->_synchroniser); TRI_JoinThread(&vocbase->_compactor); + + // free dead collections (already dropped but pointers still around) + for (i = 0; i < vocbase->_deadCollections._length; ++i) { + TRI_vocbase_col_t* collection; + + collection = (TRI_vocbase_col_t*) vocbase->_deadCollections._buffer[i]; + FreeCollection(vocbase, collection); + } - // clear the hashs and vectors + // free collections + for (i = 0; i < vocbase->_collections._length; ++i) { + TRI_vocbase_col_t* collection; + + collection = (TRI_vocbase_col_t*) vocbase->_collections._buffer[i]; + FreeCollection(vocbase, collection); + } + + // clear the hashes and vectors TRI_DestroyAssociativePointer(&vocbase->_collectionsByName); TRI_DestroyAssociativePointer(&vocbase->_collectionsById); TRI_DestroyVectorPointer(&vocbase->_collections); + TRI_DestroyVectorPointer(&vocbase->_deadCollections); // free query functions TRI_FreeQueryFunctions(vocbase->_functions); + + // free the cursors + TRI_FreeShadowStore(vocbase->_cursors); // release lock on database TRI_DestroyLockFile(vocbase->_lockFile); TRI_FreeString(vocbase->_lockFile); - // free the cursors - TRI_FreeShadowStore(vocbase->_cursors); - // destroy lock TRI_DestroyReadWriteLock(&vocbase->_lock); } @@ -1218,6 +1284,7 @@ TRI_vocbase_col_t* TRI_CreateCollectionVocBase (TRI_vocbase_t* vocbase, TRI_col_ collection->_status = TRI_VOC_COL_STATUS_LOADED; collection->_collection = doc; + FreeCollectionPath(collection); collection->_path = TRI_DuplicateString(doc->base._directory); TRI_WRITE_UNLOCK_COLLECTIONS_VOCBASE(vocbase); diff --git a/VocBase/vocbase.h b/VocBase/vocbase.h index 789b3ca32b..991433497e 100644 --- a/VocBase/vocbase.h +++ b/VocBase/vocbase.h @@ -271,6 +271,7 @@ typedef struct TRI_vocbase_s { TRI_read_write_lock_t _lock; TRI_vector_pointer_t _collections; + TRI_vector_pointer_t _deadCollections; // pointers to collections dropped that can be removed later TRI_associative_pointer_t _collectionsByName; TRI_associative_pointer_t _collectionsById; diff --git a/js/actions/system/api-database.js b/js/actions/system/api-database.js new file mode 100644 index 0000000000..a17f455016 --- /dev/null +++ b/js/actions/system/api-database.js @@ -0,0 +1,261 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief querying and managing collections +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 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 Achim Brandt +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var actions = require("actions"); +var API = "_api/database/"; + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup AvocadoAPI +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @fn JSA_GET_api_datebase_collections +/// @brief returns all collections +/// +/// @REST{GET /_api/database/collections} +/// +/// Returns all collections. The result is a list of objects with the following +/// attributes: +/// +/// @FA{id} +/// +/// The identifier of the collection. +/// +/// @FA{name} +/// +/// The name of the collection. +/// +/// @EXAMPLES +/// +/// @verbinclude api_database1 +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url : API + "collections", + context : "api", + + callback : function (req, res) { + if (req.requestType != actions.GET) { + actions.resultUnsupported(req, res); + } + else { + var collections = db._collections(); + var result = []; + + for (var i = 0; i < collections.length; ++i) { + collection = collections[i]; + + result.push({ id : collection._id, name : collection.name() }); + } + + actions.result(req, res, actions.HTTP_OK, result); + } + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns information about a collection +/// +/// @REST{GET /_api/database/collection/@FA{collection-identifier}} +/// +/// The result is an objects with the following attributes: +/// +/// @FA{id} +/// +/// The identifier of the collection. +/// +/// @FA{name} +/// +/// The name of the collection. +/// +/// @EXAMPLES +/// +/// Using a name: +/// +/// @verbinclude api_database2 +/// +/// Using an identifier: +/// +/// @verbinclude api_database3 +//////////////////////////////////////////////////////////////////////////////// + +function GET_api_database_collection (req, res) { + if (req.suffix.length != 1) { + actions.collectionUnknown(req, res); + } + else { + var name = req.suffix[0]; + var id = parseInt(name); + + if (id != NaN) { + name = id; + } + + var collection = db._collection(name); + + if (collection == null) { + actions.collectionUnknown(req, res, name); + } + else { + var result = {}; + + result.id = collection._id; + result.name = collection.name(); + + actions.resultOk(req, res, actions.HTTP_OK, result); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a new collection +/// +/// @REST{POST /_api/database/collection} +/// +/// Creates a new collection. If the collection could be create, a @LIT{HTTP 200} +/// is returned. If the collection already exists, a @LIT{HTTP 409} is +/// returned. +/// +/// The call expects a JSON hash array as body with the following +/// attributes: +/// +/// @FA{name} +/// +/// The name of the collection. +/// +/// @FA{waitForSync} (optional, default true) +/// +/// If @FA{waitForSync} is false, then creation of documents will not wait +/// for the synchronization to file. +/// +/// In case of success, returns information about the created collection: +/// +/// @FA{id} +/// +/// The identifier of the collection. +/// +/// @FA{name} +/// +/// The name of the collection. +/// +/// @EXAMPLES +/// +/// Create a collection named test: +/// +/// @verbinclude api_database4 +/// +/// Try it again: +/// +/// @verbinclude api_database5 +//////////////////////////////////////////////////////////////////////////////// + +function POST_api_database_collection (req, res) { + var body = JSON.parse(req.requestBody || "{}"); + var name = body.name; + var waitForSync = true; + + if (body.hasOwnProperty("waitForSync")) { + waitForSync = body.waitForSync; + } + + if (name == null) { + badParameter(req, res, "name"); + } + else { + var collection = db._collection(name); + + if (collection != null) { + actions.error(req, res, + actions.HTTP_CONFLICT, + actions.VERR_COLLECTION_EXISTS, + "collection already exists", + undefined, + { name : collection.name(), id : collection._id }); + } + else { + collection = db[name]; + + if (collection == null) { + actions.badParameter(req, res, "cannot create collection named '" + name + "'"); + } + else { + if (collection._id == 0) { + collection.load(); + } + + if (collection._id == 0) { + actions.badParameter(req, res, "cannot create collection named '" + name + "'"); + } + else { + var result = {}; + + result.id = collection._id; + result.name = collection.name(); + + collection.parameter({ waitForSync : waitForSync }); + + actions.resultOk(req, res, actions.HTTP_OK, result); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief reads or creates a collection +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url : API + "collection", + context : "api", + + callback : function (req, res) { + if (req.requestType == actions.GET) { + GET_api_database_collection(req, res); + } + else if (req.requestType == actions.POST) { + POST_api_database_collection(req, res); + } + else { + actions.resultUnsupported(req, res); + } + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: