diff --git a/arangod/Aql/CollectBlock.cpp b/arangod/Aql/CollectBlock.cpp index 3f001a438a..7a1e794d0d 100644 --- a/arangod/Aql/CollectBlock.cpp +++ b/arangod/Aql/CollectBlock.cpp @@ -1016,8 +1016,15 @@ int DistinctCollectBlock::getOrSkipSome(size_t atLeast, size_t atMost, } TRI_ASSERT(cur != nullptr); - res->shrink(skipped); - } + if (skipped > 0) { + res->shrink(skipped); + } + else { + AqlItemBlock* block = res.get(); + returnBlock(block); + res.release(); + } + } returnBlock(cur); _done = true; result = res.release(); @@ -1035,8 +1042,14 @@ int DistinctCollectBlock::getOrSkipSome(size_t atLeast, size_t atMost, } if (!skipping) { - TRI_ASSERT(skipped > 0); - res->shrink(skipped); + if (skipped > 0) { + res->shrink(skipped); + } + else { + AqlItemBlock* block = res.get(); + returnBlock(block); + res.release(); + } } result = res.release(); diff --git a/js/server/tests/aql/aql-distinct.js b/js/server/tests/aql/aql-distinct.js index 3068f092c8..ac84e7a4de 100644 --- a/js/server/tests/aql/aql-distinct.js +++ b/js/server/tests/aql/aql-distinct.js @@ -300,7 +300,42 @@ function ahuacatlCollect () { var query = "FOR result IN UnitTestsCollection COLLECT key=result.a.key, name = result.a.name INTO group LIMIT 2,5 RETURN [key,name]"; var result = AQL_EXECUTE(query).json; assertEqual([[0,2],[0,3],[0,4],[0,5],[0,6]], result); - } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief distinct regression test: +/// crash when a subsequent call to DistinctCollectBlock::getSome() added no new +/// non-distinct results. The crash may occur ONLY IN MAINTAINER MODE, while +/// in non-maintainer mode problems may occur only because either an empty +/// result is returned (which would cause some parent blocks to crash), or +/// an erroneous result is returned, but I didn't find a way to produce this +/// reliably with an AQL query. +//////////////////////////////////////////////////////////////////////////////// + + testDistinctRegressionGetSomeWithEmptyResult : function () { + const query = 'FOR i IN 1..2 FOR k IN 1..1000 RETURN DISTINCT k'; + containsDistinct(query); + const options = {optimizer: {rules: [ "-interchange-adjacent-enumerations" ]}}; + + // check plan + const plan = helper.getCompactPlan(AQL_EXPLAIN(query, {}, options)); + assertEqual( + [ + "SingletonNode", + "CalculationNode", + "CalculationNode", + "EnumerateListNode", + "EnumerateListNode", + "CollectNode", + "ReturnNode", + ], + plan.map(node => node.type) + ); + + // execute query + const result = AQL_EXECUTE(query, {}, options).json; + assertEqual(1000, result.length); + }, }; }