diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 742ad600f6..fd55f87272 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -48,6 +48,10 @@ int ExecutionBlock::bind (std::map* params) { ExecutionBlock* ExecutionBlock::instanciatePlan (ExecutionPlan const* ep) { ExecutionBlock* eb; switch (ep->getType()) { + case ExecutionPlan::SINGLETON: { + eb = new SingletonBlock(static_cast(ep)); + break; + } case ExecutionPlan::ENUMERATE_COLLECTION: { eb = new EnumerateCollectionBlock(static_cast(ep)); break; diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index a0dad193e2..76b71b45f3 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -35,6 +35,8 @@ #include "Aql/ExecutionPlan.h" #include "Utils/transactions.h" +using namespace triagens::basics; + struct TRI_json_s; namespace triagens { @@ -43,7 +45,7 @@ namespace triagens { class ExecutionBlock { public: ExecutionBlock (ExecutionPlan const* ep) - : _exePlan(ep) { } + : _exePlan(ep), _done(false) { } virtual ~ExecutionBlock (); @@ -99,6 +101,7 @@ namespace triagens { for (auto it = _dependencies.begin(); it != _dependencies.end(); ++it) { (*it)->initialize(); } + // FIXME: report errors from above return TRI_ERROR_NO_ERROR; } @@ -110,6 +113,8 @@ namespace triagens { for (auto it = _dependencies.begin(); it != _dependencies.end(); ++it) { (*it)->execute(); } + // FIXME: report errors from above + _done = false; return TRI_ERROR_NO_ERROR; } @@ -117,12 +122,13 @@ namespace triagens { for (auto it = _dependencies.begin(); it != _dependencies.end(); ++it) { (*it)->shutdown(); } + // FIXME: report errors from above return TRI_ERROR_NO_ERROR; } - virtual AqlValue* getOne () = 0; + virtual AqlItem* getOne () = 0; - std::vector getSome (int atLeast, int atMost); + std::vector getSome (int atLeast, int atMost); bool skip (int number); @@ -133,7 +139,8 @@ namespace triagens { protected: ExecutionPlan const* _exePlan; std::vector _dependencies; - std::deque _buffer; + std::deque _buffer; + bool _done; public: @@ -142,12 +149,49 @@ namespace triagens { }; + class SingletonBlock : public ExecutionBlock { + + public: + + SingletonBlock (SingletonPlan const* ep) + : ExecutionBlock(ep) { + } + + ~SingletonBlock () { + } + + int initialize () { + ExecutionBlock::initialize(); + return TRI_ERROR_NO_ERROR; + } + + int execute () { + ExecutionBlock::execute(); + return TRI_ERROR_NO_ERROR; + } + + int shutdown () { + return TRI_ERROR_NO_ERROR; + } + + AqlItem* getOne () { + if (_done) { + return nullptr; + } + + auto p = reinterpret_cast(_exePlan); + AqlItem* res = new AqlItem(p->_nrVars); + return res; + } + + }; + class EnumerateCollectionBlock : public ExecutionBlock { public: EnumerateCollectionBlock (EnumerateCollectionPlan const* ep) - : ExecutionBlock(ep) { + : ExecutionBlock(ep), _input(nullptr) { } ~EnumerateCollectionBlock () { @@ -156,11 +200,17 @@ namespace triagens { int initialize () { // TODO: this is very very inefficient // it must be implemented properly for production + + int res = ExecutionBlock::initialize(); + if (res != TRI_ERROR_NO_ERROR) { + return res; + } + auto p = reinterpret_cast(_exePlan); V8ReadTransaction trx(p->_vocbase, p->_collname); - int res = trx.begin(); + res = trx.begin(); vector docs; res = trx.read(docs); @@ -168,32 +218,63 @@ namespace triagens { auto shaper = trx.documentCollection()->getShaper(); + _allDocs.clear(); for (size_t i = 0; i < n; ++i) { TRI_shaped_json_t shaped; TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, docs[i]->getDataPtr()); - triagens::basics::Json json(TRI_UNKNOWN_MEM_ZONE, TRI_JsonShapedJson(shaper, &shaped)); - _buffer.push_back(new AqlValue(json)); + _allDocs.push_back(new Json(TRI_UNKNOWN_MEM_ZONE, + TRI_JsonShapedJson(shaper, &shaped))); } res = trx.finish(res); + + _pos = 0; + if (_allDocs.size() == 0) { + _done = true; + } + _input = nullptr; + return res; } int shutdown () { - return TRI_ERROR_NO_ERROR; + int res = ExecutionBlock::shutdown(); // Tell all dependencies + _allDocs.clear(); + return res; } - AqlValue* getOne () { + AqlItem* getOne () { std::cout << "getOne of EnumerateCollectionBlock" << std::endl; - if (_buffer.empty()) { - return nullptr; + if (_done) { + return nullptr; + } + if (_input == nullptr) { + _input = _dependencies[0]->getOne(); + if (_input == nullptr) { + _done = true; + return nullptr; + } + _pos = 0; + if (_allDocs.size() == 0) { + _done = true; + return nullptr; + } + } + AqlItem* res = new AqlItem(_input, 1); + res->setValue(0,0,new AqlValue(_allDocs[_pos])); + if (++_pos >= _allDocs.size()) { + _input = nullptr; // get a new item next time } - auto value = _buffer.front(); - _buffer.pop_front(); - return value; + return res; } - + + private: + + vector _allDocs; + size_t _pos; + AqlItem* _input; + bool _done; }; class RootBlock : public ExecutionBlock { @@ -208,7 +289,7 @@ namespace triagens { ~RootBlock () { } - AqlValue* getOne () { + AqlItem* getOne () { std::cout << "getOne of RootBlock" << std::endl; return _dependencies[0]->getOne(); } diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index cd0b3162fd..2e1e7df4d4 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -91,6 +91,31 @@ void ExecutionPlan::appendAsString (std::string& st, int indent) { st.push_back('>'); } +// ----------------------------------------------------------------------------- +// --SECTION-- methods of SingletonPlan +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief toJson, for SingletonPlan +//////////////////////////////////////////////////////////////////////////////// + +Json SingletonPlan::toJson (TRI_memory_zone_t* zone) const { + Json json(ExecutionPlan::toJson(zone)); // call base class method + if (json.isEmpty()) { + return json; + } + // Now put info about number of vars: + try { + json("nrVariables", Json(_nrVars)); + } + catch (std::exception& e) { + return Json(); + } + + // And return it: + return json; +} + // ----------------------------------------------------------------------------- // --SECTION-- methods of EnumerateCollectionPlan // ----------------------------------------------------------------------------- @@ -112,7 +137,8 @@ Json EnumerateCollectionPlan::toJson (TRI_memory_zone_t* zone) const { else { json("vocbase", Json(_vocbase->_name)); } - json("collection", Json(_collname)); + json("collection", Json(_collname)) + ("nrVariables", Json(_nrVars)); } catch (std::exception& e) { return Json(); @@ -240,7 +266,7 @@ void testExecutionPlans () { std::cout << a.toString() << std::endl; std::cout << "Got here" << std::endl; - auto ec = new EnumerateCollectionPlan(nullptr, "guck"); + auto ec = new EnumerateCollectionPlan(nullptr, "guck", 1); Json jjj(ec->toJson()); cout << jjj.toString() << endl; auto li = new LimitPlan(12, 17); diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index 367a13d5f8..58288b7caa 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -56,6 +56,7 @@ namespace triagens { enum NodeType { ILLEGAL, + SINGLETON, ENUMERATE_COLLECTION, INDEX_RANGE, STATIC_LIST, @@ -218,6 +219,71 @@ namespace triagens { }; +// ----------------------------------------------------------------------------- +// --SECTION-- class SingletonPlan +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief class SingletonPlan, derived from ExecutionPlan +//////////////////////////////////////////////////////////////////////////////// + + class SingletonPlan : public ExecutionPlan { + + friend class SingletonBlock; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor with a vocbase and a collection name +//////////////////////////////////////////////////////////////////////////////// + + public: + + SingletonPlan (int32_t nrvars) + : ExecutionPlan(), _nrVars(nrvars) { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the type of the node +//////////////////////////////////////////////////////////////////////////////// + + virtual NodeType getType () const { + return SINGLETON; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the type of the node as a string +//////////////////////////////////////////////////////////////////////////////// + + virtual std::string getTypeString () const { + return std::string("SingletonPlan"); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief export to JSON +//////////////////////////////////////////////////////////////////////////////// + + virtual triagens::basics::Json toJson ( + TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE) const; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone execution plan recursively +//////////////////////////////////////////////////////////////////////////////// + + virtual ExecutionPlan* clone () const { + auto c = new SingletonPlan(_nrVars); + cloneDependencies(c); + return static_cast(c); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief we need to know how many variables we have in this scope +//////////////////////////////////////////////////////////////////////////////// + + private: + + int32_t _nrVars; + + }; + // ----------------------------------------------------------------------------- // --SECTION-- class EnumerateCollectionPlan // ----------------------------------------------------------------------------- @@ -236,8 +302,11 @@ namespace triagens { public: - EnumerateCollectionPlan (TRI_vocbase_t* vocbase, std::string collname) - : ExecutionPlan(), _vocbase(vocbase), _collname(collname) { + EnumerateCollectionPlan (TRI_vocbase_t* vocbase, + std::string collname, + int32_t nrVars) + : ExecutionPlan(), _vocbase(vocbase), _collname(collname), + _nrVars(nrVars) { } //////////////////////////////////////////////////////////////////////////////// @@ -268,15 +337,11 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// virtual ExecutionPlan* clone () const { - auto c = new EnumerateCollectionPlan(_vocbase, _collname); + auto c = new EnumerateCollectionPlan(_vocbase, _collname, _nrVars); cloneDependencies(c); return static_cast(c); } -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - //////////////////////////////////////////////////////////////////////////////// /// @brief we need to know the database and the collection //////////////////////////////////////////////////////////////////////////////// @@ -285,6 +350,7 @@ namespace triagens { TRI_vocbase_t* _vocbase; std::string _collname; + int32_t _nrVars; }; diff --git a/arangod/Aql/Types.h b/arangod/Aql/Types.h index d87f356b17..fb57e5bbb1 100644 --- a/arangod/Aql/Types.h +++ b/arangod/Aql/Types.h @@ -37,88 +37,147 @@ namespace triagens { namespace aql { // ----------------------------------------------------------------------------- -// --SECTION-- AqlValues +// --SECTION-- AqlDoc // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @brief struct AqlValue, used to pipe documents through executions -/// the execution engine keeps one AqlValue 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 the WAl or a datafile, it stays -/// there unchanged and we only store a (pointer to) a copy of the -/// TRI_doc_mptr_t struct. Sometimes, when it is modified (or created -/// on the fly anyway), 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 AqlItem; struct AqlValue { - triagens::basics::Json _json; - TRI_doc_mptr_t* _mptr; - triagens::basics::Json _vars; -//////////////////////////////////////////////////////////////////////////////// -/// @brief convenience constructors -//////////////////////////////////////////////////////////////////////////////// + enum AqlValueType { + JSON, + DOCVEC, + RANGE + }; - AqlValue () - : _json(), _mptr(nullptr), _vars() { + struct Range { + int64_t _low; + int64_t _high; + Range(int64_t low, int64_t high) : _low(low), _high(high) {} + }; + + union { + triagens::basics::Json* _json; + std::vector* _vector; + Range _range; + }; + + AqlValueType _type; + + AqlValue (triagens::basics::Json* json) + : _json(json), _type(JSON) { } - AqlValue (TRI_doc_mptr_t* mptr) - : _json(), _mptr(mptr), _vars() { + AqlValue (std::vector* vector) + : _vector(vector), _type(DOCVEC) { } - AqlValue (triagens::basics::Json json) - : _json(json), _mptr(nullptr), _vars() { + AqlValue (int64_t low, int64_t high) + : _range(low, high), _type(RANGE) { + } + + ~AqlValue () { + switch (_type) { + case JSON: + delete _json; + break; + case DOCVEC: + delete _vector; + break; + case RANGE: + break; + } + } + + std::string toString () { + switch (_type) { + case JSON: + return _json->toString(); + case DOCVEC: + return "I am a DOCVEC."; + case RANGE: + std::stringstream s; + s << "I am a range: " << _range._low << " .. " << _range._high; + return s.str(); + } + } + + }; + + struct AqlItem { + AqlItem* _outer; + int32_t _refcount; + int32_t _nrvars; + AqlValue** _vars; + + AqlItem (int nrvars) + : _outer(nullptr), _refcount(1), _nrvars(nrvars) { + if (nrvars > 0) { + _vars = new AqlValue* [nrvars]; + } + else { + _vars = nullptr; + } + } + + AqlItem (AqlItem* outer, int nrvars) + : _outer(outer), _refcount(1), _nrvars(nrvars) { + outer->_refcount++; + if (nrvars > 0) { + _vars = new AqlValue* [nrvars]; + for (int i = 0; i < nrvars; i++) { + _vars[i] = nullptr; + } + } + else { + _vars = nullptr; + } } //////////////////////////////////////////////////////////////////////////////// /// @brief destructor //////////////////////////////////////////////////////////////////////////////// - ~AqlValue () { - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief return a string representation of the value -//////////////////////////////////////////////////////////////////////////////// - - std::string toString () const { - std::string out; - if (! _json.isEmpty()) { - out += _json.toString(); + ~AqlItem () { + if (_outer != nullptr) { + _outer->_refcount--; + if (_outer->_refcount == 0) { + delete _outer; + } + _outer = nullptr; } - else if (_mptr != nullptr) { - out.append("got a master pointer"); + if (_vars != nullptr) { + for (int i = 0; i < _nrvars; i++) { + delete _vars[i]; + } + delete[] _vars; } - - if (! _vars.isEmpty()) { - out += _vars.toString(); - } - - return out; } //////////////////////////////////////////////////////////////////////////////// /// @brief getValue, get the current value of a variable or attribute //////////////////////////////////////////////////////////////////////////////// - triagens::basics::Json getValue (std::string name); + AqlValue* getValue (int up, int index) { + AqlItem* p = this; + for (int i = 0; i < up; i++) { + p = p->_outer; + } + return p->_vars[index]; + } //////////////////////////////////////////////////////////////////////////////// /// @brief setValue, set the current value of a variable or attribute //////////////////////////////////////////////////////////////////////////////// - void setValue (std::string name, triagens::basics::Json json); + void setValue (int up, int index, AqlValue* zeug) { + AqlItem* p = this; + for (int i = 0; i < up; i++) { + p = p->_outer; + } + p->_vars[index] = zeug; + } }; @@ -173,7 +232,7 @@ namespace triagens { /// @brief execute the expression //////////////////////////////////////////////////////////////////////////////// - triagens::basics::Json execute (AqlValue* aqldoc); + AqlValue* execute (AqlItem* aqldoc); //////////////////////////////////////////////////////////////////////////////// /// @brief private members diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 550d82f741..7e8e6fc23e 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -5414,7 +5414,9 @@ static v8::Handle JS_PengAql (v8::Arguments const& argv) { TRI_V8_EXCEPTION_USAGE(scope, "AQL_PENG()"); } - triagens::aql::ExecutionPlan* enumPlan = new triagens::aql::EnumerateCollectionPlan(vocbase, "fuxx"); + triagens::aql::ExecutionPlan* singlePlan = new triagens::aql::SingletonPlan(0); + triagens::aql::ExecutionPlan* enumPlan = new triagens::aql::EnumerateCollectionPlan(vocbase, "fuxx", 1); + enumPlan->addDependency(singlePlan); triagens::aql::ExecutionPlan* rootPlan = new triagens::aql::RootPlan(); rootPlan->addDependency(enumPlan); @@ -5423,10 +5425,10 @@ static v8::Handle JS_PengAql (v8::Arguments const& argv) { exec->initialize(); exec->execute(); - triagens::aql::AqlValue* value; + triagens::aql::AqlItem* value; while (nullptr != (value = exec->getOne())) { std::cout << "Peng" << std::endl; - std::cout << value->toString() << std::endl; + std::cout << value->getValue(0,0)->toString() << std::endl; delete value; }