diff --git a/src/engine/ExportQueryExecutionTrees.cpp b/src/engine/ExportQueryExecutionTrees.cpp index 873bd21ae7..515e08830f 100644 --- a/src/engine/ExportQueryExecutionTrees.cpp +++ b/src/engine/ExportQueryExecutionTrees.cpp @@ -784,16 +784,17 @@ ExportQueryExecutionTrees::computeResultAsQLeverJSON( qet, query.constructClause().triples_, query._limitOffset, std::move(result), std::move(cancellationHandle)); - size_t resultSize = 0; + size_t numBindingsYielded = 0; for (const std::string& b : bindings) { - if (resultSize > 0) [[likely]] { + if (numBindingsYielded > 0) [[likely]] { co_yield ","; } co_yield b; - ++resultSize; + ++numBindingsYielded; } RuntimeInformation runtimeInformation = qet.getRootOperation()->runtimeInfo(); + size_t resultSizeBeforeImplicitLimit = runtimeInformation.numRows_; runtimeInformation.addLimitOffsetRow(query._limitOffset, false); auto timeResultComputation = @@ -805,7 +806,16 @@ ExportQueryExecutionTrees::computeResultAsQLeverJSON( qet.getRootOperation()->getRuntimeInfoWholeQuery()); jsonSuffix["runtimeInformation"]["query_execution_tree"] = nlohmann::ordered_json(runtimeInformation); - jsonSuffix["resultsize"] = resultSize; + // TODO: This is a simple hack to get the result size for typical SELECT + // queries. When the final operation is lazy, it will be the size of the + // blocks produced by that operation (which is a lower bound on the size of + // the full result). For CONSTRUCT queries, it will be at most the implicit + // limit. The proper solution is to reinstate the `send` parameter + // (independently from the LIMIT), and if it is set, send only that many + // results but compute the full result size. + jsonSuffix["resultsize"] = query.hasSelectClause() + ? resultSizeBeforeImplicitLimit + : numBindingsYielded; jsonSuffix["time"]["total"] = absl::StrCat(requestTimer.msecs().count(), "ms"); jsonSuffix["time"]["computeResult"] = diff --git a/test/ExportQueryExecutionTreesTest.cpp b/test/ExportQueryExecutionTreesTest.cpp index ed8482d66c..ce10671092 100644 --- a/test/ExportQueryExecutionTreesTest.cpp +++ b/test/ExportQueryExecutionTreesTest.cpp @@ -1067,7 +1067,7 @@ TEST(ExportQueryExecutionTrees, LimitOffset) { g )" + xmlTrailer; TestCaseSelectQuery testCaseLimitOffset{ - kg, objectQuery, 2, + kg, objectQuery, 4, // TSV "?s\n" "\n" @@ -1421,7 +1421,8 @@ TEST(ExportQueryExecutionTrees, verifyQleverJsonContainsValidMetadata) { auto& runtimeInformation = runtimeInformationWrapper["query_execution_tree"]; EXPECT_EQ(runtimeInformation["result_cols"], 3); EXPECT_EQ(runtimeInformation["result_rows"], 4); - EXPECT_EQ(json["resultsize"], 4); + // This number is an implementation detail for lazy results and may change. + EXPECT_EQ(json["resultsize"], 6); auto& timingInformation = json["time"]; EXPECT_GE(toChrono(timingInformation["total"].get()), 1ms); // Ensure result is not returned in microseconds and subsequently interpreted