//////////////////////////////////////////////////////////////////////////////// /// @brief fundamental types for the optimisation and execution of AQL /// /// @file arangod/Aql/Types.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_TYPES_H #define ARANGODB_AQL_TYPES_H 1 #include #include #include "Aql/AstNode.h" #include "Aql/Variable.h" #include "V8/v8-conv.h" #include "VocBase/document-collection.h" #include "VocBase/voc-shaper.h" #include "V8Server/v8-vocbase.h" #include "Utils/V8TransactionContext.h" #include "Utils/AqlTransaction.h" namespace triagens { namespace aql { // ----------------------------------------------------------------------------- // --SECTION-- AqlDoc // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief type for register numbers/ids //////////////////////////////////////////////////////////////////////////////// typedef unsigned int RegisterId; //////////////////////////////////////////////////////////////////////////////// /// @brief forward declaration for blocks of items //////////////////////////////////////////////////////////////////////////////// class AqlItemBlock; //////////////////////////////////////////////////////////////////////////////// /// @brief a struct to hold a value, registers hole AqlValue* during the /// execution //////////////////////////////////////////////////////////////////////////////// struct AqlValue { //////////////////////////////////////////////////////////////////////////////// /// @brief AqlValueType, indicates what sort of value we have //////////////////////////////////////////////////////////////////////////////// enum AqlValueType { EMPTY, // contains no data JSON, // Json* SHAPED, // TRI_df_marker_t* DOCVEC, // a vector of blocks of results coming from a subquery RANGE // a pointer to a range remembering lower and upper bound }; //////////////////////////////////////////////////////////////////////////////// /// @brief Range, to hold a range compactly //////////////////////////////////////////////////////////////////////////////// struct Range { int64_t const _low; int64_t const _high; Range (int64_t low, int64_t high) : _low(low), _high(high) {} }; //////////////////////////////////////////////////////////////////////////////// /// @brief the actual data //////////////////////////////////////////////////////////////////////////////// union { triagens::basics::Json* _json; TRI_df_marker_t const* _marker; std::vector* _vector; Range const* _range; }; //////////////////////////////////////////////////////////////////////////////// /// @brief _type, the type of value //////////////////////////////////////////////////////////////////////////////// AqlValueType _type; //////////////////////////////////////////////////////////////////////////////// /// @brief constructors for the various value types, note that they all take /// ownership of the corresponding pointers //////////////////////////////////////////////////////////////////////////////// AqlValue () : _json(nullptr), _type(EMPTY) { } AqlValue (triagens::basics::Json* json) : _json(json), _type(JSON) { } AqlValue (TRI_df_marker_t const* marker) : _marker(marker), _type(SHAPED) { } AqlValue (std::vector* vector) : _vector(vector), _type(DOCVEC) { } AqlValue (int64_t low, int64_t high) : _type(RANGE) { _range = new Range(low, high); } //////////////////////////////////////////////////////////////////////////////// /// @brief destructor, doing nothing automatically! //////////////////////////////////////////////////////////////////////////////// ~AqlValue () { } //////////////////////////////////////////////////////////////////////////////// /// @brief destroy, explicit destruction, only when needed //////////////////////////////////////////////////////////////////////////////// void destroy (); //////////////////////////////////////////////////////////////////////////////// /// @brief erase, this does not free the stuff in the AqlValue, it only /// erases the pointers and makes the AqlValue structure EMPTY, this /// is used when the AqlValue is stolen and stored in another object //////////////////////////////////////////////////////////////////////////////// void erase () { _type = EMPTY; _json = nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief clone for recursive copying //////////////////////////////////////////////////////////////////////////////// AqlValue clone () const; //////////////////////////////////////////////////////////////////////////////// /// @brief construct a V8 value as input for the expression execution in V8 //////////////////////////////////////////////////////////////////////////////// v8::Handle toV8 (AQL_TRANSACTION_V8* trx, TRI_document_collection_t const* document) const; //////////////////////////////////////////////////////////////////////////////// /// @brief toString method //////////////////////////////////////////////////////////////////////////////// std::string toString (TRI_document_collection_t const* document) { switch (_type) { case JSON: { return _json->toString(); } case SHAPED: { TRI_shaper_t* shaper = document->getShaper(); TRI_shaped_json_t shaped; TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, _marker); triagens::basics::Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped)); char const* key = TRI_EXTRACT_MARKER_KEY(_marker); std::string id(document->_info._name); id.push_back('/'); id += std::string(key); json("_id", triagens::basics::Json(id)); json("_rev", triagens::basics::Json(std::to_string( TRI_EXTRACT_MARKER_RID(_marker) ))); json("_key", triagens::basics::Json(key)); return json.toString(); } case DOCVEC: { std::stringstream s; s << "I am a DOCVEC with " << _vector->size() << " blocks."; return s.str(); } case RANGE: { std::stringstream s; s << "I am a range: " << _range->_low << " .. " << _range->_high; return s.str(); } default: return std::string(""); } } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson method //////////////////////////////////////////////////////////////////////////////// triagens::basics::Json toJson (TRI_document_collection_t const* document) { switch (_type) { case JSON: { return *_json; } case SHAPED: { TRI_shaper_t* shaper = document->getShaper(); TRI_shaped_json_t shaped; TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, _marker); triagens::basics::Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped)); char const* key = TRI_EXTRACT_MARKER_KEY(_marker); std::string id(document->_info._name); id.push_back('/'); id += std::string(key); json("_id", triagens::basics::Json(id)); json("_rev", triagens::basics::Json(std::to_string( TRI_EXTRACT_MARKER_RID(_marker) ))); json("_key", triagens::basics::Json(key)); return json; } case DOCVEC: { THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); } case RANGE: { THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); } case EMPTY: { return triagens::basics::Json(); } } THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } //////////////////////////////////////////////////////////////////////////////// /// @brief a quick method to decide whether a value is empty //////////////////////////////////////////////////////////////////////////////// bool isEmpty () { return _type == EMPTY; } //////////////////////////////////////////////////////////////////////////////// /// @brief a quick method to decide whether a value is true //////////////////////////////////////////////////////////////////////////////// bool isTrue () { if (_type != JSON) { return false; } TRI_json_t* json = _json->json(); if (TRI_IsBooleanJson(json) && json->_value._boolean) { return true; } else if (TRI_IsNumberJson(json) && json->_value._number != 0.0) { return true; } else if (TRI_IsStringJson(json) && json->_value._string.length != 0) { return true; } else { return false; } } }; } } //////////////////////////////////////////////////////////////////////////////// /// @brief hash function for AqlValue objects //////////////////////////////////////////////////////////////////////////////// namespace std { template<> struct hash { size_t operator () (triagens::aql::AqlValue const& x) const { std::hash intHash; std::hash ptrHash; size_t res = intHash(static_cast(x._type)); switch (x._type) { case triagens::aql::AqlValue::JSON: { return res ^ ptrHash(x._json); } case triagens::aql::AqlValue::SHAPED: { return res ^ ptrHash(x._marker); } case triagens::aql::AqlValue::DOCVEC: { return res ^ ptrHash(x._vector); } case triagens::aql::AqlValue::RANGE: { return res ^ ptrHash(x._range); } default: { TRI_ASSERT(false); return 0; } } } }; template<> struct equal_to { bool operator () (triagens::aql::AqlValue const& a, triagens::aql::AqlValue const& b) const { if (a._type != b._type) { return false; } switch (a._type) { case triagens::aql::AqlValue::JSON: { return a._json == b._json; } case triagens::aql::AqlValue::SHAPED: { return a._marker == b._marker; } case triagens::aql::AqlValue::DOCVEC: { return a._vector == b._vector; } case triagens::aql::AqlValue::RANGE: { return a._range == b._range; } default: { TRI_ASSERT(false); return true; } } } }; } namespace triagens { namespace aql { // ----------------------------------------------------------------------------- // --SECTION-- AqlItemBlock // ----------------------------------------------------------------------------- class AqlItemBlock { std::vector _data; std::vector _docColls; size_t _nrItems; RegisterId _nrRegs; public: AqlItemBlock (size_t nrItems, RegisterId nrRegs) : _nrItems(nrItems), _nrRegs(nrRegs) { if (nrItems > 0 && nrRegs > 0) { _data.reserve(nrItems * nrRegs); for (size_t i = 0; i < nrItems * nrRegs; ++i) { _data.emplace_back(); } } if (nrRegs > 0) { _docColls.reserve(nrRegs); for (size_t i = 0; i < nrRegs; ++i) { _docColls.push_back(nullptr); } } } //////////////////////////////////////////////////////////////////////////////// /// @brief destructor //////////////////////////////////////////////////////////////////////////////// ~AqlItemBlock () { std::unordered_set cache; for (size_t i = 0; i < _nrItems * _nrRegs; i++) { if (! _data[i].isEmpty()) { auto it = cache.find(_data[i]); if (it == cache.end()) { cache.insert(_data[i]); _data[i].destroy(); } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief getValue, get the value of a register //////////////////////////////////////////////////////////////////////////////// AqlValue getValue (size_t index, RegisterId varNr) const { return _data[index * _nrRegs + varNr]; } //////////////////////////////////////////////////////////////////////////////// /// @brief setValue, set the current value of a register //////////////////////////////////////////////////////////////////////////////// void setValue (size_t index, RegisterId varNr, AqlValue zeug) { TRI_ASSERT(_data[index * _nrRegs + varNr].isEmpty()); _data[index * _nrRegs + varNr] = zeug; } //////////////////////////////////////////////////////////////////////////////// /// @brief eraseValue, erase the current value of a register not freeing it /// this is used if the value is stolen and later released from elsewhere //////////////////////////////////////////////////////////////////////////////// void eraseValue (size_t index, RegisterId varNr) { _data[index * _nrRegs + varNr].erase(); } //////////////////////////////////////////////////////////////////////////////// /// @brief getDocumentCollection //////////////////////////////////////////////////////////////////////////////// TRI_document_collection_t const* getDocumentCollection (RegisterId varNr) const { return _docColls[varNr]; } //////////////////////////////////////////////////////////////////////////////// /// @brief setDocumentCollection, set the current value of a variable or attribute //////////////////////////////////////////////////////////////////////////////// void setDocumentCollection (RegisterId varNr, TRI_document_collection_t const* docColl) { _docColls[varNr] = docColl; } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _nrRegs //////////////////////////////////////////////////////////////////////////////// RegisterId getNrRegs () const { return _nrRegs; } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _nrItems //////////////////////////////////////////////////////////////////////////////// size_t size () const { return _nrItems; } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _data //////////////////////////////////////////////////////////////////////////////// vector& getData () { return _data; } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _data //////////////////////////////////////////////////////////////////////////////// vector& getDocumentCollections () { return _docColls; } //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone //////////////////////////////////////////////////////////////////////////////// AqlItemBlock* slice (size_t from, size_t to) { TRI_ASSERT(from < to && to <= _nrItems); std::unordered_map cache; auto res = new AqlItemBlock(to - from, _nrRegs); for (RegisterId col = 0; col < _nrRegs; col++) { res->_docColls[col] = _docColls[col]; } for (size_t row = from; row < to; row++) { for (RegisterId col = 0; col < _nrRegs; col++) { AqlValue& a(_data[row * _nrRegs + col]); if (! a.isEmpty()) { auto it = cache.find(a); if (it == cache.end()) { AqlValue b = a.clone(); res->_data[(row - from) * _nrRegs + col] = b; cache.insert(make_pair(a,b)); } else { res->_data[(row - from) * _nrRegs + col] = it->second; } } } } return res; } //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone for a subset //////////////////////////////////////////////////////////////////////////////// AqlItemBlock* slice (vector& chosen, size_t from, size_t to) { TRI_ASSERT(from < to && to <= chosen.size()); std::unordered_map cache; auto res = new AqlItemBlock(to - from, _nrRegs); for (RegisterId col = 0; col < _nrRegs; col++) { res->_docColls[col] = _docColls[col]; } for (size_t row = from; row < to; row++) { for (RegisterId col = 0; col < _nrRegs; col++) { AqlValue& a(_data[chosen[row] * _nrRegs + col]); if (! a.isEmpty()) { auto it = cache.find(a); if (it == cache.end()) { AqlValue b = a.clone(); res->_data[(row - from) * _nrRegs + col] = b; cache.insert(make_pair(a,b)); } else { res->_data[(row - from) * _nrRegs + col] = it->second; } } } } return res; } //////////////////////////////////////////////////////////////////////////////// /// @brief splice multiple blocks, note that the new block now owns all /// AqlValue pointers in the old blocks, therefore, the latter are all /// set to nullptr, just to be sure. //////////////////////////////////////////////////////////////////////////////// static AqlItemBlock* splice(std::vector& blocks); }; } // namespace triagens::aql } // namespace triagens #endif // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: