diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp new file mode 100644 index 0000000000..f6defdab33 --- /dev/null +++ b/arangod/Aql/ExecutionBlock.cpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Infrastructure for ExecutionBlocks, the execution engine +/// +/// @file arangod/Aql/ExecutionBlock.cpp +/// +/// 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 +//////////////////////////////////////////////////////////////////////////////// + +#include "Aql/ExecutionBlock.h" + +using namespace triagens::basics; +using namespace triagens::arango; +using namespace triagens::aql; + +// ----------------------------------------------------------------------------- +// --SECTION-- +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: + + diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h new file mode 100644 index 0000000000..dd01cbc40d --- /dev/null +++ b/arangod/Aql/ExecutionBlock.h @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Infrastructure for ExecutionBlocks (the execution engine) +/// +/// @file arangod/Aql/ExecutionBlock.h +/// +/// 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_EXECUTION_BLOCK_H +#define ARANGODB_AQL_EXECUTION_BLOCK_H 1 + +#include + +#include "VocBase/document-collection.h" + +#include "Aql/ExecutionPlan.h" + +namespace triagens { + namespace aql { + +// ----------------------------------------------------------------------------- +// --SECTION-- AqlDocuments +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief struct AqlDocument, used to pipe documents through executions +/// the execution engine keeps one AqlDocument struct for each document +/// that is piped through the engine. Note that the document can exist in +/// one of two formats throughout its lifetime in the engine. When it resides +/// originally in a datafile, it stays there unchanged and we only store +/// a (pointer to) a copy of the TRI_doc_mptr_t struct. As soon as it is +/// modified (or created on the fly anyway), or if it originally resides +/// in the WAL, we keep the document as a TRI_json_t, wrapped by a Json +/// struct. That is, the following struct has the following invariant: +/// Either the whole struct is empty and thus _json is empty and _mptr is +/// a nullptr. Otherwise, either _json is empty and _mptr is not a nullptr, +/// or _json is non-empty and _mptr is anullptr. +/// Additionally, the struct contains another TRI_json_t holding the +/// current state of the LET variables (and possibly some other +/// computations). This is the _vars attribute. +/// Note that both Json subobjects are constructed as AUTOFREE. +//////////////////////////////////////////////////////////////////////////////// + + struct AqlDocument { + triagens::basics::Json _json; + TRI_doc_mptr_copy_t* _mptr; + triagens::basics::Json _vars; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief convenience constructors +//////////////////////////////////////////////////////////////////////////////// + + AqlDocument () + : _json(), _mptr(nullptr), _vars() { + } + + AqlDocument (TRI_doc_mptr_t* mptr) + : _json(), _vars() { + _mptr = new TRI_doc_mptr_copy_t(*mptr); + } + + AqlDocument (triagens::basics::Json json) + : _json(json), _mptr(nullptr), _vars() { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + ~AqlDocument () { + if (_mptr != nullptr) { + delete _mptr; + } + } + + }; + + } // namespace triagens::aql +} // namespace triagens + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: + + diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 722d2d16e8..7db7d410b7 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// /// @brief Infrastructure for ExecutionPlans /// -/// @file arangod/Aql/ExecutionPlan.h +/// @file arangod/Aql/ExecutionPlan.cpp /// /// DISCLAIMER /// @@ -148,6 +148,32 @@ Json LimitPlan::toJson (TRI_memory_zone_t* zone) { return json; } +// ----------------------------------------------------------------------------- +// --SECTION-- methods of FilterPlan +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief toJson, for FilterPlan +//////////////////////////////////////////////////////////////////////////////// + +Json FilterPlan::toJson (TRI_memory_zone_t* zone) { + Json json(ExecutionPlan::toJson(zone)); // call base class method + if (json.isEmpty()) { + return json; + } + // Now put info about offset and limit in + try { + json("attribute", Json(_attribute)) + ("value", Json(_value.copy())); + } + catch (std::exception& e) { + return Json(); + } + + // And return it: + return json; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief test function //////////////////////////////////////////////////////////////////////////////// @@ -163,27 +189,6 @@ void testExecutionPlans () { std::cout << b.toString() << std::endl; std::cout << a.toString() << std::endl; std::cout << "Got here" << std::endl; - ExecutionPlan* e = new ExecutionPlan(); - ExecutionPlan* f = new ExecutionPlan(e); - string st; - e->appendAsString(st, 0); - cout << "e as string:\n" << st << endl; - st.clear(); - f->appendAsString(st, 0); - cout << "f as string:\n" << st << endl; - TRI_json_t* json = e->toJson(TRI_UNKNOWN_MEM_ZONE); - if (json != nullptr) { - cout << "e as JSON:\n" << - JsonHelper::toString(json) << endl; - } - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); - json = f->toJson(TRI_UNKNOWN_MEM_ZONE); - if (json != nullptr) { - cout << "f as JSON:\n" << - JsonHelper::toString(json) << endl; - } - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); - delete f; // should not leave a leak auto ec = new EnumerateCollectionPlan(nullptr, "guck"); Json jjj(ec->toJson()); @@ -192,7 +197,7 @@ void testExecutionPlans () { jjj = li->toJson(); cout << jjj.toString() << endl; - json = Json(12); + TRI_json_t* json = Json(12); cout << JsonHelper::toString(json) << endl; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index 91dd72244d..368271c361 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -35,11 +35,60 @@ #include #include +#include "Aql/AstNode.h" + namespace triagens { namespace aql { //////////////////////////////////////////////////////////////////////////////// -/// @brief class ExecutionPlan, abstract base class +/// @brief class AqlExpression, used in execution plans and execution blocks +//////////////////////////////////////////////////////////////////////////////// + + class AqlExpression { + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief default constructor, creating an empty expression +//////////////////////////////////////////////////////////////////////////////// + + AqlExpression () : _ast(nullptr) { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor, using an abstract syntax tree +//////////////////////////////////////////////////////////////////////////////// + + AqlExpression (AstNode* ast) : _ast(ast) { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destructor +//////////////////////////////////////////////////////////////////////////////// + + ~AqlExpression () { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getAst, get the underlying abstract syntax tree +//////////////////////////////////////////////////////////////////////////////// + + AstNode* getAst () { + return _ast; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief private members +//////////////////////////////////////////////////////////////////////////////// + + private: + + AstNode* _ast; + + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief class ExecutionPlan, abstract base class of all execution plans //////////////////////////////////////////////////////////////////////////////// class ExecutionPlan { @@ -139,6 +188,23 @@ namespace triagens { return _dependencies; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief remove a dependency, returns true if the pointer was found and +/// removed, please note that this does not delete ep! +//////////////////////////////////////////////////////////////////////////////// + + bool removeDependency (ExecutionPlan* ep) { + auto it = _dependencies.begin(); + while (it != _dependencies.end()) { + if (*it == ep) { + _dependencies.erase(it); + return true; + } + ++it; + } + return false; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief access the pos-th dependency //////////////////////////////////////////////////////////////////////////////// @@ -156,8 +222,18 @@ namespace triagens { /// @brief clone execution plan recursively, this makes the class abstract //////////////////////////////////////////////////////////////////////////////// - virtual ExecutionPlan* clone () { // = 0; make this abstract later - return this; + virtual ExecutionPlan* clone () = 0; // make class abstract + +//////////////////////////////////////////////////////////////////////////////// +/// @brief helper for cloning, use virtual clone methods for dependencies +//////////////////////////////////////////////////////////////////////////////// + + void cloneDependencies (ExecutionPlan* theClone) { + auto it = _dependencies.begin(); + while (it != _dependencies.end()) { + theClone->_dependencies.push_back((*it)->clone()); + ++it; + } } //////////////////////////////////////////////////////////////////////////////// @@ -230,6 +306,16 @@ namespace triagens { virtual triagens::basics::Json toJson ( TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE); +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone execution plan recursively +//////////////////////////////////////////////////////////////////////////////// + + virtual ExecutionPlan* clone () { + auto c = new EnumerateCollectionPlan(_vocbase, _collname); + cloneDependencies(c); + return static_cast(c); + } + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -246,7 +332,7 @@ namespace triagens { }; // ----------------------------------------------------------------------------- -// --SECTION-- class EnumerateCollectionPlan +// --SECTION-- class LimitPlan // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -300,9 +386,15 @@ namespace triagens { virtual triagens::basics::Json toJson ( TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE); -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone execution plan recursively +//////////////////////////////////////////////////////////////////////////////// + + virtual ExecutionPlan* clone () { + auto c = new LimitPlan(_offset, _limit); + cloneDependencies(c); + return static_cast(c); + } //////////////////////////////////////////////////////////////////////////////// /// @brief we need to know the offset and limit @@ -314,6 +406,75 @@ namespace triagens { size_t _limit; }; + +// ----------------------------------------------------------------------------- +// --SECTION-- class LimitPlan +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief class FilterPlan, derived from ExecutionPlan +//////////////////////////////////////////////////////////////////////////////// + + class FilterPlan : public ExecutionPlan { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructors for various arguments, always with offset and limit +//////////////////////////////////////////////////////////////////////////////// + + public: + + FilterPlan (std::string attribute, triagens::basics::Json value) + : ExecutionPlan(), _attribute(attribute), _value(value) { + } + + FilterPlan (ExecutionPlan* ep, std::string attribute, + triagens::basics::Json value) + : ExecutionPlan(ep), _attribute(attribute), _value(value) { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the type of the node +//////////////////////////////////////////////////////////////////////////////// + + virtual NodeType getType () { + return FILTER; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the type of the node as a string +//////////////////////////////////////////////////////////////////////////////// + + virtual std::string getTypeString () { + return std::string("FilterPlan"); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief export to JSON +//////////////////////////////////////////////////////////////////////////////// + + virtual triagens::basics::Json toJson ( + TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone execution plan recursively +//////////////////////////////////////////////////////////////////////////////// + + virtual ExecutionPlan* clone () { + auto c = new FilterPlan(_attribute, _value.copy()); + cloneDependencies(c); + return static_cast(c); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief we need to know the offset and limit +//////////////////////////////////////////////////////////////////////////////// + + private: + + std::string _attribute; + triagens::basics::Json _value; + + }; } // namespace triagens::aql } // namespace triagens diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 21662e13c0..697c38b975 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable( Ahuacatl/ahuacatl-variable.cpp Aql/AstNode.cpp Aql/BindParameters.cpp + Aql/ExecutionBlock.cpp Aql/ExecutionPlan.cpp Aql/grammar.cpp Aql/Parser.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files index 4f039ce24e..9ab84a9087 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -40,6 +40,7 @@ arangod_libarangod_a_SOURCES = \ arangod/Ahuacatl/ahuacatl-variable.cpp \ arangod/Aql/AstNode.cpp \ arangod/Aql/BindParameters.cpp \ + arangod/Aql/ExecutionBlock.cpp \ arangod/Aql/ExecutionPlan.cpp \ arangod/Aql/grammar.cpp \ arangod/Aql/Parser.cpp \ diff --git a/arangod/VocBase/document-collection.h b/arangod/VocBase/document-collection.h index 1360a04186..2ac674fbe4 100644 --- a/arangod/VocBase/document-collection.h +++ b/arangod/VocBase/document-collection.h @@ -128,6 +128,9 @@ struct TRI_doc_mptr_t { _dataptr(nullptr) { } + virtual ~TRI_doc_mptr_t () { + } + void clear () { _rid = 0; _fid = 0;