diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index 4ed6e210b5..d36faa5b91 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -234,15 +234,21 @@ AstNode* Ast::createNodeInsert (AstNode const* expression, AstNode* Ast::createNodeUpdate (AstNode const* keyExpression, AstNode const* docExpression, AstNode const* collection, - AstNode* options) { + AstNode const* options) { AstNode* node = createNode(NODE_TYPE_UPDATE); + + if (options == nullptr) { + // no options given. now use default options + options = &NopNode; + } + + node->addMember(options); node->addMember(collection); node->addMember(docExpression); if (keyExpression != nullptr) { node->addMember(keyExpression); } - // TODO: handle options return node; } @@ -254,15 +260,21 @@ AstNode* Ast::createNodeUpdate (AstNode const* keyExpression, AstNode* Ast::createNodeReplace (AstNode const* keyExpression, AstNode const* docExpression, AstNode const* collection, - AstNode* options) { + AstNode const* options) { AstNode* node = createNode(NODE_TYPE_REPLACE); + + if (options == nullptr) { + // no options given. now use default options + options = &NopNode; + } + + node->addMember(options); node->addMember(collection); node->addMember(docExpression); if (keyExpression != nullptr) { node->addMember(keyExpression); } - // TODO: handle options return node; } diff --git a/arangod/Aql/Ast.h b/arangod/Aql/Ast.h index 126feffa9c..f4e7364b07 100644 --- a/arangod/Aql/Ast.h +++ b/arangod/Aql/Ast.h @@ -262,7 +262,7 @@ namespace triagens { AstNode* createNodeUpdate (AstNode const*, AstNode const*, AstNode const*, - AstNode*); + AstNode const*); //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST replace node @@ -271,7 +271,7 @@ namespace triagens { AstNode* createNodeReplace (AstNode const*, AstNode const*, AstNode const*, - AstNode*); + AstNode const*); //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST collect node diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 6314ffe32c..26e743c0b2 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -27,6 +27,7 @@ #include "Aql/ExecutionBlock.h" #include "Basics/StringUtils.h" +#include "Basics/json-utilities.h" #include "Utils/Exception.h" #include "VocBase/vocbase.h" @@ -1877,25 +1878,25 @@ AqlItemBlock* ReturnBlock::getSome (size_t atLeast, } // ----------------------------------------------------------------------------- -// --SECTION-- class RemoveBlock +// --SECTION-- class ModificationBlock // ----------------------------------------------------------------------------- -RemoveBlock::RemoveBlock (AQL_TRANSACTION_V8* trx, - RemoveNode const* ep) +ModificationBlock::ModificationBlock (AQL_TRANSACTION_V8* trx, + ModificationNode const* ep) : ExecutionBlock(trx, ep), _collection(ep->_collection) { } -RemoveBlock::~RemoveBlock () { +ModificationBlock::~ModificationBlock () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief get some - this accumulates all input and calls the remove() method +/// @brief get some - this accumulates all input and calls the work() method //////////////////////////////////////////////////////////////////////////////// -AqlItemBlock* RemoveBlock::getSome (size_t atLeast, - size_t atMost) { +AqlItemBlock* ModificationBlock::getSome (size_t atLeast, + size_t atMost) { std::vector blocks; @@ -1919,7 +1920,7 @@ AqlItemBlock* RemoveBlock::getSome (size_t atLeast, blocks.push_back(res); } - remove(blocks); + work(blocks); freeBlocks(blocks); return nullptr; @@ -1930,11 +1931,73 @@ AqlItemBlock* RemoveBlock::getSome (size_t atLeast, } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief resolve a collection name and return cid and document key +//////////////////////////////////////////////////////////////////////////////// + +int ModificationBlock::resolve (char const* handle, + TRI_voc_cid_t& cid, + std::string& key) const { + char const* p = strchr(handle, TRI_DOCUMENT_HANDLE_SEPARATOR_CHR); + if (p == nullptr || *p == '\0') { + return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; + } + + if (*handle >= '0' && *handle <= '9') { + cid = triagens::basics::StringUtils::uint64(handle, p - handle); + } + else { + std::string const name(handle, p - handle); + cid = _trx->resolver()->getCollectionIdCluster(name); + } + + if (cid == 0) { + return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND; + } + + key = std::string(p + 1); + + return TRI_ERROR_NO_ERROR; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extract a key from the AqlValue passed +//////////////////////////////////////////////////////////////////////////////// + +int ModificationBlock::extractKey (AqlValue const& value, + TRI_document_collection_t const* document, + std::string& key) const { + Json member(value.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_KEY)); + + // TODO: allow _id, too + + TRI_json_t const* json = member.json(); + if (TRI_IsStringJson(json)) { + key = std::string(json->_value._string.data, json->_value._string.length - 1); + return TRI_ERROR_NO_ERROR; + } + + return TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- class RemoveBlock +// ----------------------------------------------------------------------------- + +RemoveBlock::RemoveBlock (AQL_TRANSACTION_V8* trx, + RemoveNode const* ep) + : ModificationBlock(trx, ep) { +} + + +RemoveBlock::~RemoveBlock () { +} + //////////////////////////////////////////////////////////////////////////////// /// @brief the actual work horse for removing data //////////////////////////////////////////////////////////////////////////////// -void RemoveBlock::remove (std::vector& blocks) { +void RemoveBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); auto it = _varOverview->varInfo.find(ep->_inVariable->id); TRI_ASSERT(it != _varOverview->varInfo.end()); @@ -1949,27 +2012,19 @@ void RemoveBlock::remove (std::vector& blocks) { for (auto it = blocks.begin(); it != blocks.end(); ++it) { auto res = (*it); auto document = res->getDocumentCollection(registerId); - + size_t const n = res->size(); // loop over the complete block for (size_t i = 0; i < n; ++i) { AqlValue a = res->getValue(i, registerId); - char const* key = nullptr; + std::string key; int errorCode = TRI_ERROR_NO_ERROR; if (a.isArray()) { // value is an array. now extract the _key attribute - Json member(a.extractArrayMember(_trx, document, "_key")); - - TRI_json_t const* json = member.json(); - if (TRI_IsStringJson(json)) { - key = json->_value._string.data; - } - else { - errorCode = TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING; - } + errorCode = extractKey(a, document, key); } else if (a.isString()) { // value is a string @@ -1981,10 +2036,10 @@ void RemoveBlock::remove (std::vector& blocks) { if (errorCode == TRI_ERROR_NO_ERROR) { // no error. we expect to have a key - TRI_ASSERT(key != nullptr); - + + // all exceptions are caught in _trx->remove() errorCode = _trx->remove(trxCollection, - std::string(key), + key, 0, TRI_DOC_UPDATE_LAST_WRITE, 0, @@ -2013,59 +2068,17 @@ void RemoveBlock::remove (std::vector& blocks) { InsertBlock::InsertBlock (AQL_TRANSACTION_V8* trx, InsertNode const* ep) - : ExecutionBlock(trx, ep), - _collection(ep->_collection) { + : ModificationBlock(trx, ep) { } - InsertBlock::~InsertBlock () { } -//////////////////////////////////////////////////////////////////////////////// -/// @brief get some - this accumulates all input and calls the insert() method -//////////////////////////////////////////////////////////////////////////////// - -AqlItemBlock* InsertBlock::getSome (size_t atLeast, - size_t atMost) { - - std::vector blocks; - - auto freeBlocks = [](std::vector& blocks) { - for (auto it = blocks.begin(); it != blocks.end(); ++it) { - if ((*it) != nullptr) { - delete (*it); - } - } - }; - - // loop over input until it is exhausted - try { - while (true) { - auto res = ExecutionBlock::getSome(atLeast, atMost); - - if (res == nullptr) { - break; - } - - blocks.push_back(res); - } - - insert(blocks); - freeBlocks(blocks); - - return nullptr; - } - catch (...) { - freeBlocks(blocks); - throw; - } -} - //////////////////////////////////////////////////////////////////////////////// /// @brief the actual work horse for inserting data //////////////////////////////////////////////////////////////////////////////// -void InsertBlock::insert (std::vector& blocks) { +void InsertBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); auto it = _varOverview->varInfo.find(ep->_inVariable->id); TRI_ASSERT(it != _varOverview->varInfo.end()); @@ -2075,30 +2088,6 @@ void InsertBlock::insert (std::vector& blocks) { bool const isEdgeCollection = _collection->isEdgeCollection(); - auto resolve = [&](char const* handle, TRI_voc_cid_t& cid, std::string& key) -> int { - char const* p = strchr(handle, TRI_DOCUMENT_HANDLE_SEPARATOR_CHR); - if (p == nullptr || *p == '\0') { - return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; - } - - if (*handle >= '0' && *handle <= '9') { - cid = triagens::basics::StringUtils::uint64(handle, p - handle); - } - else { - std::string const name(handle, p - handle); - cid = _trx->resolver()->getCollectionIdCluster(name); - } - - if (cid == 0) { - return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND; - } - - key = std::string(p + 1); - - return TRI_ERROR_NO_ERROR; - }; - - if (ep->_outVariable == nullptr) { // don't return anything @@ -2116,7 +2105,7 @@ void InsertBlock::insert (std::vector& blocks) { for (auto it = blocks.begin(); it != blocks.end(); ++it) { auto res = (*it); auto document = res->getDocumentCollection(registerId); - + size_t const n = res->size(); // loop over the complete block @@ -2129,10 +2118,10 @@ void InsertBlock::insert (std::vector& blocks) { // value is an array if (isEdgeCollection) { - // array must have "_from" and "_to" + // array must have _from and _to attributes TRI_json_t const* json; - Json member(a.extractArrayMember(_trx, document, "_from")); + Json member(a.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_FROM)); json = member.json(); if (TRI_IsStringJson(json)) { @@ -2143,7 +2132,7 @@ void InsertBlock::insert (std::vector& blocks) { } if (errorCode == TRI_ERROR_NO_ERROR) { - Json member(a.extractArrayMember(_trx, document, "_to")); + Json member(a.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_TO)); json = member.json(); if (TRI_IsStringJson(json)) { errorCode = resolve(json->_value._string.data, edge._toCid, to); @@ -2188,6 +2177,177 @@ void InsertBlock::insert (std::vector& blocks) { } } +// ----------------------------------------------------------------------------- +// --SECTION-- class UpdateBlock +// ----------------------------------------------------------------------------- + +UpdateBlock::UpdateBlock (AQL_TRANSACTION_V8* trx, + UpdateNode const* ep) + : ModificationBlock(trx, ep) { +} + +UpdateBlock::~UpdateBlock () { +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the actual work horse for inserting data +//////////////////////////////////////////////////////////////////////////////// + +void UpdateBlock::work (std::vector& blocks) { + auto ep = static_cast(getPlanNode()); + auto it = _varOverview->varInfo.find(ep->_inVariable->id); + TRI_ASSERT(it != _varOverview->varInfo.end()); + RegisterId const registerId = it->second.registerId; + + auto trxCollection = _trx->trxCollection(_collection->cid()); + + if (ep->_outVariable == nullptr) { + // don't return anything + + // loop over all blocks + for (auto it = blocks.begin(); it != blocks.end(); ++it) { + auto res = (*it); + auto document = res->getDocumentCollection(registerId); + + size_t const n = res->size(); + + // loop over the complete block + for (size_t i = 0; i < n; ++i) { + AqlValue a = res->getValue(i, registerId); + + int errorCode = TRI_ERROR_NO_ERROR; + std::string key; + + if (a.isArray()) { + // value is an array. now extract the _key attribute + errorCode = extractKey(a, document, key); + } + else { + errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID; + } + + if (errorCode == TRI_ERROR_NO_ERROR) { + TRI_doc_mptr_copy_t mptr; + auto json = a.toJson(_trx, document); + + // read old document + TRI_doc_mptr_copy_t oldDocument; + errorCode = _trx->readSingle(trxCollection, &oldDocument, key); + + if (errorCode == TRI_ERROR_NO_ERROR) { + if (oldDocument.getDataPtr() != nullptr) { + TRI_shaped_json_t shapedJson; + TRI_EXTRACT_SHAPED_JSON_MARKER(shapedJson, oldDocument.getDataPtr()); // PROTECTED by trx here + TRI_json_t* old = TRI_JsonShapedJson(_collection->documentCollection()->getShaper(), &shapedJson); + + if (old != nullptr) { + TRI_json_t* patchedJson = TRI_MergeJson(TRI_UNKNOWN_MEM_ZONE, old, json.json(), ep->_options.nullMeansRemove); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, old); + + if (patchedJson != nullptr) { + // all exceptions are caught in _trx->update() + errorCode = _trx->update(trxCollection, key, 0, &mptr, patchedJson, TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, patchedJson); + } + else { + errorCode = TRI_ERROR_OUT_OF_MEMORY; + } + } + else { + errorCode = TRI_ERROR_OUT_OF_MEMORY; + } + } + else { + errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND; + } + } + } + + if (errorCode != TRI_ERROR_NO_ERROR && + ! ep->_options.ignoreErrors) { + THROW_ARANGO_EXCEPTION(errorCode); + } + } + // done with a block + + // now free it already + (*it) = nullptr; + delete res; + } + } +} + +// ----------------------------------------------------------------------------- +// --SECTION-- class ReplaceBlock +// ----------------------------------------------------------------------------- + +ReplaceBlock::ReplaceBlock (AQL_TRANSACTION_V8* trx, + ReplaceNode const* ep) + : ModificationBlock(trx, ep) { +} + +ReplaceBlock::~ReplaceBlock () { +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the actual work horse for replacing data +//////////////////////////////////////////////////////////////////////////////// + +void ReplaceBlock::work (std::vector& blocks) { + auto ep = static_cast(getPlanNode()); + auto it = _varOverview->varInfo.find(ep->_inVariable->id); + TRI_ASSERT(it != _varOverview->varInfo.end()); + RegisterId const registerId = it->second.registerId; + + auto trxCollection = _trx->trxCollection(_collection->cid()); + + if (ep->_outVariable == nullptr) { + // don't return anything + + // loop over all blocks + for (auto it = blocks.begin(); it != blocks.end(); ++it) { + auto res = (*it); + auto document = res->getDocumentCollection(registerId); + + size_t const n = res->size(); + + // loop over the complete block + for (size_t i = 0; i < n; ++i) { + AqlValue a = res->getValue(i, registerId); + + int errorCode = TRI_ERROR_NO_ERROR; + std::string key; + + if (a.isArray()) { + // value is an array. now extract the _key attribute + errorCode = extractKey(a, document, key); + } + else { + errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID; + } + + if (errorCode == TRI_ERROR_NO_ERROR) { + TRI_doc_mptr_copy_t mptr; + auto json = a.toJson(_trx, document); + + // all exceptions are caught in _trx->update() + errorCode = _trx->update(trxCollection, key, 0, &mptr, json.json(), TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync); + } + + if (errorCode != TRI_ERROR_NO_ERROR && + ! ep->_options.ignoreErrors) { + THROW_ARANGO_EXCEPTION(errorCode); + } + } + // done with a block + + // now free it already + (*it) = nullptr; + delete res; + } + } +} + // ----------------------------------------------------------------------------- // --SECTION-- struct ExecutionBlock::VarOverview // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index fcf5940cd6..6d62f5bdc2 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -1031,11 +1031,81 @@ namespace triagens { }; +// ----------------------------------------------------------------------------- +// --SECTION-- ModificationBlock +// ----------------------------------------------------------------------------- + + class ModificationBlock : public ExecutionBlock { + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + + ModificationBlock (AQL_TRANSACTION_V8*, ModificationNode const*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + virtual ~ModificationBlock (); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getSome +//////////////////////////////////////////////////////////////////////////////// + + virtual AqlItemBlock* getSome (size_t atLeast, + size_t atMost); + +// ----------------------------------------------------------------------------- +// --SECTION-- protected methods +// ----------------------------------------------------------------------------- + + protected: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the actual work horse +//////////////////////////////////////////////////////////////////////////////// + + virtual void work (std::vector&) = 0; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief resolve a collection name and return cid and document key +//////////////////////////////////////////////////////////////////////////////// + + int resolve (char const*, + TRI_voc_cid_t&, + std::string&) const; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extract a key from the AqlValue passed +//////////////////////////////////////////////////////////////////////////////// + + int extractKey (AqlValue const&, + TRI_document_collection_t const*, + std::string&) const; + +// ----------------------------------------------------------------------------- +// --SECTION-- protected variables +// ----------------------------------------------------------------------------- + + protected: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief collection +//////////////////////////////////////////////////////////////////////////////// + + Collection* _collection; + + }; + + // ----------------------------------------------------------------------------- // --SECTION-- RemoveBlock // ----------------------------------------------------------------------------- - class RemoveBlock : public ExecutionBlock { + class RemoveBlock : public ModificationBlock { public: @@ -1051,36 +1121,17 @@ namespace triagens { ~RemoveBlock (); -//////////////////////////////////////////////////////////////////////////////// -/// @brief getSome -//////////////////////////////////////////////////////////////////////////////// - - virtual AqlItemBlock* getSome (size_t atLeast, - size_t atMost); - // ----------------------------------------------------------------------------- -// --SECTION-- private methods +// --SECTION-- protected methods // ----------------------------------------------------------------------------- - private: + protected: //////////////////////////////////////////////////////////////////////////////// /// @brief the actual work horse for removing data //////////////////////////////////////////////////////////////////////////////// - void remove (std::vector&); - -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - - private: - -//////////////////////////////////////////////////////////////////////////////// -/// @brief collection -//////////////////////////////////////////////////////////////////////////////// - - Collection* _collection; + void work (std::vector&); }; @@ -1088,7 +1139,7 @@ namespace triagens { // --SECTION-- InsertBlock // ----------------------------------------------------------------------------- - class InsertBlock : public ExecutionBlock { + class InsertBlock : public ModificationBlock { public: @@ -1104,36 +1155,85 @@ namespace triagens { ~InsertBlock (); -//////////////////////////////////////////////////////////////////////////////// -/// @brief getSome -//////////////////////////////////////////////////////////////////////////////// - - virtual AqlItemBlock* getSome (size_t atLeast, - size_t atMost); - // ----------------------------------------------------------------------------- -// --SECTION-- private methods +// --SECTION-- protected methods // ----------------------------------------------------------------------------- - private: + protected: //////////////////////////////////////////////////////////////////////////////// /// @brief the actual work horse for inserting data //////////////////////////////////////////////////////////////////////////////// - void insert (std::vector&); + void work (std::vector&); + + }; // ----------------------------------------------------------------------------- -// --SECTION-- private variables +// --SECTION-- UpdateBlock // ----------------------------------------------------------------------------- - private: + class UpdateBlock : public ModificationBlock { + + public: //////////////////////////////////////////////////////////////////////////////// -/// @brief collection +/// @brief constructor //////////////////////////////////////////////////////////////////////////////// - Collection* _collection; + UpdateBlock (AQL_TRANSACTION_V8* trx, UpdateNode const* ep); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + ~UpdateBlock (); + +// ----------------------------------------------------------------------------- +// --SECTION-- protected methods +// ----------------------------------------------------------------------------- + + protected: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the actual work horse for updating data +//////////////////////////////////////////////////////////////////////////////// + + void work (std::vector&); + + }; + +// ----------------------------------------------------------------------------- +// --SECTION-- ReplaceBlock +// ----------------------------------------------------------------------------- + + class ReplaceBlock : public ModificationBlock { + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + + ReplaceBlock (AQL_TRANSACTION_V8* trx, ReplaceNode const* ep); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + ~ReplaceBlock (); + +// ----------------------------------------------------------------------------- +// --SECTION-- protected methods +// ----------------------------------------------------------------------------- + + protected: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the actual work horse for replacing data +//////////////////////////////////////////////////////////////////////////////// + + void work (std::vector&); }; diff --git a/arangod/Aql/ExecutionEngine.cpp b/arangod/Aql/ExecutionEngine.cpp index c910888612..6242eb96ad 100644 --- a/arangod/Aql/ExecutionEngine.cpp +++ b/arangod/Aql/ExecutionEngine.cpp @@ -159,6 +159,20 @@ struct Instanciator : public WalkerWorker { root = eb; break; } + case ExecutionNode::UPDATE: { + eb = new UpdateBlock(engine->getTransaction(), + static_cast(en)); + + root = eb; + break; + } + case ExecutionNode::REPLACE: { + eb = new ReplaceBlock(engine->getTransaction(), + static_cast(en)); + + root = eb; + break; + } default: { THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 2ce01f502b..e54e9160de 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -294,7 +294,7 @@ void IndexRangeNode::toJsonHelper (std::map& indexTab, // put together the range info . . . Json ranges(Json::List); - +/* for (auto x : *_ranges) { Json item(Json::Array); item("name", Json(x._name)) @@ -304,7 +304,7 @@ void IndexRangeNode::toJsonHelper (std::map& indexTab, ("highOpen", Json(x._highOpen)); ranges(item); } - +*/ // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) @@ -594,8 +594,13 @@ void UpdateNode::toJsonHelper (std::map& indexTab, // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) - ("collection", Json(_collname)) - ("outVariable", _outVariable->toJson()); + ("collection", Json(_collection->name)) + ("inVariable", _inVariable->toJson()); + + // output variable might be empty + if (_outVariable != nullptr) { + json("outVariable", _outVariable->toJson()); + } // And add it: int len = static_cast(nodes.size()); @@ -622,8 +627,13 @@ void ReplaceNode::toJsonHelper (std::map& indexTab, // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) - ("collection", Json(_collname)) - ("outVariable", _outVariable->toJson()); + ("collection", Json(_collection->name)) + ("inVariable", _inVariable->toJson()); + + // output variable might be empty + if (_outVariable != nullptr) { + json("outVariable", _outVariable->toJson()); + } // And add it: int len = static_cast(nodes.size()); diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index b13de04eca..7743bf843f 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -1455,6 +1455,7 @@ namespace triagens { class ModificationNode : public ExecutionNode { friend class ExecutionBlock; + friend class ModificationBlock; //////////////////////////////////////////////////////////////////////////////// /// @brief constructor with a vocbase and a collection and options @@ -1728,7 +1729,7 @@ namespace triagens { /// @brief class UpdateNode //////////////////////////////////////////////////////////////////////////////// - class UpdateNode : public ExecutionNode { + class UpdateNode : public ModificationNode { friend class ExecutionBlock; friend class UpdateBlock; @@ -1740,13 +1741,16 @@ namespace triagens { public: UpdateNode (TRI_vocbase_t* vocbase, - std::string collname, + Collection* collection, + ModificationOptions const& options, + Variable const* inVariable, Variable const* outVariable) - : ExecutionNode(), _vocbase(vocbase), _collname(collname), + : ModificationNode(vocbase, collection, options), + _inVariable(inVariable), _outVariable(outVariable) { - TRI_ASSERT(_vocbase != nullptr); - TRI_ASSERT(_outVariable != nullptr); + TRI_ASSERT(_inVariable != nullptr); + // _outVariable might be a nullptr } //////////////////////////////////////////////////////////////////////////////// @@ -1770,7 +1774,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// virtual ExecutionNode* clone () const { - auto c = new UpdateNode(_vocbase, _collname, _outVariable); + auto c = new UpdateNode(_vocbase, _collection, _options, _inVariable, _outVariable); cloneDependencies(c); return static_cast(c); } @@ -1784,6 +1788,28 @@ namespace triagens { return 1000 * _dependencies.at(0)->getCost(); //FIXME change this! } +//////////////////////////////////////////////////////////////////////////////// +/// @brief getVariablesUsedHere +//////////////////////////////////////////////////////////////////////////////// + + virtual std::vector getVariablesUsedHere () { + std::vector v; + v.push_back(_inVariable); + return v; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getVariablesSetHere +//////////////////////////////////////////////////////////////////////////////// + + virtual std::vector getVariablesSetHere () { + std::vector v; + if (_outVariable != nullptr) { + v.push_back(_outVariable); + } + return v; + } + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -1791,16 +1817,10 @@ namespace triagens { private: //////////////////////////////////////////////////////////////////////////////// -/// @brief _vocbase, the database +/// @brief input variable //////////////////////////////////////////////////////////////////////////////// - TRI_vocbase_t* _vocbase; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief _collname, the collection name -//////////////////////////////////////////////////////////////////////////////// - - std::string _collname; + Variable const* _inVariable; //////////////////////////////////////////////////////////////////////////////// /// @brief output variable @@ -1818,7 +1838,7 @@ namespace triagens { /// @brief class ReplaceNode //////////////////////////////////////////////////////////////////////////////// - class ReplaceNode : public ExecutionNode { + class ReplaceNode : public ModificationNode { friend class ExecutionBlock; friend class ReplaceBlock; @@ -1830,13 +1850,16 @@ namespace triagens { public: ReplaceNode (TRI_vocbase_t* vocbase, - std::string collname, + Collection* collection, + ModificationOptions const& options, + Variable const* inVariable, Variable const* outVariable) - : ExecutionNode(), _vocbase(vocbase), _collname(collname), + : ModificationNode(vocbase, collection, options), + _inVariable(inVariable), _outVariable(outVariable) { - TRI_ASSERT(_vocbase != nullptr); - TRI_ASSERT(_outVariable != nullptr); + TRI_ASSERT(_inVariable != nullptr); + // _outVariable might be a nullptr } //////////////////////////////////////////////////////////////////////////////// @@ -1860,7 +1883,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// virtual ExecutionNode* clone () const { - auto c = new UpdateNode(_vocbase, _collname, _outVariable); + auto c = new ReplaceNode(_vocbase, _collection, _options, _inVariable, _outVariable); cloneDependencies(c); return static_cast(c); } @@ -1874,6 +1897,28 @@ namespace triagens { return 1000 * _dependencies.at(0)->getCost(); //FIXME change this! } +//////////////////////////////////////////////////////////////////////////////// +/// @brief getVariablesUsedHere +//////////////////////////////////////////////////////////////////////////////// + + virtual std::vector getVariablesUsedHere () { + std::vector v; + v.push_back(_inVariable); + return v; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getVariablesSetHere +//////////////////////////////////////////////////////////////////////////////// + + virtual std::vector getVariablesSetHere () { + std::vector v; + if (_outVariable != nullptr) { + v.push_back(_outVariable); + } + return v; + } + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -1881,16 +1926,10 @@ namespace triagens { private: //////////////////////////////////////////////////////////////////////////////// -/// @brief _vocbase, the database +/// @brief input variable //////////////////////////////////////////////////////////////////////////////// - TRI_vocbase_t* _vocbase; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief _collname, the collection name -//////////////////////////////////////////////////////////////////////////////// - - std::string _collname; + Variable const* _inVariable; //////////////////////////////////////////////////////////////////////////////// /// @brief output variable diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 96d5ca1dfd..6630deaad2 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -120,7 +120,6 @@ ModificationOptions ExecutionPlan::createOptions (AstNode const* node) { auto name = member->getStringValue(); auto value = member->getMember(0); -std::cout << "VALUE: " << value->typeString() << "\n"; TRI_ASSERT(value->isConstant()); if (strcmp(name, "waitForSync") == 0) { @@ -129,6 +128,10 @@ std::cout << "VALUE: " << value->typeString() << "\n"; else if (strcmp(name, "ignoreErrors") == 0) { options.ignoreErrors = value->toBoolean(); } + else if (strcmp(name, "keepNull") == 0) { + // nullMeansRemove is the opposite of keepNull + options.nullMeansRemove = (! value->toBoolean()); + } } } } @@ -597,26 +600,27 @@ ExecutionNode* ExecutionPlan::fromNodeUpdate (Ast const* ast, ExecutionNode* previous, AstNode const* node) { TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_UPDATE); - TRI_ASSERT(node->numMembers() >= 2); + TRI_ASSERT(node->numMembers() >= 3); - auto collection = node->getMember(0); - auto expression = node->getMember(1); - - // collection, expression - char const* collectionName = collection->getStringValue(); + auto options = createOptions(node->getMember(0)); + char const* collectionName = node->getMember(1)->getStringValue(); + auto collections = ast->query()->collections(); + auto collection = collections->get(collectionName); + auto expression = node->getMember(2); +// auto keyExpression = node->getMember(3); ExecutionNode* en = nullptr; if (expression->type == NODE_TYPE_REFERENCE) { // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new UpdateNode(ast->query()->vocbase(), std::string(collectionName), v)); + en = addNode(new UpdateNode(ast->query()->vocbase(), collection, options, v, nullptr)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new UpdateNode(ast->query()->vocbase(), std::string(collectionName), calc->outVariable())); + en = addNode(new UpdateNode(ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); previous = calc; } @@ -631,26 +635,27 @@ ExecutionNode* ExecutionPlan::fromNodeReplace (Ast const* ast, ExecutionNode* previous, AstNode const* node) { TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_REPLACE); - TRI_ASSERT(node->numMembers() >= 2); + TRI_ASSERT(node->numMembers() >= 3); - auto collection = node->getMember(0); - auto expression = node->getMember(1); - - // collection, expression - char const* collectionName = collection->getStringValue(); + auto options = createOptions(node->getMember(0)); + char const* collectionName = node->getMember(1)->getStringValue(); + auto collections = ast->query()->collections(); + auto collection = collections->get(collectionName); + auto expression = node->getMember(2); +// auto keyExpression = node->getMember(3); ExecutionNode* en = nullptr; - + if (expression->type == NODE_TYPE_REFERENCE) { // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new ReplaceNode(ast->query()->vocbase(), std::string(collectionName), v)); + en = addNode(new ReplaceNode(ast->query()->vocbase(), collection, options, v, nullptr)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new ReplaceNode(ast->query()->vocbase(), std::string(collectionName), calc->outVariable())); + en = addNode(new ReplaceNode(ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); previous = calc; } @@ -723,13 +728,11 @@ ExecutionNode* ExecutionPlan::fromNode (Ast const* ast, } case NODE_TYPE_UPDATE: { - THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); en = fromNodeUpdate(ast, en, member); break; } case NODE_TYPE_REPLACE: { - THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); en = fromNodeReplace(ast, en, member); break; } diff --git a/arangod/Aql/ModificationOptions.h b/arangod/Aql/ModificationOptions.h index 0f1c7b584f..7adb6e03e4 100644 --- a/arangod/Aql/ModificationOptions.h +++ b/arangod/Aql/ModificationOptions.h @@ -49,7 +49,8 @@ namespace triagens { ModificationOptions () : ignoreErrors(false), - waitForSync(false) { + waitForSync(false), + nullMeansRemove(false) { } // ----------------------------------------------------------------------------- @@ -58,6 +59,7 @@ namespace triagens { bool ignoreErrors; bool waitForSync; + bool nullMeansRemove; }; diff --git a/arangod/Aql/tokens.cpp b/arangod/Aql/tokens.cpp index d4bfd345ef..d1629a9548 100644 --- a/arangod/Aql/tokens.cpp +++ b/arangod/Aql/tokens.cpp @@ -637,8 +637,8 @@ namespace triagens { #define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \ size_t length = yyextra->remainingLength(); \ - if (length > maxBytesToRead) { \ - length = maxBytesToRead; \ + if (length > static_cast(maxBytesToRead)) { \ + length = static_cast(maxBytesToRead); \ } \ if (length > 0) { \ yyextra->fillBuffer(resultBuffer, length); \ diff --git a/arangod/Aql/tokens.ll b/arangod/Aql/tokens.ll index 728994d0c3..637466425b 100644 --- a/arangod/Aql/tokens.ll +++ b/arangod/Aql/tokens.ll @@ -38,8 +38,8 @@ namespace triagens { #define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \ size_t length = yyextra->remainingLength(); \ - if (length > maxBytesToRead) { \ - length = maxBytesToRead; \ + if (length > static_cast(maxBytesToRead)) { \ + length = static_cast(maxBytesToRead); \ } \ if (length > 0) { \ yyextra->fillBuffer(resultBuffer, length); \ diff --git a/arangod/Utils/Transaction.h b/arangod/Utils/Transaction.h index 259e8da680..10d5521931 100644 --- a/arangod/Utils/Transaction.h +++ b/arangod/Utils/Transaction.h @@ -495,6 +495,74 @@ namespace triagens { return res; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief update a single document, using JSON +//////////////////////////////////////////////////////////////////////////////// + + int update (TRI_transaction_collection_t* trxCollection, + std::string const& key, + TRI_voc_rid_t rid, + TRI_doc_mptr_copy_t* mptr, + TRI_json_t* const json, + TRI_doc_update_policy_e policy, + TRI_voc_rid_t expectedRevision, + TRI_voc_rid_t* actualRevision, + bool forceSync) { + + TRI_shaper_t* shaper = this->shaper(trxCollection); + TRI_memory_zone_t* zone = shaper->_memoryZone; + TRI_shaped_json_t* shaped = TRI_ShapedJsonJson(shaper, json, true, isLocked(trxCollection, TRI_TRANSACTION_WRITE)); + + if (shaped == nullptr) { + return TRI_ERROR_ARANGO_SHAPER_FAILED; + } + + if (orderBarrier(trxCollection) == nullptr) { + return TRI_ERROR_OUT_OF_MEMORY; + } + + int res = update(trxCollection, + key, + rid, + mptr, + shaped, + policy, + expectedRevision, + actualRevision, + forceSync); + + TRI_FreeShapedJson(zone, shaped); + return res; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief read a single document, identified by key +//////////////////////////////////////////////////////////////////////////////// + + int readSingle (TRI_transaction_collection_t* trxCollection, + TRI_doc_mptr_copy_t* mptr, + std::string const& key) { + + TRI_ASSERT(mptr != nullptr); + + if (orderBarrier(trxCollection) == nullptr) { + return TRI_ERROR_OUT_OF_MEMORY; + } + + try { + return TRI_ReadShapedJsonDocumentCollection(trxCollection, + (TRI_voc_key_t) key.c_str(), + mptr, + ! isLocked(trxCollection, TRI_TRANSACTION_READ)); + } + catch (triagens::arango::Exception const& ex) { + return ex.code(); + } + catch (...) { + return TRI_ERROR_INTERNAL; + } + } + // ----------------------------------------------------------------------------- // --SECTION-- protected methods // ----------------------------------------------------------------------------- @@ -696,34 +764,6 @@ namespace triagens { return TRI_ERROR_NO_ERROR; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief read a single document, identified by key -//////////////////////////////////////////////////////////////////////////////// - - int readSingle (TRI_transaction_collection_t* trxCollection, - TRI_doc_mptr_copy_t* mptr, - std::string const& key) { - - TRI_ASSERT(mptr != nullptr); - - if (orderBarrier(trxCollection) == nullptr) { - return TRI_ERROR_OUT_OF_MEMORY; - } - - try { - return TRI_ReadShapedJsonDocumentCollection(trxCollection, - (TRI_voc_key_t) key.c_str(), - mptr, - ! isLocked(trxCollection, TRI_TRANSACTION_READ)); - } - catch (triagens::arango::Exception const& ex) { - return ex.code(); - } - catch (...) { - return TRI_ERROR_INTERNAL; - } - } - //////////////////////////////////////////////////////////////////////////////// /// @brief read all documents //////////////////////////////////////////////////////////////////////////////// @@ -997,46 +1037,6 @@ namespace triagens { } } -//////////////////////////////////////////////////////////////////////////////// -/// @brief update a single document, using JSON -//////////////////////////////////////////////////////////////////////////////// - - int update (TRI_transaction_collection_t* trxCollection, - std::string const& key, - TRI_voc_rid_t rid, - TRI_doc_mptr_copy_t* mptr, - TRI_json_t* const json, - TRI_doc_update_policy_e policy, - TRI_voc_rid_t expectedRevision, - TRI_voc_rid_t* actualRevision, - bool forceSync) { - - TRI_shaper_t* shaper = this->shaper(trxCollection); - TRI_memory_zone_t* zone = shaper->_memoryZone; - TRI_shaped_json_t* shaped = TRI_ShapedJsonJson(shaper, json, true, isLocked(trxCollection, TRI_TRANSACTION_WRITE)); - - if (shaped == nullptr) { - return TRI_ERROR_ARANGO_SHAPER_FAILED; - } - - if (orderBarrier(trxCollection) == nullptr) { - return TRI_ERROR_OUT_OF_MEMORY; - } - - int res = update(trxCollection, - key, - rid, - mptr, - shaped, - policy, - expectedRevision, - actualRevision, - forceSync); - - TRI_FreeShapedJson(zone, shaped); - return res; - } - //////////////////////////////////////////////////////////////////////////////// /// @brief update a single document, using shaped json ////////////////////////////////////////////////////////////////////////////////