diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 77bf7d0f4c..32a4238e98 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -703,6 +703,10 @@ namespace triagens { if (_done) { return nullptr; } + std::cout << _depth << " "; + for (auto i: _varOverview->nrRegs) + std::cout << i << " "; + std::cout << "\n"; AqlItemBlock* res = new AqlItemBlock(1, _varOverview->nrRegs[_depth]); if (_inputRegisterValues != nullptr) { @@ -1475,21 +1479,44 @@ namespace triagens { return nullptr; } for (size_t i = 0; i < res->size(); i++) { - _subquery->initialize(); - _subquery->bind(res, i); - _subquery->execute(); + int ret; + ret = _subquery->initialize(); + if (ret == TRI_ERROR_NO_ERROR) { + ret = _subquery->bind(res, i); + } + if (ret == TRI_ERROR_NO_ERROR) { + ret = _subquery->execute(); + } + if (ret != TRI_ERROR_NO_ERROR) { + delete res; + THROW_ARANGO_EXCEPTION(ret); + } + auto results = new std::vector; - do { - auto tmp = _subquery->getSome(DefaultBatchSize, DefaultBatchSize); - if (tmp == nullptr) { - break; + try { + do { + auto tmp = _subquery->getSome(DefaultBatchSize, DefaultBatchSize); + if (tmp == nullptr) { + break; + } + results->push_back(tmp); } - results->push_back(tmp); - } - while(true); + while(true); + } + catch (...) { + delete res; + delete results; + throw; + } res->setValue(i, _outReg, AqlValue(results)); - _subquery->shutdown(); + + ret = _subquery->shutdown(); + if (ret != TRI_ERROR_NO_ERROR) { + delete res; + THROW_ARANGO_EXCEPTION(ret); + } + } return res; } @@ -1805,29 +1832,6 @@ namespace triagens { return TRI_ERROR_NO_ERROR; } -/* - void emitRow (AqlItemBlock* res, - size_t j, - AqlItemBlock* cur, - void* row) { - std::cout << "EMIT ROW: " << j << "\n"; - - if (j == 0) { - // first row in block - TRI_ASSERT(cur->getNrRegs() <= res->getNrRegs()); - - inheritRegisters(cur, res, _pos); - } - - for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) { - // TODO: return the actual group values - res->setValue(j, (*it).first, AqlValue(new basics::Json(42))); - } - - clearGroup(); - } - -*/ //////////////////////////////////////////////////////////////////////////////// /// @brief getSome @@ -1864,7 +1868,7 @@ std::cout << "AGGREGATE::GETSOME - DONE\n"; std::cout << "POS: " << _pos << "\n"; inheritRegisters(cur, res, _pos); - + size_t j = 0; while (j < atMost) { @@ -1874,9 +1878,11 @@ std::cout << "POS: " << _pos << "\n"; if (_currentGroup.groupValues[0].isEmpty()) { // we never had any previous group newGroup = true; + std::cout << "NEED TO CREATE NEW GROUP\n"; } else { // we already had a group, check if the group has changed + std::cout << "HAVE A GROUP\n"; size_t i = 0; for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) { @@ -1894,13 +1900,18 @@ std::cout << "POS: " << _pos << "\n"; } if (newGroup) { + std::cout << "CREATING GROUP...\n"; if (! _currentGroup.groupValues[0].isEmpty()) { // need to emit the current group first + std::cout << "EMITTING OLD GROUP INTO ROW #" << j << "\n"; size_t i = 0; for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) { + std::cout << "REGISTER #" << (*it).first << "\n"; res->setValue(j, (*it).first, _currentGroup.groupValues[i]); ++i; } + + ++j; } // construct the new group @@ -1908,7 +1919,7 @@ std::cout << "POS: " << _pos << "\n"; for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) { _currentGroup.groupValues[i] = cur->getValue(_pos, (*it).second).clone(); _currentGroup.collections[i] = cur->getDocumentCollection((*it).second); - // std::cout << "GROUP VALUE #" << i << ": " << _currentGroup.groupValues[i].toString(_currentGroup.collections[i]) << "\n"; + std::cout << "GROUP VALUE #" << i << ": " << _currentGroup.groupValues[i].toString(_currentGroup.collections[i]) << "\n"; ++i; } } @@ -1917,16 +1928,19 @@ std::cout << "POS: " << _pos << "\n"; // _currentGroup.groupDetails. } - ++j; - if (++_pos >= cur->size()) { _buffer.pop_front(); delete cur; _pos = 0; +std::cout << "SHRINKING BLOCK TO " << j << " ROWS\n"; + res->shrink(j); return res; } } +std::cout << "SHRINKING BLOCK TO " << j << " ROWS\n"; + res->shrink(j); + return res; } diff --git a/arangod/Aql/Types.cpp b/arangod/Aql/Types.cpp index 15d57025b5..a31abd1cb2 100644 --- a/arangod/Aql/Types.cpp +++ b/arangod/Aql/Types.cpp @@ -123,14 +123,41 @@ v8::Handle AqlValue::toV8 (AQL_TRANSACTION_V8* trx, } case DOCVEC: { - THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); + // calculate the result list length + size_t totalSize = 0; + for (auto it = _vector->begin(); it != _vector->end(); ++it) { + totalSize += (*it)->size(); + } + + // allocate the result list + v8::Handle result = v8::Array::New(static_cast(totalSize)); + uint32_t j = 0; // output row count + + for (auto it = _vector->begin(); it != _vector->end(); ++it) { + auto current = (*it); + size_t const n = current->size(); + auto vecCollection = current->getDocumentCollection(0); + for (size_t i = 0; i < n; ++i) { + result->Set(j++, current->getValue(i, 0).toV8(trx, vecCollection)); + } + } + return result; } case RANGE: { - v8::Handle values = v8::Array::New(); - // TODO: fill range - THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); - return values; + TRI_ASSERT(_range != nullptr); + + // allocate the buffer for the result + int64_t const n = _range->_high - _range->_low + 1; + v8::Handle result = v8::Array::New(static_cast(n)); + + uint32_t j = 0; // output row count + for (int64_t i = _range->_low; i <= _range->_high; ++i) { + // is it safe to use a double here (precision loss)? + result->Set(j++, v8::Number::New(static_cast(i))); + } + + return result; } case EMPTY: { @@ -281,6 +308,7 @@ AqlItemBlock* AqlItemBlock::splice (std::vector& blocks) { } pos += (*it)->size(); } + return res; } @@ -288,101 +316,111 @@ AqlItemBlock* AqlItemBlock::splice (std::vector& blocks) { /// @brief 3-way comparison for AqlValue objects //////////////////////////////////////////////////////////////////////////////// -int triagens::aql::CompareAqlValues ( AqlValue const& left, - TRI_document_collection_t const* leftcoll, - AqlValue const& right, - TRI_document_collection_t const* rightcoll ) { +int triagens::aql::CompareAqlValues (AqlValue const& left, + TRI_document_collection_t const* leftcoll, + AqlValue const& right, + TRI_document_collection_t const* rightcoll) { + if (left._type != right._type) { + if (left._type == AqlValue::EMPTY) { + return -1; + } - if (left._type != right._type) { - if (left._type == AqlValue::EMPTY) { - return -1; - } - if (right._type == AqlValue::EMPTY) { - return 1; - } - if (left._type == AqlValue::JSON && right._type == AqlValue::SHAPED) { - triagens::basics::Json rjson = right.toJson(rightcoll); - return TRI_CompareValuesJson(left._json->json(), rjson.json()); - } - if (left._type == AqlValue::SHAPED && right._type == AqlValue::JSON) { - triagens::basics::Json ljson = left.toJson(leftcoll); - return TRI_CompareValuesJson(ljson.json(), right._json->json()); - } - // No other comparisons are defined - TRI_ASSERT(false); + if (right._type == AqlValue::EMPTY) { + return 1; + } + + if (left._type == AqlValue::JSON && right._type == AqlValue::SHAPED) { + triagens::basics::Json rjson = right.toJson(rightcoll); + return TRI_CompareValuesJson(left._json->json(), rjson.json()); + } + + if (left._type == AqlValue::SHAPED && right._type == AqlValue::JSON) { + triagens::basics::Json ljson = left.toJson(leftcoll); + return TRI_CompareValuesJson(ljson.json(), right._json->json()); + } + + // No other comparisons are defined + TRI_ASSERT(false); + } + + // if we get here, types are equal + + switch (left._type) { + case AqlValue::EMPTY: { + return 0; + } + + case AqlValue::JSON: { + return TRI_CompareValuesJson(left._json->json(), right._json->json()); + } + + case AqlValue::SHAPED: { + TRI_shaped_json_t l; + TRI_shaped_json_t r; + TRI_EXTRACT_SHAPED_JSON_MARKER(l, left._marker); + TRI_EXTRACT_SHAPED_JSON_MARKER(r, right._marker); + + return TRI_CompareShapeTypes(nullptr, nullptr, &l, leftcoll->getShaper(), + nullptr, nullptr, &r, rightcoll->getShaper()); + } + + case AqlValue::DOCVEC: { + // use lexicographic ordering of AqlValues regardless of block, + // DOCVECs have a single register coming from ReturnNode. + size_t lblock = 0; + size_t litem = 0; + size_t rblock = 0; + size_t ritem = 0; + + while (lblock < left._vector->size() && + rblock < right._vector->size()) { + AqlValue lval = left._vector->at(lblock)->getValue(litem, 0); + AqlValue rval = right._vector->at(rblock)->getValue(ritem, 0); + int cmp = CompareAqlValues(lval, + left._vector->at(lblock)->getDocumentCollection(0), + rval, + right._vector->at(rblock)->getDocumentCollection(0)); + if (cmp != 0){ + return cmp; } - switch (left._type) { - case AqlValue::EMPTY: { - return 0; - } - case AqlValue::JSON: { - return TRI_CompareValuesJson(left._json->json(), right._json->json()); - } - case AqlValue::SHAPED: { - TRI_shaped_json_t l; - TRI_shaped_json_t r; - TRI_EXTRACT_SHAPED_JSON_MARKER(l, left._marker); - TRI_EXTRACT_SHAPED_JSON_MARKER(r, right._marker); - - return TRI_CompareShapeTypes(nullptr, nullptr, &l, leftcoll->getShaper(), - nullptr, nullptr, &r, rightcoll->getShaper()); - } - case AqlValue::DOCVEC: { - // use lexicographic ordering of AqlValues regardless of block, - // DOCVECs have a single register coming from ReturnNode. - size_t lblock = 0; - size_t litem = 0; - size_t rblock = 0; - size_t ritem = 0; - - while( lblock < left._vector->size() && rblock < right._vector->size() ){ - AqlValue lval = left._vector->at(lblock)->getValue(litem, 0); - AqlValue rval = right._vector->at(rblock)->getValue(ritem, 0); - int cmp = CompareAqlValues(lval, - left._vector->at(lblock)->getDocumentCollection(0), - rval, - right._vector->at(rblock)->getDocumentCollection(0)); - if(cmp != 0){ - return cmp; - } - if(++litem == left._vector->size()){ - litem = 0; - lblock++; - } - if(++ritem == right._vector->size()){ - ritem = 0; - rblock++; - } - } - - if(lblock == left._vector->size() && rblock == right._vector->size()){ - return 0; - } - - return (lblock < left._vector->size()?-1:1); - } - case AqlValue::RANGE: { - if(left._range->_low < right._range->_low){ - return -1; - } - if (left._range->_low > right._range->_low){ - return 1; - } - if (left._range->_high < left._range->_high) { - return -1; - } - if (left._range->_high > left._range->_high) { - return 1; - } - return 0; - } - default: { - TRI_ASSERT(false); - return 0; - } + if (++litem == left._vector->size()) { + litem = 0; + lblock++; } + if (++ritem == right._vector->size()) { + ritem = 0; + rblock++; + } + } + if (lblock == left._vector->size() && + rblock == right._vector->size()){ + return 0; + } + return (lblock < left._vector->size() ? -1 : 1); + } + case AqlValue::RANGE: { + if (left._range->_low < right._range->_low){ + return -1; + } + if (left._range->_low > right._range->_low){ + return 1; + } + if (left._range->_high < left._range->_high) { + return -1; + } + if (left._range->_high > left._range->_high) { + return 1; + } + return 0; + } + + default: { + TRI_ASSERT(false); + return 0; + } + } } // Local Variables: diff --git a/arangod/Aql/Types.h b/arangod/Aql/Types.h index 13771d0500..8c02813aef 100644 --- a/arangod/Aql/Types.h +++ b/arangod/Aql/Types.h @@ -330,9 +330,11 @@ namespace triagens { ~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(); @@ -341,20 +343,46 @@ namespace triagens { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief shrink the block to the specified number of rows +//////////////////////////////////////////////////////////////////////////////// + + void shrink (size_t nrItems) { + if (nrItems == _nrItems) { + // nothing to do + return; + } + if (nrItems > _nrItems) { + // cannot use shrink() to increase the size of the block + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + + // erase all stored values in the region that we freed + for (size_t i = nrItems; i < _nrItems; ++i) { + for (RegisterId j = 0; j < _nrRegs; ++j) { + eraseValue(i, j); + } + } + + // adjust the size of the block + _nrItems = nrItems; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief getValue, get the value of a register //////////////////////////////////////////////////////////////////////////////// - AqlValue getValue (size_t index, RegisterId varNr) const { - return _data[index * _nrRegs + varNr]; - } + 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 value) { - TRI_ASSERT(_data[index * _nrRegs + varNr].isEmpty()); + TRI_ASSERT(_data.capacity() > index * _nrRegs + varNr); + TRI_ASSERT(_data.at(index * _nrRegs + varNr).isEmpty()); _data[index * _nrRegs + varNr] = value; } @@ -363,121 +391,141 @@ namespace triagens { /// 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(); - } + 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]; - } + 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; - } + void setDocumentCollection (RegisterId varNr, TRI_document_collection_t const* docColl) { + _docColls[varNr] = docColl; + } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _nrRegs //////////////////////////////////////////////////////////////////////////////// - RegisterId getNrRegs () const { - return _nrRegs; - } + RegisterId getNrRegs () const { + return _nrRegs; + } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _nrItems //////////////////////////////////////////////////////////////////////////////// - size_t size () const { - return _nrItems; - } + size_t size () const { + return _nrItems; + } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _data //////////////////////////////////////////////////////////////////////////////// - vector& getData () { - return _data; - } + vector& getData () { + return _data; + } //////////////////////////////////////////////////////////////////////////////// /// @brief getter for _docColls //////////////////////////////////////////////////////////////////////////////// - vector& getDocumentCollections () { - return _docColls; - } + vector& getDocumentCollections () { + return _docColls; + } //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone //////////////////////////////////////////////////////////////////////////////// - AqlItemBlock* slice (size_t from, size_t to) { - TRI_ASSERT(from < to && to <= _nrItems); + 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]); + std::unordered_map cache; - 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; + AqlItemBlock* res = nullptr; + try { + 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; + } + catch (...) { + delete res; + throw; } } - 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()); + 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]); + std::unordered_map cache; - 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; + AqlItemBlock* res = nullptr; + try { + 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; + } + catch (...) { + delete res; + throw; } } - return res; - } //////////////////////////////////////////////////////////////////////////////// /// @brief splice multiple blocks, note that the new block now owns all @@ -485,7 +533,7 @@ namespace triagens { /// set to nullptr, just to be sure. //////////////////////////////////////////////////////////////////////////////// - static AqlItemBlock* splice(std::vector& blocks); + static AqlItemBlock* splice (std::vector& blocks); }; diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index c50bcb7c6a..baf4e7e169 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -35,13 +35,17 @@ #include "Ahuacatl/ahuacatl-collections.h" #include "Ahuacatl/ahuacatl-explain.h" -#include "Aql/ExecutionBlock.h" #include "Aql/Query.h" #include "Basics/Utf8Helper.h" -#include "HttpServer/ApplicationEndpointServer.h" + +#include "BasicsC/conversions.h" +#include "BasicsC/json-utilities.h" +#include "Utils/transactions.h" #include "Utils/AhuacatlGuard.h" #include "Utils/AhuacatlTransaction.h" #include "Utils/V8ResolverGuard.h" + +#include "HttpServer/ApplicationEndpointServer.h" #include "V8/v8-conv.h" #include "V8/v8-execution.h" #include "V8/v8-utils.h"