Example #1
0
/// @brief getBlockForClient: try to get atLeast pairs into
/// _distBuffer.at(clientId), this means we have to look at every row in the
/// incoming blocks until they run out or we find enough rows for clientId. We
/// also keep track of blocks which should be sent to other clients than the
/// current one.
bool DistributeBlock::getBlockForClient(size_t atLeast, size_t atMost,
                                        size_t clientId) {
  DEBUG_BEGIN_BLOCK();
  if (_buffer.empty()) {
    _index = 0;  // position in _buffer
    _pos = 0;    // position in _buffer.at(_index)
  }

  std::vector<std::deque<std::pair<size_t, size_t>>>& buf = _distBuffer;
  // it should be the case that buf.at(clientId) is empty

  while (buf.at(clientId).size() < atLeast) {
    if (_index == _buffer.size()) {
      if (!ExecutionBlock::getBlock(atLeast, atMost)) {
        if (buf.at(clientId).size() == 0) {
          _doneForClient.at(clientId) = true;
          return false;
        }
        break;
      }
    }

    AqlItemBlock* cur = _buffer.at(_index);

    while (_pos < cur->size() && buf.at(clientId).size() < atMost) {
      // this may modify the input item buffer in place
      size_t id = sendToClient(cur);

      buf.at(id).emplace_back(std::make_pair(_index, _pos++));
    }

    if (_pos == cur->size()) {
      _pos = 0;
      _index++;
    } else {
      break;
    }
  }

  return true;

  // cppcheck-suppress style
  DEBUG_END_BLOCK();
}
Example #2
0
QueryResultV8 Query::executeV8 (v8::Isolate* isolate, QueryRegistry* registry) {

  // Now start the execution:
  try {
    QueryResultV8 res = prepare(registry);
    if (res.code != TRI_ERROR_NO_ERROR) {
      return res;
    }

    uint32_t j = 0;
    QueryResultV8 result(TRI_ERROR_NO_ERROR);
    result.result  = v8::Array::New(isolate);
    triagens::basics::Json stats;

    AqlItemBlock* value;

    while (nullptr != (value = _engine->getSome(1, ExecutionBlock::DefaultBatchSize))) {
      auto doc = value->getDocumentCollection(0);
      size_t const n = value->size();
      // reserve space for n additional results at once
      /// json.reserve(n);
      
      for (size_t i = 0; i < n; ++i) {
        AqlValue val = value->getValue(i, 0);

        if (! val.isEmpty()) {
          result.result->Set(j++, val.toV8(isolate, _trx, doc)); 
        }
      }
      delete value;
    }

    stats = _engine->_stats.toJson();

    _trx->commit();
    
    cleanupPlanAndEngine(TRI_ERROR_NO_ERROR);

    enterState(FINALIZATION); 

    result.warnings = warningsToJson(TRI_UNKNOWN_MEM_ZONE);
    result.stats    = stats.steal(); 

    if (_profile != nullptr && profiling()) {
      result.profile = _profile->toJson(TRI_UNKNOWN_MEM_ZONE);
    }

    return result;
  }
  catch (triagens::basics::Exception const& ex) {
    cleanupPlanAndEngine(ex.code());
    return QueryResultV8(ex.code(), ex.message() + getStateString());
  }
  catch (std::bad_alloc const&) {
    cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
    return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
  }
  catch (std::exception const& ex) {
    cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
    return QueryResultV8(TRI_ERROR_INTERNAL, ex.what() + getStateString());
  }
  catch (...) {
    cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
    return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
  }
}
Example #3
0
/// @brief getSome
AqlItemBlock* TraversalBlock::getSome(size_t,  // atLeast,
                                      size_t atMost) {
  DEBUG_BEGIN_BLOCK();
  traceGetSomeBegin();
  if (_done) {
    traceGetSomeEnd(nullptr);
    return nullptr;
  }

  if (_buffer.empty()) {
    size_t toFetch = (std::min)(DefaultBatchSize(), atMost);
    if (!ExecutionBlock::getBlock(toFetch, toFetch)) {
      _done = true;
      traceGetSomeEnd(nullptr);
      return nullptr;
    }
    _pos = 0;  // this is in the first block
  }

  // If we get here, we do have _buffer.front()
  AqlItemBlock* cur = _buffer.front();
  size_t const curRegs = cur->getNrRegs();

  if (_pos == 0) {
    // Initial initialization
    initializePaths(cur, _pos);
  }

  // Iterate more paths:
  if (_posInPaths >= _vertices.size()) {
    if (!morePaths(atMost)) {
      // This input does not have any more paths. maybe the next one has.
      // we can only return nullptr iff the buffer is empty.
      if (++_pos >= cur->size()) {
        _buffer.pop_front();  // does not throw
        // returnBlock(cur);
        delete cur;
        _pos = 0;
      } else {
        initializePaths(cur, _pos);
      }
      auto r = getSome(atMost, atMost);
      traceGetSomeEnd(r);
      return r;
    }
  }

  size_t available = _vertices.size() - _posInPaths;
  size_t toSend = (std::min)(atMost, available);

  RegisterId nrRegs =
      getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()];

  std::unique_ptr<AqlItemBlock> res(requestBlock(toSend, nrRegs));
  // automatically freed if we throw
  TRI_ASSERT(curRegs <= res->getNrRegs());

  // only copy 1st row of registers inherited from previous frame(s)
  inheritRegisters(cur, res.get(), _pos);

  for (size_t j = 0; j < toSend; j++) {
    if (usesVertexOutput()) {
      res->setValue(j, _vertexReg, _vertices[_posInPaths].clone());
    }
    if (usesEdgeOutput()) {
      res->setValue(j, _edgeReg, _edges[_posInPaths].clone());
    }
    if (usesPathOutput()) {
      res->setValue(j, _pathReg, _paths[_posInPaths].clone());
    }
    if (j > 0) {
      // re-use already copied AqlValues
      res->copyValuesFromFirstRow(j, static_cast<RegisterId>(curRegs));
    }
    ++_posInPaths;
  }
  // Advance read position:
  if (_posInPaths >= _vertices.size()) {
    // we have exhausted our local paths buffer
    // fetch more paths into our buffer
    if (!morePaths(atMost)) {
      // nothing more to read, re-initialize fetching of paths
      if (++_pos >= cur->size()) {
        _buffer.pop_front();  // does not throw
        // returnBlock(cur);
        delete cur;
        _pos = 0;
      } else {
        initializePaths(cur, _pos);
      }
    }
  }

  // Clear out registers no longer needed later:
  clearRegisters(res.get());
  traceGetSomeEnd(res.get());
  return res.release();

  // cppcheck-suppress style
  DEBUG_END_BLOCK();
}
Example #4
0
/// @brief getSome
AqlItemBlock* GatherBlock::getSome(size_t atLeast, size_t atMost) {
  DEBUG_BEGIN_BLOCK();
  traceGetSomeBegin();
  if (_done) {
    traceGetSomeEnd(nullptr);
    return nullptr;
  }

  // the simple case . . .
  if (_isSimple) {
    auto res = _dependencies.at(_atDep)->getSome(atLeast, atMost);
    while (res == nullptr && _atDep < _dependencies.size() - 1) {
      _atDep++;
      res = _dependencies.at(_atDep)->getSome(atLeast, atMost);
    }
    if (res == nullptr) {
      _done = true;
    }
    traceGetSomeEnd(res);
    return res;
  }

  // the non-simple case . . .
  size_t available = 0;  // nr of available rows
  size_t index = 0;      // an index of a non-empty buffer

  // pull more blocks from dependencies . . .
  for (size_t i = 0; i < _dependencies.size(); i++) {
    if (_gatherBlockBuffer.at(i).empty()) {
      if (getBlock(i, atLeast, atMost)) {
        index = i;
        _gatherBlockPos.at(i) = std::make_pair(i, 0);
      }
    } else {
      index = i;
    }

    auto cur = _gatherBlockBuffer.at(i);
    if (!cur.empty()) {
      available += cur.at(0)->size() - _gatherBlockPos.at(i).second;
      for (size_t j = 1; j < cur.size(); j++) {
        available += cur.at(j)->size();
      }
    }
  }

  if (available == 0) {
    _done = true;
    traceGetSomeEnd(nullptr);
    return nullptr;
  }

  size_t toSend = (std::min)(available, atMost);  // nr rows in outgoing block

  // the following is similar to AqlItemBlock's slice method . . .
  std::unordered_map<AqlValue, AqlValue> cache;

  // comparison function
  OurLessThan ourLessThan(_trx, _gatherBlockBuffer, _sortRegisters);
  AqlItemBlock* example = _gatherBlockBuffer.at(index).front();
  size_t nrRegs = example->getNrRegs();

  auto res = std::make_unique<AqlItemBlock>(
      toSend, static_cast<arangodb::aql::RegisterId>(nrRegs));
  // automatically deleted if things go wrong

  for (size_t i = 0; i < toSend; i++) {
    // get the next smallest row from the buffer . . .
    std::pair<size_t, size_t> val = *(std::min_element(
        _gatherBlockPos.begin(), _gatherBlockPos.end(), ourLessThan));

    // copy the row in to the outgoing block . . .
    for (RegisterId col = 0; col < nrRegs; col++) {
      AqlValue const& x(
          _gatherBlockBuffer.at(val.first).front()->getValue(val.second, col));
      if (!x.isEmpty()) {
        auto it = cache.find(x);

        if (it == cache.end()) {
          AqlValue y = x.clone();
          try {
            res->setValue(i, col, y);
          } catch (...) {
            y.destroy();
            throw;
          }
          cache.emplace(x, y);
        } else {
          res->setValue(i, col, it->second);
        }
      }
    }

    // renew the _gatherBlockPos and clean up the buffer if necessary
    _gatherBlockPos.at(val.first).second++;
    if (_gatherBlockPos.at(val.first).second ==
        _gatherBlockBuffer.at(val.first).front()->size()) {
      AqlItemBlock* cur = _gatherBlockBuffer.at(val.first).front();
      delete cur;
      _gatherBlockBuffer.at(val.first).pop_front();
      _gatherBlockPos.at(val.first) = std::make_pair(val.first, 0);
    }
  }

  traceGetSomeEnd(res.get());
  return res.release();

  // cppcheck-suppress style
  DEBUG_END_BLOCK();
}
AqlItemBlock* ShortestPathBlock::getSome(size_t, size_t atMost) {
  DEBUG_BEGIN_BLOCK();
  if (_done) {
    return nullptr;
  }

  if (_buffer.empty()) {
    size_t toFetch = (std::min)(DefaultBatchSize(), atMost);
    if (!ExecutionBlock::getBlock(toFetch, toFetch)) {
      _done = true;
      return nullptr;
    }
    _pos = 0;  // this is in the first block
  }

  // If we get here, we do have _buffer.front()
  AqlItemBlock* cur = _buffer.front();
  size_t const curRegs = cur->getNrRegs();

  // Collect the next path:
  if (_posInPath >= _pathLength) {
    if (!nextPath(cur)) {
      // This input does not have any path. maybe the next one has.
      // we can only return nullptr iff the buffer is empty.
      if (++_pos >= cur->size()) {
        _buffer.pop_front();  // does not throw
        delete cur;
        _pos = 0;
      }
      return getSome(atMost, atMost);
    }
  }

  size_t available = _pathLength - _posInPath;
  size_t toSend = (std::min)(atMost, available);

  RegisterId nrRegs =
      getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()];
  std::unique_ptr<AqlItemBlock> res(requestBlock(toSend, nrRegs));
  // automatically freed if we throw
  TRI_ASSERT(curRegs <= res->getNrRegs());

  // only copy 1st row of registers inherited from previous frame(s)
  inheritRegisters(cur, res.get(), _pos);

  // TODO this might be optimized in favor of direct mptr.
  VPackBuilder resultBuilder;
  for (size_t j = 0; j < toSend; j++) {
    if (usesVertexOutput()) {
      // TODO this might be optimized in favor of direct mptr.
      resultBuilder.clear();
      _path->vertexToVelocyPack(_trx, _posInPath, resultBuilder);
      res->setValue(j, _vertexReg, AqlValue(resultBuilder.slice()));
    }
    if (usesEdgeOutput()) {
      // TODO this might be optimized in favor of direct mptr.
      resultBuilder.clear();
      _path->edgeToVelocyPack(_trx, _posInPath, resultBuilder);
      res->setValue(j, _edgeReg, AqlValue(resultBuilder.slice()));
    }
    if (j > 0) {
      // re-use already copied aqlvalues
      res->copyValuesFromFirstRow(j, static_cast<RegisterId>(curRegs));
    }
    ++_posInPath;
  }

  if (_posInPath >= _pathLength) {
    // Advance read position for next call
    if (++_pos >= cur->size()) {
      _buffer.pop_front();  // does not throw
      delete cur;
      _pos = 0;
    }
  }

  // Clear out registers no longer needed later:
  clearRegisters(res.get());
  return res.release();
  DEBUG_END_BLOCK();
}