diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index 07208d1afe..a8dc8b1c4e 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -831,36 +831,40 @@ Json AqlValue::extractObjectMember (triagens::arango::AqlTransaction* trx, TRI_ASSERT(_marker != nullptr); // look for the attribute name in the shape - if (*name == '_') { - if (strcmp(name, TRI_VOC_ATTRIBUTE_KEY) == 0) { + if (*name == '_' && name[1] != '\0') { + if (name[1] == 'k' && strcmp(name, TRI_VOC_ATTRIBUTE_KEY) == 0) { // _key value is copied into JSON return Json(TRI_UNKNOWN_MEM_ZONE, TRI_EXTRACT_MARKER_KEY(_marker)); } - else if (strcmp(name, TRI_VOC_ATTRIBUTE_ID) == 0) { + if (name[1] == 'i' && strcmp(name, TRI_VOC_ATTRIBUTE_ID) == 0) { + // _id buffer.reset(); trx->resolver()->getCollectionName(document->_info._cid, buffer); buffer.appendChar('/'); buffer.appendText(TRI_EXTRACT_MARKER_KEY(_marker)); return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length()); } - else if (strcmp(name, TRI_VOC_ATTRIBUTE_REV) == 0) { + if (name[1] == 'r' && strcmp(name, TRI_VOC_ATTRIBUTE_REV) == 0) { + // _rev TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(_marker); buffer.reset(); buffer.appendInteger(rid); return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length()); } - else if (strcmp(name, TRI_VOC_ATTRIBUTE_FROM) == 0 && - (_marker->_type == TRI_DOC_MARKER_KEY_EDGE || - _marker->_type == TRI_WAL_MARKER_EDGE)) { + if (name[1] == 'f' && + strcmp(name, TRI_VOC_ATTRIBUTE_FROM) == 0 && + (_marker->_type == TRI_DOC_MARKER_KEY_EDGE || + _marker->_type == TRI_WAL_MARKER_EDGE)) { buffer.reset(); trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_FROM_CID(_marker), buffer); buffer.appendChar('/'); buffer.appendText(TRI_EXTRACT_MARKER_FROM_KEY(_marker)); return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length()); } - else if (strcmp(name, TRI_VOC_ATTRIBUTE_TO) == 0 && - (_marker->_type == TRI_DOC_MARKER_KEY_EDGE || - _marker->_type == TRI_WAL_MARKER_EDGE)) { + if (name[1] == 't' && + strcmp(name, TRI_VOC_ATTRIBUTE_TO) == 0 && + (_marker->_type == TRI_DOC_MARKER_KEY_EDGE || + _marker->_type == TRI_WAL_MARKER_EDGE)) { buffer.reset(); trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(_marker), buffer); buffer.appendChar('/'); diff --git a/arangod/Aql/AttributeAccessor.cpp b/arangod/Aql/AttributeAccessor.cpp new file mode 100644 index 0000000000..b523f28551 --- /dev/null +++ b/arangod/Aql/AttributeAccessor.cpp @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief AQL, specialized attribute accessor for expressions +/// +/// @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 "AttributeAccessor.h" +#include "Aql/Variable.h" +#include "Basics/StringBuffer.h" +#include "Basics/json.h" +#include "ShapedJson/shaped-json.h" +#include "VocBase/document-collection.h" + +using namespace triagens::aql; +using Json = triagens::basics::Json; + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create the accessor +//////////////////////////////////////////////////////////////////////////////// + +AttributeAccessor::AttributeAccessor (char const* name, + Variable const* variable) + : _name(name), + _variable(variable), + _buffer(TRI_UNKNOWN_MEM_ZONE) { + + TRI_ASSERT(_name != nullptr); + TRI_ASSERT(_variable != nullptr); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroy the accessor +//////////////////////////////////////////////////////////////////////////////// + +AttributeAccessor::~AttributeAccessor () { +} + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief execute the accessor +//////////////////////////////////////////////////////////////////////////////// + +AqlValue AttributeAccessor::get (triagens::arango::AqlTransaction* trx, + std::vector& docColls, + std::vector& argv, + size_t startPos, + std::vector const& vars, + std::vector const& regs) { + + size_t i = 0; + for (auto it = vars.begin(); it != vars.end(); ++it, ++i) { + if ((*it)->id == _variable->id) { + + // save the collection info + TRI_document_collection_t const* collection = docColls[regs[i]]; + + // get the AQL value + auto& result = argv[startPos + regs[i]]; + + // extract the attribute + auto j = result.extractObjectMember(trx, collection, _name, true, _buffer); + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal())); + } + // fall-through intentional + } + + return AqlValue(new Json(Json::Null)); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/Aql/AttributeAccessor.h b/arangod/Aql/AttributeAccessor.h new file mode 100644 index 0000000000..f8fccec5e5 --- /dev/null +++ b/arangod/Aql/AttributeAccessor.h @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief AQL, specialized attribute accessor for expressions +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-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 triAGENS GmbH, Cologne, Germany +/// +/// @author Max Neunhoeffer +/// @author Copyright 2014, triagens GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_AQL_ATTRIBUTE_ACCESSOR_H +#define ARANGODB_AQL_ATTRIBUTE_ACCESSOR_H 1 + +#include "Basics/Common.h" +#include "Aql/AqlValue.h" +#include "Aql/types.h" +#include "Basics/StringBuffer.h" +#include "Utils/AqlTransaction.h" + +struct TRI_document_collection_t; + +namespace triagens { + namespace aql { + class Variable; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief AttributeAccessor +//////////////////////////////////////////////////////////////////////////////// + + class AttributeAccessor { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + + AttributeAccessor (char const*, + Variable const*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + ~AttributeAccessor (); + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief execute the accessor +//////////////////////////////////////////////////////////////////////////////// + + AqlValue get (triagens::arango::AqlTransaction* trx, + std::vector&, + std::vector&, + size_t, + std::vector const&, + std::vector const&); + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the attribute name +//////////////////////////////////////////////////////////////////////////////// + + char const* _name; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the accessed variable +//////////////////////////////////////////////////////////////////////////////// + + Variable const* _variable; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief buffer for temporary strings +//////////////////////////////////////////////////////////////////////////////// + + triagens::basics::StringBuffer _buffer; + + }; + + } // namespace triagens::aql +} // namespace triagens + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index cae64394e0..3983a41972 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -30,15 +30,16 @@ #include "Aql/Expression.h" #include "Aql/AqlValue.h" #include "Aql/Ast.h" +#include "Aql/AttributeAccessor.h" #include "Aql/Executor.h" #include "Aql/V8Expression.h" #include "Aql/Variable.h" +#include "Basics/Exceptions.h" #include "Basics/JsonHelper.h" #include "Basics/StringBuffer.h" #include "Basics/json.h" #include "ShapedJson/shaped-json.h" #include "VocBase/document-collection.h" -#include "Basics/Exceptions.h" using namespace triagens::aql; using Json = triagens::basics::Json; @@ -112,15 +113,28 @@ Expression::Expression (Ast* ast, Expression::~Expression () { if (_built) { - if (_type == V8) { - delete _func; - _func = nullptr; - } - else if (_type == JSON) { - TRI_ASSERT(_data != nullptr); - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _data); - _data = nullptr; - // _json is freed automatically by AqlItemBlock + switch (_type) { + case JSON: + TRI_ASSERT(_data != nullptr); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _data); + break; + + case ATTRIBUTE: { + TRI_ASSERT(_accessor != nullptr); + delete _accessor; + break; + } + + case V8: + delete _func; + break; + + case SIMPLE: + case UNPROCESSED: { + // nothing to do + break; + } + } } } @@ -163,6 +177,15 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx, return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, _data, Json::NOFREE)); } + case SIMPLE: { + return executeSimpleExpression(_node, collection, trx, docColls, argv, startPos, vars, regs); + } + + case ATTRIBUTE: { + TRI_ASSERT(_accessor != nullptr); + return _accessor->get(trx, docColls, argv, startPos, vars, regs); + } + case V8: { TRI_ASSERT(_func != nullptr); try { @@ -184,10 +207,6 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx, } } - case SIMPLE: { - return executeSimpleExpression(_node, collection, trx, docColls, argv, startPos, vars, regs); - } - case UNPROCESSED: { // fall-through to exception } @@ -323,6 +342,20 @@ void Expression::analyzeExpression () { _canThrow = _node->canThrow(); _canRunOnDBServer = _node->canRunOnDBServer(); _isDeterministic = _node->isDeterministic(); + + if (_node->type == NODE_TYPE_ATTRIBUTE_ACCESS) { + TRI_ASSERT_EXPENSIVE(_node->numMembers() == 1); + auto member = _node->getMemberUnchecked(0); + + if (member->type == NODE_TYPE_REFERENCE) { + auto name = static_cast(_node->getData()); + auto v = static_cast(member->getData()); + + // specialize the simple expression into an attribute accessor + _accessor = new AttributeAccessor(name, v); + _type = ATTRIBUTE; + } + } } else { // expression is a V8 expression @@ -772,13 +805,13 @@ bool Expression::isConstant () const { //////////////////////////////////////////////////////////////////////////////// /// @brief this gives you ("variable.access", "Reference") -/// call isSimpleAccessReference in advance to ensure no exceptions. +/// call isAttributeAccess in advance to ensure no exceptions. //////////////////////////////////////////////////////////////////////////////// std::pair Expression::getMultipleAttributes() { - if (! isSimple()) { + if (_type != SIMPLE && _type != ATTRIBUTE) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "getAccessNRef works only on simple expressions!"); + "getMultipleAttributes works only on simple expressions or attribute accesses!"); } auto expNode = _node; diff --git a/arangod/Aql/Expression.h b/arangod/Aql/Expression.h index 3e7b5b9b20..56b347e8a0 100644 --- a/arangod/Aql/Expression.h +++ b/arangod/Aql/Expression.h @@ -50,6 +50,7 @@ namespace triagens { class AqlItemBlock; struct AqlValue; class Ast; + class AttributeAccessor; class Executor; struct V8Expression; @@ -63,7 +64,8 @@ namespace triagens { UNPROCESSED, JSON, V8, - SIMPLE + SIMPLE, + ATTRIBUTE }; // ----------------------------------------------------------------------------- @@ -164,22 +166,12 @@ namespace triagens { AqlValue execute (triagens::arango::AqlTransaction* trx, std::vector&, - std::vector&, size_t, + std::vector&, + size_t, std::vector const&, std::vector const&, TRI_document_collection_t const**); -//////////////////////////////////////////////////////////////////////////////// -/// @brief check whether this is a simple expression -//////////////////////////////////////////////////////////////////////////////// - - inline bool isSimple () { - if (_type == UNPROCESSED) { - analyzeExpression(); - } - return _type == SIMPLE; - } - //////////////////////////////////////////////////////////////////////////////// /// @brief check whether this is a JSON expression //////////////////////////////////////////////////////////////////////////////// @@ -210,11 +202,14 @@ namespace triagens { if (_type == UNPROCESSED) { analyzeExpression(); } + switch (_type) { case JSON: return "json"; case SIMPLE: return "simple"; + case ATTRIBUTE: + return "attribute"; case V8: return "v8"; case UNPROCESSED: { @@ -245,7 +240,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// /// @brief this gives you ("variable.access", "Reference") -/// call isSimpleAccessReference in advance to ensure no exceptions. +/// call isAttributeAccess in advance to ensure no exceptions. //////////////////////////////////////////////////////////////////////////////// std::pair getMultipleAttributes(); @@ -348,6 +343,8 @@ namespace triagens { V8Expression* _func; struct TRI_json_t* _data; + + AttributeAccessor* _accessor; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 20c692ff88..81c4358764 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -46,6 +46,7 @@ add_executable( Aql/AqlValue.cpp Aql/Ast.cpp Aql/AstNode.cpp + Aql/AttributeAccessor.cpp Aql/BindParameters.cpp Aql/Collection.cpp Aql/CollectionScanner.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files index fa8ac9c839..8f8734a805 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -19,6 +19,7 @@ arangod_libarangod_a_SOURCES = \ arangod/Aql/AqlValue.cpp \ arangod/Aql/Ast.cpp \ arangod/Aql/AstNode.cpp \ + arangod/Aql/AttributeAccessor.cpp \ arangod/Aql/BindParameters.cpp \ arangod/Aql/Collection.cpp \ arangod/Aql/CollectionScanner.cpp \