VPackMessageNoOwnBuffer VppResponse::prepareForNetwork() { // initalize builder with vpackbuffer. then we do not need to // steal the header and can avoid the shared pointer VPackBuilder builder; builder.openArray(); builder.add(VPackValue(int(1))); builder.add(VPackValue(int(2))); // 2 == response builder.add( VPackValue(static_cast<int>(meta::underlyingValue(_responseCode)))); builder.close(); _header = builder.steal(); if (_vpackPayloads.empty()) { if (_generateBody) { LOG_TOPIC(INFO, Logger::REQUESTS) << "Response should generate body but no Data available"; _generateBody = false; // no body availalbe } return VPackMessageNoOwnBuffer(VPackSlice(_header->data()), VPackSlice::noneSlice(), _messageId, _generateBody); } else { std::vector<VPackSlice> slices; for (auto const& buffer : _vpackPayloads) { slices.emplace_back(buffer.data()); } return VPackMessageNoOwnBuffer(VPackSlice(_header->data()), std::move(slices), _messageId, _generateBody); } }
/// @brief create an AqlValue from a vector of AqlItemBlock*s AqlValue AqlValue::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src, std::vector<std::string> const& variableNames) { VPackBuilder builder; builder.openArray(); for (auto const& current : src) { RegisterId const n = current->getNrRegs(); std::vector<RegisterId> registers; for (RegisterId j = 0; j < n; ++j) { // temporaries don't have a name and won't be included if (!variableNames[j].empty()) { registers.emplace_back(j); } } for (size_t i = 0; i < current->size(); ++i) { builder.openObject(); // only enumerate the registers that are left for (auto const& reg : registers) { builder.add(VPackValue(variableNames[reg])); current->getValueReference(i, reg).toVelocyPack(trx, builder, false); } builder.close(); } } builder.close(); return AqlValue(builder); }
void SingleServerTraversalPath::pathToVelocyPack(arangodb::Transaction* trx, VPackBuilder& result) { result.openObject(); result.add(VPackValue("edges")); result.openArray(); for (auto const& it : _path.edges) { auto cached = _traverser->_edges.find(it); // All edges are cached!! TRI_ASSERT(cached != _traverser->_edges.end()); result.addExternal((*cached).second); } result.close(); result.add(VPackValue("vertices")); result.openArray(); for (auto const& it : _path.vertices) { result.add(_traverser->fetchVertexData(it).slice()); } result.close(); result.close(); }
/// @brief create an AqlValue from a vector of AqlItemBlock*s AqlValue AqlValue::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector<AqlItemBlock*> const& src, arangodb::aql::RegisterId expressionRegister) { VPackBuilder builder; builder.openArray(); for (auto const& current : src) { for (size_t i = 0; i < current->size(); ++i) { current->getValueReference(i, expressionRegister).toVelocyPack(trx, builder, false); } } builder.close(); return AqlValue(builder); }
void AuthInfo::insertInitial() { if (!_authInfo.empty()) { return; } try { VPackBuilder builder; builder.openArray(); // The only users object builder.add(VPackValue(VPackValueType::Object)); // username builder.add("user", VPackValue("root")); builder.add("authData", VPackValue(VPackValueType::Object)); // simple auth builder.add("simple", VPackValue(VPackValueType::Object)); builder.add("method", VPackValue("sha256")); char const* salt = "c776f5f4"; builder.add("salt", VPackValue(salt)); char const* hash = "ef74bc6fd59ac713bf5929c5ac2f42233e50d4d58748178132ea46dec433bd5b"; builder.add("hash", VPackValue(hash)); builder.close(); // simple builder.add("active", VPackValue(true)); builder.close(); // authData builder.add("databases", VPackValue(VPackValueType::Object)); builder.add("*", VPackValue("rw")); builder.close(); builder.close(); // The user object builder.close(); // The Array populate(builder.slice()); } catch (...) { // No action } }
void VelocyPackHelper::SanitizeExternals(VPackSlice const input, VPackBuilder& output) { if (input.isExternal()) { output.add(input.resolveExternal()); } else if (input.isObject()) { output.openObject(); for (auto const& it : VPackObjectIterator(input)) { output.add(VPackValue(it.key.copyString())); SanitizeExternals(it.value, output); } output.close(); } else if (input.isArray()) { output.openArray(); for (auto const& it : VPackArrayIterator(input)) { SanitizeExternals(it, output); } output.close(); } else { output.add(input); } }
/// @brief toJson, transfer a whole AqlItemBlock to Json, the result can /// be used to recreate the AqlItemBlock via the Json constructor /// Here is a description of the data format: The resulting Json has /// the following attributes: /// "nrItems": the number of rows of the AqlItemBlock /// "nrRegs": the number of registers of the AqlItemBlock /// "error": always set to false /// "data": this contains the actual data in the form of a list of /// numbers. The AqlItemBlock is stored columnwise, starting /// from the first column (top to bottom) and going right. /// Each entry found is encoded in the following way: /// 0.0 means a single empty entry /// -1.0 followed by a positive integer N (encoded as number) /// means a run of that many empty entries /// -2.0 followed by two numbers LOW and HIGH means a range /// and LOW and HIGH are the boundaries (inclusive) /// 1.0 means a JSON entry at the "next" position in "raw" /// the "next" position starts with 2 and is increased /// by one for every 1.0 found in data /// integer values >= 2.0 mean a JSON entry, in this /// case the "raw" list contains an entry in the /// corresponding position /// "raw": List of actual values, positions 0 and 1 are always null /// such that actual indices start at 2 void AqlItemBlock::toVelocyPack(arangodb::AqlTransaction* trx, VPackBuilder& result) const { VPackBuilder data; data.openArray(); VPackBuilder raw; raw.openArray(); // Two nulls in the beginning such that indices start with 2 raw.add(VPackValue(VPackValueType::Null)); raw.add(VPackValue(VPackValueType::Null)); std::unordered_map<AqlValue, size_t> table; // remember duplicates size_t emptyCount = 0; // here we count runs of empty AqlValues auto commitEmpties = [&]() { // this commits an empty run to the data if (emptyCount > 0) { if (emptyCount == 1) { data.add(VPackValue(0)); } else { data.add(VPackValue(-1)); data.add(VPackValue(emptyCount)); } emptyCount = 0; } }; size_t pos = 2; // write position in raw for (RegisterId column = 0; column < _nrRegs; column++) { for (size_t i = 0; i < _nrItems; i++) { AqlValue const& a(_data[i * _nrRegs + column]); if (a.isEmpty()) { emptyCount++; } else { commitEmpties(); if (a.isRange()) { data.add(VPackValue(-2)); data.add(VPackValue(a.range()->_low)); data.add(VPackValue(a.range()->_high)); } else { auto it = table.find(a); if (it == table.end()) { a.toVelocyPack(trx, raw, false); data.add(VPackValue(1)); table.emplace(a, pos++); } else { data.add(VPackValue(it->second)); } } } } } commitEmpties(); raw.close(); data.close(); result.add("nrItems", VPackValue(_nrItems)); result.add("nrRegs", VPackValue(_nrRegs)); result.add("data", data.slice()); result.add("raw", raw.slice()); result.add("error", VPackValue(false)); result.add("exhausted", VPackValue(false)); }
int RocksDBFeature::dropPrefix(std::string const& prefix) { if (!isEnabled()) { return TRI_ERROR_NO_ERROR; } TRI_ASSERT(Instance != nullptr); try { VPackBuilder builder; // create lower and upper bound for deletion builder.openArray(); builder.add(VPackSlice::minKeySlice()); builder.close(); std::string l; l.reserve(prefix.size() + builder.slice().byteSize()); l.append(prefix); // extend the prefix to at least 24 bytes while (l.size() < RocksDBIndex::keyPrefixSize()) { uint64_t value = 0; l.append(reinterpret_cast<char const*>(&value), sizeof(uint64_t)); } l.append(builder.slice().startAs<char const>(), builder.slice().byteSize()); builder.clear(); builder.openArray(); builder.add(VPackSlice::maxKeySlice()); builder.close(); std::string u; u.reserve(prefix.size() + builder.slice().byteSize()); u.append(prefix); // extend the prefix to at least 24 bytes while (u.size() < RocksDBIndex::keyPrefixSize()) { uint64_t value = UINT64_MAX; u.append(reinterpret_cast<char const*>(&value), sizeof(uint64_t)); } u.append(builder.slice().startAs<char const>(), builder.slice().byteSize()); #if 0 for (size_t i = 0; i < prefix.size(); i += sizeof(TRI_idx_iid_t)) { char const* x = prefix.c_str() + i; size_t o; char* q = TRI_EncodeHexString(x, 8, &o); if (q != nullptr) { LOG(TRACE) << "RocksDB prefix part: " << q; TRI_FreeString(TRI_CORE_MEM_ZONE, q); } } LOG(TRACE) << "dropping RocksDB range: " << VPackSlice(l.c_str() + RocksDBIndex::keyPrefixSize()).toJson() << " - " << VPackSlice(u.c_str() + RocksDBIndex::keyPrefixSize()).toJson(); #endif // delete files in range lower..upper rocksdb::Slice lower(l.c_str(), l.size()); rocksdb::Slice upper(u.c_str(), u.size()); { rocksdb::Status status = rocksdb::DeleteFilesInRange(_db->GetBaseDB(), _db->GetBaseDB()->DefaultColumnFamily(), &lower, &upper); if (!status.ok()) { // if file deletion failed, we will still iterate over the remaining keys, so we // don't need to abort and raise an error here LOG(WARN) << "RocksDB file deletion failed"; } } // go on and delete the remaining keys (delete files in range does not necessarily // find them all, just complete files) auto comparator = RocksDBFeature::instance()->comparator(); rocksdb::DB* db = _db->GetBaseDB(); rocksdb::WriteBatch batch; std::unique_ptr<rocksdb::Iterator> it(db->NewIterator(rocksdb::ReadOptions())); it->Seek(lower); while (it->Valid()) { int res = comparator->Compare(it->key(), upper); if (res >= 0) { break; } batch.Delete(it->key()); it->Next(); } // now apply deletion batch rocksdb::Status status = db->Write(rocksdb::WriteOptions(), &batch); if (!status.ok()) { LOG(WARN) << "RocksDB key deletion failed: " << status.ToString(); return TRI_ERROR_INTERNAL; } return TRI_ERROR_NO_ERROR; } catch (arangodb::basics::Exception const& ex) { LOG(ERR) << "caught exception during RocksDB key prefix deletion: " << ex.what(); return ex.code(); } catch (std::exception const& ex) { LOG(ERR) << "caught exception during RocksDB key prefix deletion: " << ex.what(); return TRI_ERROR_INTERNAL; } catch (...) { LOG(ERR) << "caught unknown exception during RocksDB key prefix deletion"; return TRI_ERROR_INTERNAL; } }
void arangodb::traverser::TraverserOptions::buildEngineInfo(VPackBuilder& result) const { result.openObject(); result.add("minDepth", VPackValue(minDepth)); result.add("maxDepth", VPackValue(maxDepth)); result.add("bfs", VPackValue(useBreadthFirst)); result.add(VPackValue("uniqueVertices")); switch (uniqueVertices) { case UniquenessLevel::NONE: result.add(VPackValue(0)); break; case UniquenessLevel::PATH: result.add(VPackValue(1)); break; case UniquenessLevel::GLOBAL: result.add(VPackValue(2)); break; } result.add(VPackValue("uniqueEdges")); switch (uniqueEdges) { case UniquenessLevel::NONE: result.add(VPackValue(0)); break; case UniquenessLevel::PATH: result.add(VPackValue(1)); break; case UniquenessLevel::GLOBAL: result.add(VPackValue(2)); break; } result.add(VPackValue("baseLookupInfos")); result.openArray(); for (auto const& it: _baseLookupInfos) { it.buildEngineInfo(result); } result.close(); if (!_depthLookupInfo.empty()) { result.add(VPackValue("depthLookupInfo")); result.openObject(); for (auto const& pair : _depthLookupInfo) { result.add(VPackValue(basics::StringUtils::itoa(pair.first))); result.openArray(); for (auto const& it : pair.second) { it.buildEngineInfo(result); } result.close(); } result.close(); } if (!_vertexExpressions.empty()) { result.add(VPackValue("vertexExpressions")); result.openObject(); for (auto const& pair : _vertexExpressions) { result.add(VPackValue(basics::StringUtils::itoa(pair.first))); result.openObject(); result.add(VPackValue("expression")); pair.second->toVelocyPack(result, true); result.close(); } result.close(); } if (_baseVertexExpression != nullptr) { result.add(VPackValue("baseVertexExpression")); result.openObject(); result.add(VPackValue("expression")); _baseVertexExpression->toVelocyPack(result, true); result.close(); } result.add(VPackValue("tmpVar")); _tmpVar->toVelocyPack(result); result.close(); }
// Internal function to receive all edges for a list of vertices // Not publicly documented on purpose. // NOTE: It ONLY except _id strings. Nothing else bool RestEdgesHandler::readEdgesForMultipleVertices() { std::vector<std::string> const& suffix = _request->suffix(); if (suffix.size() != 1) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expected POST " + EDGES_PATH + "/<collection-identifier>?direction=<direction>"); return false; } bool parseSuccess = true; std::shared_ptr<VPackBuilder> parsedBody = parseVelocyPackBody(&VPackOptions::Defaults, parseSuccess); if (!parseSuccess) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expected POST " + EDGES_PATH + "/<collection-identifier>?direction=<direction>"); // A body is required return false; } VPackSlice body = parsedBody->slice(); if (!body.isArray()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "Expected an array of vertex _id's in body parameter"); return false; } std::string collectionName = suffix[0]; CollectionNameResolver resolver(_vocbase); TRI_col_type_e colType = resolver.getCollectionTypeCluster(collectionName); if (colType == TRI_COL_TYPE_UNKNOWN) { generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); return false; } else if (colType != TRI_COL_TYPE_EDGE) { generateError(rest::ResponseCode::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID); return false; } bool found; std::string dirString = _request->value("direction", found); if (!found || dirString.empty()) { dirString = "any"; } TRI_edge_direction_e direction; if (dirString == "any") { direction = TRI_EDGE_ANY; } else if (dirString == "out" || dirString == "outbound") { direction = TRI_EDGE_OUT; } else if (dirString == "in" || dirString == "inbound") { direction = TRI_EDGE_IN; } else { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "<direction> must by any, in, or out, not: " + dirString); return false; } std::vector<traverser::TraverserExpression*> const expressions; if (ServerState::instance()->isCoordinator()) { rest::ResponseCode responseCode; VPackBuilder resultDocument; resultDocument.openObject(); for (auto const& it : VPackArrayIterator(body)) { if (it.isString()) { std::string vertexString(it.copyString()); int res = getFilteredEdgesOnCoordinator( _vocbase->name(), collectionName, vertexString, direction, expressions, responseCode, resultDocument); if (res != TRI_ERROR_NO_ERROR) { generateError(responseCode, res); return false; } } } resultDocument.add("error", VPackValue(false)); resultDocument.add("code", VPackValue(200)); resultDocument.close(); generateResult(rest::ResponseCode::OK, resultDocument.slice()); return true; } // find and load collection given by name or identifier SingleCollectionTransaction trx( StandaloneTransactionContext::Create(_vocbase), collectionName, TRI_TRANSACTION_READ); // ............................................................................. // inside read transaction // ............................................................................. int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { generateTransactionError(collectionName, res, ""); return false; } // If we are a DBserver, we want to use the cluster-wide collection // name for error reporting: if (ServerState::instance()->isDBServer()) { collectionName = trx.resolver()->getCollectionName(trx.cid()); } size_t filtered = 0; size_t scannedIndex = 0; VPackBuilder resultBuilder; resultBuilder.openObject(); // build edges resultBuilder.add(VPackValue("edges")); // only key resultBuilder.openArray(); bool ok = getEdgesForVertexList(body, expressions, direction, trx, resultBuilder, scannedIndex, filtered); if (!ok) { // Ignore the error } resultBuilder.close(); res = trx.finish(res); if (res != TRI_ERROR_NO_ERROR) { generateTransactionError(collectionName, res, ""); return false; } resultBuilder.add("error", VPackValue(false)); resultBuilder.add("code", VPackValue(200)); resultBuilder.add("stats", VPackValue(VPackValueType::Object)); resultBuilder.add("scannedIndex", VPackValue(scannedIndex)); resultBuilder.add("filtered", VPackValue(filtered)); resultBuilder.close(); // inner object resultBuilder.close(); // and generate a response generateResult(rest::ResponseCode::OK, resultBuilder.slice(), trx.transactionContext()); return true; }
// read in- or outbound edges bool RestEdgesHandler::readEdges( std::vector<traverser::TraverserExpression*> const& expressions) { std::vector<std::string> const& suffix = _request->suffix(); if (suffix.size() != 1) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "expected GET " + EDGES_PATH + "/<collection-identifier>?vertex=<vertex-handle>&" "direction=<direction>"); return false; } std::string collectionName = suffix[0]; CollectionNameResolver resolver(_vocbase); TRI_col_type_e colType = resolver.getCollectionTypeCluster(collectionName); if (colType == TRI_COL_TYPE_UNKNOWN) { generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); return false; } if (colType != TRI_COL_TYPE_EDGE) { generateError(rest::ResponseCode::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID); return false; } bool found; std::string dir = _request->value("direction", found); if (!found || dir.empty()) { dir = "any"; } std::string dirString(dir); TRI_edge_direction_e direction; if (dirString == "any") { direction = TRI_EDGE_ANY; } else if (dirString == "out" || dirString == "outbound") { direction = TRI_EDGE_OUT; } else if (dirString == "in" || dirString == "inbound") { direction = TRI_EDGE_IN; } else { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "<direction> must by any, in, or out, not: " + dirString); return false; } std::string const& startVertex = _request->value("vertex", found); if (!found || startVertex.empty()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD, "illegal document handle"); return false; } if (ServerState::instance()->isCoordinator()) { std::string vertexString(startVertex); rest::ResponseCode responseCode; VPackBuilder resultDocument; resultDocument.openObject(); int res = getFilteredEdgesOnCoordinator( _vocbase->name(), collectionName, vertexString, direction, expressions, responseCode, resultDocument); if (res != TRI_ERROR_NO_ERROR) { generateError(responseCode, res); return false; } resultDocument.add("error", VPackValue(false)); resultDocument.add("code", VPackValue(200)); resultDocument.close(); generateResult(rest::ResponseCode::OK, resultDocument.slice()); return true; } // find and load collection given by name or identifier SingleCollectionTransaction trx( StandaloneTransactionContext::Create(_vocbase), collectionName, TRI_TRANSACTION_READ); // ............................................................................. // inside read transaction // ............................................................................. int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { generateTransactionError(collectionName, res, ""); return false; } size_t filtered = 0; size_t scannedIndex = 0; VPackBuilder resultBuilder; resultBuilder.openObject(); // build edges resultBuilder.add(VPackValue("edges")); // only key resultBuilder.openArray(); // NOTE: collecitonName is the shard-name in DBServer case bool ok = getEdgesForVertex(startVertex, collectionName, expressions, direction, trx, resultBuilder, scannedIndex, filtered); resultBuilder.close(); res = trx.finish(res); if (!ok) { // Error has been built internally return false; } if (res != TRI_ERROR_NO_ERROR) { if (ServerState::instance()->isDBServer()) { // If we are a DBserver, we want to use the cluster-wide collection // name for error reporting: collectionName = trx.resolver()->getCollectionName(trx.cid()); } generateTransactionError(collectionName, res, ""); return false; } resultBuilder.add("error", VPackValue(false)); resultBuilder.add("code", VPackValue(200)); resultBuilder.add("stats", VPackValue(VPackValueType::Object)); resultBuilder.add("scannedIndex", VPackValue(scannedIndex)); resultBuilder.add("filtered", VPackValue(filtered)); resultBuilder.close(); // inner object resultBuilder.close(); // and generate a response generateResult(rest::ResponseCode::OK, resultBuilder.slice(), trx.transactionContext()); return true; }