diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index 34bde2c389..8fbbe5e722 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -29,6 +29,7 @@ #include "Aql/AstNode.h" #include "Aql/Scopes.h" +#include "Basics/StringBuffer.h" using namespace triagens::aql; @@ -310,6 +311,105 @@ std::string AstNode::typeString () const { return ""; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify the node into a string buffer +//////////////////////////////////////////////////////////////////////////////// + +void AstNode::append (triagens::basics::StringBuffer* buffer) const { + if (type == NODE_TYPE_VALUE) { + appendValue(buffer); + return; + } + + if (type == NODE_TYPE_LIST) { + buffer->appendText("[ "); + size_t const n = numMembers(); + for (size_t i = 0; i < n; ++i) { + if (i > 0) { + buffer->appendText(", "); + } + + AstNode* member = getMember(i); + if (member != nullptr) { + member->append(buffer); + } + } + buffer->appendText(" ]"); + return; + } + + if (type == NODE_TYPE_ARRAY) { + buffer->appendText("{ "); + size_t const n = numMembers(); + for (size_t i = 0; i < n; ++i) { + if (i > 0) { + buffer->appendText(", "); + } + + AstNode* member = getMember(i); + if (member != nullptr) { + TRI_ASSERT(member->type == NODE_TYPE_ARRAY_ELEMENT); + TRI_ASSERT(member->numMembers() == 1); + + buffer->appendChar('"'); + buffer->appendJsonEncoded(member->getStringValue()); + buffer->appendText("\" : "); + + member->getMember(0)->append(buffer); + } + } + buffer->appendText(" }"); + return; + } +} + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify the value of a node into a string buffer +//////////////////////////////////////////////////////////////////////////////// + +void AstNode::appendValue (triagens::basics::StringBuffer* buffer) const { + TRI_ASSERT(type == NODE_TYPE_VALUE); + + switch (value.type) { + case VALUE_TYPE_BOOL: { + if (value.value._bool) { + buffer->appendText("true", 4); + } + else { + buffer->appendText("false", 5); + } + break; + } + + case VALUE_TYPE_INT: { + buffer->appendInteger(value.value._int); + break; + } + + case VALUE_TYPE_DOUBLE: { + buffer->appendDecimal(value.value._double); + break; + } + + case VALUE_TYPE_STRING: { + buffer->appendChar('"'); + buffer->appendJsonEncoded(value.value._string); + buffer->appendChar('"'); + break; + } + + case VALUE_TYPE_NULL: + default: { + buffer->appendText("null", 4); + break; + } + } +} + // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index 428efe7a68..a3b864202a 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -36,6 +36,10 @@ #include "Utils/Exception.h" namespace triagens { + namespace basics { + class StringBuffer; + } + namespace aql { //////////////////////////////////////////////////////////////////////////////// @@ -351,6 +355,22 @@ namespace triagens { std::string typeString () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify the node into a string buffer +//////////////////////////////////////////////////////////////////////////////// + + void append (triagens::basics::StringBuffer*) const; + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify the value of a node into a string buffer +//////////////////////////////////////////////////////////////////////////////// + + void appendValue (triagens::basics::StringBuffer*) const; + // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/Parser.cpp b/arangod/Aql/Parser.cpp index caa06dae8f..9748a65538 100644 --- a/arangod/Aql/Parser.cpp +++ b/arangod/Aql/Parser.cpp @@ -56,7 +56,7 @@ Parser::Parser (Query* query) Aqllex_init(&_scanner); Aqlset_extra(this, _scanner); - _ast = new QueryAst(this); + _ast = new QueryAst(query, this); _stack.reserve(16); } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 1a78d187fe..993b18793b 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -29,6 +29,7 @@ #include "Aql/Query.h" #include "Aql/Parser.h" +#include "Aql/V8Executor.h" #include "BasicsC/json.h" #include "Utils/Exception.h" #include "VocBase/vocbase.h" @@ -50,11 +51,14 @@ Query::Query (TRI_vocbase_t* vocbase, size_t queryLength, TRI_json_t* bindParameters) : _vocbase(vocbase), + _executor(nullptr), _queryString(queryString), _queryLength(queryLength), _type(AQL_QUERY_READ), _bindParameters(bindParameters), _error() { + + TRI_ASSERT(_vocbase != nullptr); } //////////////////////////////////////////////////////////////////////////////// @@ -62,6 +66,10 @@ Query::Query (TRI_vocbase_t* vocbase, //////////////////////////////////////////////////////////////////////////////// Query::~Query () { + if (_executor != nullptr) { + delete _executor; + _executor = nullptr; + } } // ----------------------------------------------------------------------------- @@ -209,6 +217,19 @@ ParseResult Query::parse () { void Query::explain () { } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get v8 executor +//////////////////////////////////////////////////////////////////////////////// + +V8Executor* Query::getExecutor () { + if (_executor == nullptr) { + _executor = new V8Executor; + } + + TRI_ASSERT(_executor != nullptr); + return _executor; +} + // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index 1cee13583e..4df5eb3115 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -42,6 +42,8 @@ struct TRI_vocbase_s; namespace triagens { namespace aql { + class V8Executor; + // ----------------------------------------------------------------------------- // --SECTION-- public types // ----------------------------------------------------------------------------- @@ -165,6 +167,12 @@ namespace triagens { void explain (); +//////////////////////////////////////////////////////////////////////////////// +/// @brief get v8 executor +//////////////////////////////////////////////////////////////////////////////// + + V8Executor* getExecutor (); + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- @@ -178,6 +186,8 @@ namespace triagens { private: struct TRI_vocbase_s* _vocbase; + + V8Executor* _executor; char const* _queryString; size_t const _queryLength; diff --git a/arangod/Aql/QueryAst.cpp b/arangod/Aql/QueryAst.cpp index 2ca7b9523c..e3e1954682 100644 --- a/arangod/Aql/QueryAst.cpp +++ b/arangod/Aql/QueryAst.cpp @@ -29,11 +29,22 @@ #include "Aql/QueryAst.h" #include "Aql/Parser.h" +#include "Aql/V8Executor.h" #include "BasicsC/tri-strings.h" #include "Utils/Exception.h" #include "VocBase/collection.h" using namespace triagens::aql; + +std::unordered_map const QueryAst::FunctionNames{ + { static_cast(NODE_TYPE_OPERATOR_BINARY_EQ), "EQUAL" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_NE), "UNEQUAL" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_GT), "GREATER" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_GE), "GREATEREQUAL" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_LT), "LESS" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_LE), "LESSEQUAL" }, + { static_cast(NODE_TYPE_OPERATOR_BINARY_IN), "IN" } +}; // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors @@ -43,8 +54,10 @@ using namespace triagens::aql; /// @brief create the AST //////////////////////////////////////////////////////////////////////////////// -QueryAst::QueryAst (Parser* parser) - : _parser(parser), +QueryAst::QueryAst (Query* query, + Parser* parser) + : _query(query), + _parser(parser), _nodes(), _strings(), _scopes(), @@ -55,6 +68,9 @@ QueryAst::QueryAst (Parser* parser) _writeOptions(nullptr), _nopNode() { + TRI_ASSERT(_query != nullptr); + TRI_ASSERT(_parser != nullptr); + _nodes.reserve(32); _root = createNode(NODE_TYPE_ROOT); } @@ -656,6 +672,7 @@ AstNode* QueryAst::createNodeRange (AstNode const* start, //////////////////////////////////////////////////////////////////////////////// AstNode* QueryAst::createNodeNop () { + // the nop node is a singleton inside a query if (_nopNode == nullptr) { _nopNode = createNode(NODE_TYPE_NOP); } @@ -787,6 +804,35 @@ void QueryAst::optimize () { // --SECTION-- private methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a comparison function using two constant values +//////////////////////////////////////////////////////////////////////////////// + +AstNode* QueryAst::executeConstComparison (std::string const& func, + AstNode const* lhs, + AstNode const* rhs) { + TRI_json_t* result = _query->getExecutor()->executeComparison(func, lhs, rhs); + + if (result == nullptr) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + + AstNode* node = nullptr; + try { + node = nodeFromJson(result); + } + catch (...) { + } + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, result); + + if (node == nullptr) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + + return node; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief optimizes a FILTER node //////////////////////////////////////////////////////////////////////////////// @@ -808,10 +854,12 @@ AstNode* QueryAst::optimizeFilter (AstNode* node) { } if (operand->getBoolValue()) { - // FILTER is always true, optimise it away + // FILTER is always true, optimize it away return createNodeNop(); } + // TODO: optimize FILTERs that are always false + return node; } @@ -970,13 +1018,29 @@ AstNode* QueryAst::optimizeBinaryOperatorRelational (AstNode* node) { if (lhs == nullptr || rhs == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } - /* + bool const lhsIsConst = lhs->isConstant(); bool const rhsIsConst = rhs->isConstant(); - // TODO -*/ - return node; + if (! lhsIsConst || ! rhsIsConst) { + return node; + } + + auto it = FunctionNames.find(static_cast(node->type)); + if (it == FunctionNames.end()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + + if (node->type == NODE_TYPE_OPERATOR_BINARY_IN && + rhs->type != NODE_TYPE_LIST) { + // right operand of IN must be a list + _parser->registerError(TRI_ERROR_QUERY_LIST_EXPECTED); + return node; + } + + std::string const& func((*it).second); + + return executeConstComparison(func, lhs, rhs); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/QueryAst.h b/arangod/Aql/QueryAst.h index 4814930e65..5b20189991 100644 --- a/arangod/Aql/QueryAst.h +++ b/arangod/Aql/QueryAst.h @@ -44,6 +44,7 @@ namespace triagens { namespace aql { class Parser; + class Query; // ----------------------------------------------------------------------------- // --SECTION-- class QueryAst @@ -65,7 +66,8 @@ namespace triagens { /// @brief create the AST //////////////////////////////////////////////////////////////////////////////// - QueryAst (Parser*); + QueryAst (Query*, + Parser*); //////////////////////////////////////////////////////////////////////////////// /// @brief destroy the AST @@ -410,6 +412,14 @@ namespace triagens { private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a comparison function using two constant values +//////////////////////////////////////////////////////////////////////////////// + + AstNode* executeConstComparison (std::string const&, + AstNode const*, + AstNode const*); + //////////////////////////////////////////////////////////////////////////////// /// @brief optimizes a FILTER node //////////////////////////////////////////////////////////////////////////////// @@ -486,6 +496,12 @@ namespace triagens { private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief the query +//////////////////////////////////////////////////////////////////////////////// + + Query* _query; + //////////////////////////////////////////////////////////////////////////////// /// @brief the query parser //////////////////////////////////////////////////////////////////////////////// @@ -546,6 +562,12 @@ namespace triagens { AstNode* _nopNode; +//////////////////////////////////////////////////////////////////////////////// +/// @brief AQL function names +//////////////////////////////////////////////////////////////////////////////// + + static std::unordered_map const FunctionNames; + }; } diff --git a/arangod/Aql/V8Executor.cpp b/arangod/Aql/V8Executor.cpp new file mode 100644 index 0000000000..4af34ba9ba --- /dev/null +++ b/arangod/Aql/V8Executor.cpp @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Aql, v8 context executor +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2014 ArangoDB GmbH, Cologne, Germany +/// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "Aql/V8Executor.h" +#include "Aql/AstNode.h" +#include "Basics/StringBuffer.h" +#include "Utils/Exception.h" +#include "V8/v8-conv.h" +#include "V8/v8-globals.h" + +using namespace triagens::aql; + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates an executor +//////////////////////////////////////////////////////////////////////////////// + +V8Executor::V8Executor () : + _buffer(nullptr) { +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys an executor +//////////////////////////////////////////////////////////////////////////////// + +V8Executor::~V8Executor () { + if (_buffer != nullptr) { + delete _buffer; + _buffer = nullptr; + } +} + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a comparison operation using V8 +//////////////////////////////////////////////////////////////////////////////// + +TRI_json_t* V8Executor::executeComparison (std::string const& func, + AstNode const* lhs, + AstNode const* rhs) { + + triagens::basics::StringBuffer* buffer = getBuffer(); + TRI_ASSERT(buffer != nullptr); + + _buffer->appendText("(function(){ var aql = require(\"org/arangodb/ahuacatl\"); return aql.RELATIONAL_"); + _buffer->appendText(func); + _buffer->appendText("("); + + lhs->append(buffer); + + _buffer->appendText(", "); + + rhs->append(buffer); + + _buffer->appendText("); })"); + + return execute(); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create the string buffer +//////////////////////////////////////////////////////////////////////////////// + +triagens::basics::StringBuffer* V8Executor::getBuffer () { + if (_buffer == nullptr) { + _buffer = new triagens::basics::StringBuffer(TRI_UNKNOWN_MEM_ZONE); + + if (_buffer == nullptr) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + + _buffer->reserve(256); + } + else { + _buffer->clear(); + } + + return _buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the contents of the string buffer +//////////////////////////////////////////////////////////////////////////////// + +TRI_json_t* V8Executor::execute () { + TRI_ASSERT(_buffer != nullptr); + + v8::Handle compiled = v8::Script::Compile(v8::String::New(_buffer->c_str(), (int) _buffer->length()), + v8::String::New("--script--")); + + if (compiled.IsEmpty()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + + v8::TryCatch tryCatch; + v8::Handle func = compiled->Run(); + + if (tryCatch.HasCaught()) { + if (tryCatch.CanContinue()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + v8g->_canceled = true; + + THROW_ARANGO_EXCEPTION(TRI_ERROR_REQUEST_CANCELED); + } + } + + if (func.IsEmpty() || ! func->IsFunction()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + + // execute the function + v8::Handle args[] = { }; + v8::Handle result = v8::Handle::Cast(func)->Call(v8::Object::New(), 0, args); + + if (tryCatch.HasCaught()) { + if (tryCatch.CanContinue()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + v8g->_canceled = true; + + THROW_ARANGO_EXCEPTION(TRI_ERROR_REQUEST_CANCELED); + } + } + + if (result.IsEmpty()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + + return TRI_ObjectToJson(result); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/Aql/V8Executor.h b/arangod/Aql/V8Executor.h new file mode 100644 index 0000000000..dd7479f629 --- /dev/null +++ b/arangod/Aql/V8Executor.h @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Aql, v8 context executor +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2014 ArangoDB GmbH, Cologne, Germany +/// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_AQL_V8_EXECUTOR_H +#define ARANGODB_AQL_V8_EXECUTOR_H 1 + +#include "Basics/Common.h" + +struct TRI_json_s; + +namespace triagens { + namespace basics { + class StringBuffer; + } + + namespace aql { + + struct AstNode; + +// ----------------------------------------------------------------------------- +// --SECTION-- class V8Executor +// ----------------------------------------------------------------------------- + + class V8Executor { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + + public: + + V8Executor (); + + ~V8Executor (); + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a comparison operation using V8 +//////////////////////////////////////////////////////////////////////////////// + + struct TRI_json_s* executeComparison (std::string const&, + AstNode const*, + AstNode const*); + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create the string buffer +//////////////////////////////////////////////////////////////////////////////// + + triagens::basics::StringBuffer* getBuffer (); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the contents of the string buffer +//////////////////////////////////////////////////////////////////////////////// + + struct TRI_json_s* execute (); + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief a string buffer used for operations +//////////////////////////////////////////////////////////////////////////////// + + triagens::basics::StringBuffer* _buffer; + + }; + + } +} + +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 697c38b975..3652cc3d34 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -67,6 +67,7 @@ add_executable( Aql/QueryAst.cpp Aql/Scopes.cpp Aql/tokens.cpp + Aql/V8Executor.cpp BitIndexes/bitarray.cpp BitIndexes/bitarrayIndex.cpp CapConstraint/cap-constraint.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files index 9ab84a9087..3400b49408 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -48,6 +48,7 @@ arangod_libarangod_a_SOURCES = \ arangod/Aql/QueryAst.cpp \ arangod/Aql/Scopes.cpp \ arangod/Aql/tokens.cpp \ + arangod/Aql/V8Executor.cpp \ arangod/BitIndexes/bitarray.cpp \ arangod/BitIndexes/bitarrayIndex.cpp \ arangod/CapConstraint/cap-constraint.cpp \ diff --git a/lib/Basics/StringBuffer.h b/lib/Basics/StringBuffer.h index 096ebd55a0..fb7a7bc0ba 100644 --- a/lib/Basics/StringBuffer.h +++ b/lib/Basics/StringBuffer.h @@ -111,7 +111,7 @@ namespace triagens { /// @brief ensure the string buffer has a specific capacity //////////////////////////////////////////////////////////////////////////////// - int reserve (const size_t length) { + int reserve (size_t length) { return TRI_ReserveStringBuffer(&_buffer, length); } @@ -445,6 +445,15 @@ namespace triagens { return *this; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief appends as json-encoded +//////////////////////////////////////////////////////////////////////////////// + + StringBuffer& appendJsonEncoded (const char * str) { + TRI_AppendJsonEncodedStringStringBuffer(&_buffer, str, true); + return *this; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief appends characters ////////////////////////////////////////////////////////////////////////////////