diff --git a/Documentation/Books/Users/Aql/Functions.mdpp b/Documentation/Books/Users/Aql/Functions.mdpp index 32c5fbbfec..29427fdf1d 100644 --- a/Documentation/Books/Users/Aql/Functions.mdpp +++ b/Documentation/Books/Users/Aql/Functions.mdpp @@ -193,7 +193,9 @@ a numeric timestamp to a string representation and vice versa. There are two date functions in AQL to create dates for further use: -- *DATE_TIMESTAMP(date)*: Creates a UTC timestamp value from *date*. +- *DATE_TIMESTAMP(date)*: Creates a UTC timestamp value from *date*. The return + value has millisecond precision. To convert the return value to seconds, divide + it by 1000. - *DATE_TIMESTAMP(year, month, day, hour, minute, second, millisecond)*: Same as before, but allows specifying the individual date components separately. @@ -294,6 +296,8 @@ The following date functions can be used with dates created by *DATE_TIMESTAMP* The following other date functions are also available: - *DATE_NOW()*: Returns the current time as a timestamp. + The return value has millisecond precision. To convert the return value to seconds, + divide it by 1000. Note that this function is evaluated on every invocation and may return different values when invoked multiple times in the same query. diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 441678f57f..9fab3f1892 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -3758,6 +3758,23 @@ size_t ScatterBlock::skipSomeForShard (size_t atLeast, size_t atMost, std::strin return skipped; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief skipForShard +//////////////////////////////////////////////////////////////////////////////// + +bool ScatterBlock::skipForShard (size_t number, std::string const& shardId) { + size_t skipped = skipSomeForShard(number, number, shardId); + size_t nr = skipped; + while ( nr != 0 && skipped < number ){ + nr = skipSomeForShard(number - skipped, number - skipped, shardId); + skipped += nr; + } + if (nr == 0) { + return true; + } + return ! hasMoreForShard(shardId); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief getClientId: get the number (used internally) /// corresponding to @@ -3824,6 +3841,9 @@ ClusterCommResult* RemoteBlock::sendRequest ( ClientTransactionID const clientTransactionId = "AQL"; CoordTransactionID const coordTransactionId = 1; std::map headers; + if (! _ownName.empty()) { + headers.insert(make_pair("Shard-Id", _ownName)); + } std::cout << "SENDING REQUEST TO " << _server << ", URLPART: " << urlPart << ", QUERYID: " << _queryId << "\n"; return cc->syncRequest(clientTransactionId, diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index db4ebe03f0..702bf52bfb 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -1629,6 +1629,12 @@ namespace triagens { size_t skipSomeForShard (size_t atLeast, size_t atMost, std::string const& shardId); +//////////////////////////////////////////////////////////////////////////////// +/// @brief skipForShard +//////////////////////////////////////////////////////////////////////////////// + + bool skipForShard (size_t number, std::string const& shardId); + private: //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 3d6dabaf57..6034e0cb79 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -209,7 +209,7 @@ ExecutionNode::ExecutionNode (ExecutionPlan* plan, _id(JsonHelper::checkAndGetNumericValue(json.json(), "id")), _estimatedCost(0.0), _estimatedCostSet(false), - _varUsageValid(false), + _varUsageValid(true), _plan(plan), _depth(JsonHelper::checkAndGetNumericValue(json.json(), "depth")) { @@ -1854,6 +1854,7 @@ void ReturnNode::toJsonHelper (triagens::basics::Json& nodes, //////////////////////////////////////////////////////////////////////////////// /// @brief clone ExecutionNode recursively //////////////////////////////////////////////////////////////////////////////// + ExecutionNode* ReturnNode::clone (ExecutionPlan* plan, bool withDependencies, bool withProperties) const { diff --git a/arangod/Aql/RestAqlHandler.cpp b/arangod/Aql/RestAqlHandler.cpp index ea4b5a9638..6bdffadd85 100644 --- a/arangod/Aql/RestAqlHandler.cpp +++ b/arangod/Aql/RestAqlHandler.cpp @@ -488,6 +488,13 @@ void RestAqlHandler::getInfoQuery (std::string const& operation, std::string const& idString) { // the GET verb + bool found; + std::string shardId; + char const* shardIdCharP = _request->header("shard-id", found); + if (found && shardIdCharP != nullptr) { + shardId = shardIdCharP; + } + QueryId qId; Query* query = nullptr; if (findQuery(idString, qId, query)) { @@ -498,33 +505,69 @@ void RestAqlHandler::getInfoQuery (std::string const& operation, TRI_ASSERT(query->engine() != nullptr); - int64_t number; - if (operation == "count") { - number = query->engine()->count(); - if (number == -1) { - answerBody("count", Json("unknown")); + try { + int64_t number; + if (operation == "count") { + number = query->engine()->count(); + if (number == -1) { + answerBody("count", Json("unknown")); + } + else { + answerBody("count", Json(static_cast(number))); + } + } + else if (operation == "remaining") { + // FIXME: + // Do the !shardId.empty() case once the ScatterBlock has remainingForShard + number = query->engine()->remaining(); + if (number == -1) { + answerBody("remaining", Json("unknown")); + } + else { + answerBody("remaining", Json(static_cast(number))); + } + } + else if (operation == "hasMore") { + bool hasMore; + if (shardId.empty()) { + hasMore = query->engine()->hasMore(); + } + else { + auto scatter = static_cast(query->engine()->root()); + if (scatter->getPlanNode()->getType() != ExecutionNode::SCATTER) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + hasMore = scatter->hasMoreForShard(shardId); + } + + answerBody("hasMore", Json(hasMore)); } else { - answerBody("count", Json(static_cast(number))); + _queryRegistry->close(_vocbase, qId); + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + return; } } - else if (operation == "remaining") { - number = query->engine()->remaining(); - if (number == -1) { - answerBody("remaining", Json("unknown")); - } - else { - answerBody("remaining", Json(static_cast(number))); - } - } - else if (operation == "hasMore") { - bool hasMore = query->engine()->hasMore(); - answerBody("hasMore", Json(hasMore)); - } - else { + catch (triagens::arango::Exception const& ex) { _queryRegistry->close(_vocbase, qId); - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - return; + + generateError(HttpResponse::SERVER_ERROR, + ex.code(), + ex.message()); + } + catch (std::exception const& ex) { + _queryRegistry->close(_vocbase, qId); + + generateError(HttpResponse::SERVER_ERROR, + TRI_ERROR_HTTP_SERVER_ERROR, + ex.what()); + } + catch (...) { + _queryRegistry->close(_vocbase, qId); + + generateError(HttpResponse::SERVER_ERROR, + TRI_ERROR_HTTP_SERVER_ERROR, + "an unknown exception occurred"); } _queryRegistry->close(_vocbase, qId); @@ -661,7 +704,7 @@ void RestAqlHandler::handleUseQuery (std::string const& operation, bool found; std::string shardId; char const* shardIdCharP = _request->header("shard-id", found); - if (shardIdCharP != nullptr) { + if (found && shardIdCharP != nullptr) { shardId = shardIdCharP; } @@ -706,7 +749,16 @@ std::cout << "ANSWERBODY: " << JsonHelper::toString(answerBody.json()) << "\n\n" "atMost", ExecutionBlock::DefaultBatchSize); size_t skipped; try { - skipped = query->engine()->skipSome(atLeast, atMost); + if (shardId.empty()) { + skipped = query->engine()->skipSome(atLeast, atMost); + } + else { + auto scatter = static_cast(query->engine()->root()); + if (scatter->getPlanNode()->getType() != ExecutionNode::SCATTER) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + skipped = scatter->skipSomeForShard(atLeast, atMost, shardId); + } } catch (...) { generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_HTTP_SERVER_ERROR, @@ -720,7 +772,18 @@ std::cout << "ANSWERBODY: " << JsonHelper::toString(answerBody.json()) << "\n\n" auto number = JsonHelper::getNumericValue(queryJson.json(), "number", 1); try { - bool exhausted = query->engine()->skip(number); + bool exhausted; + if (shardId.empty()) { + exhausted = query->engine()->skip(number); + } + else { + auto scatter = static_cast(query->engine()->root()); + if (scatter->getPlanNode()->getType() != ExecutionNode::SCATTER) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + exhausted = scatter->skipForShard(number, shardId); + } + answerBody("exhausted", Json(exhausted)) ("error", Json(false)); }