diff --git a/tests/Aql/HashedCollectExecutorTest.cpp b/tests/Aql/HashedCollectExecutorTest.cpp index 6eeba487e6..d53662dc04 100644 --- a/tests/Aql/HashedCollectExecutorTest.cpp +++ b/tests/Aql/HashedCollectExecutorTest.cpp @@ -857,6 +857,346 @@ TEST_F(HashedCollectExecutorTestRowsNoCount, test_produce_datarange_3) { ASSERT_EQ(myNumbers.at(2), 3); } +TEST_F(HashedCollectExecutorTestRowsNoCount, test_produce_datarange_4) { + // This fetcher will not be called! + // After Execute is done this fetcher shall be removed, the Executor does not need it anymore! + auto fakeUnusedBlock = VPackParser::fromJson("[ ]"); + SingleRowFetcherHelper<::arangodb::aql::BlockPassthrough::Disable> fetcher( + itemBlockManager, fakeUnusedBlock->steal(), false); + + // This is the relevant part of the test + HashedCollectExecutor testee(fetcher, infos); + SharedAqlItemBlockPtr inBlock = + buildBlock<1>(itemBlockManager, {{R"(1)"}, {R"(2)"}, {R"(1)"}, {R"(2)"}}); + AqlItemBlockInputRange input{ExecutorState::DONE, inBlock, 0, inBlock->size()}; + + OutputAqlItemRow output(std::move(block), infos.getOutputRegisters(), + infos.registersToKeep(), infos.registersToClear()); + EXPECT_EQ(output.numRowsWritten(), 0); + + auto const [state, stats, call] = testee.produceRows(1000, input, output); + EXPECT_EQ(state, ExecutorState::DONE); + EXPECT_EQ(output.numRowsWritten(), 2); + + std::vector myNumbers; + auto block = output.stealBlock(); + + // check for types + AqlValue x = block->getValue(0, 1); + ASSERT_TRUE(x.isNumber()); + myNumbers.emplace_back(x.slice().getInt()); + + AqlValue y = block->getValue(1, 1); + ASSERT_TRUE(y.isNumber()); + myNumbers.emplace_back(y.slice().getInt()); + + // now sort vector and check for appearances + std::sort(myNumbers.begin(), myNumbers.end()); + ASSERT_EQ(myNumbers.at(0), 1); + ASSERT_EQ(myNumbers.at(1), 2); +} + +TEST(HashedCollectExecutorTestRowsCount, test_produce_datarange_sum) { + ResourceMonitor monitor; + AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; + + mocks::MockAqlServer server{}; + std::unique_ptr fakedQuery = server.createFakeQuery(); + arangodb::transaction::Methods* trx = fakedQuery->trx(); + + std::unordered_set regToClear; + std::unordered_set regToKeep; + std::vector> groupRegisters; + groupRegisters.emplace_back(std::make_pair(1, 0)); + + std::unordered_set readableInputRegisters; + readableInputRegisters.insert(0); + + std::unordered_set writeableOutputRegisters; + writeableOutputRegisters.insert(1); + + RegisterId nrOutputRegister = 3; + + std::vector> aggregateRegisters; + aggregateRegisters.emplace_back(std::make_pair(1, 0)); + + std::vector aggregateTypes; + aggregateTypes.emplace_back("SUM"); + + // if count = true, then we need to set a valid countRegister + bool count = true; + RegisterId collectRegister = 2; + writeableOutputRegisters.insert(2); + + SharedAqlItemBlockPtr block{new AqlItemBlock(itemBlockManager, 1000, nrOutputRegister)}; + HashedCollectExecutorInfos infos(1, nrOutputRegister, regToClear, regToKeep, + std::move(readableInputRegisters), + std::move(writeableOutputRegisters), + std::move(groupRegisters), collectRegister, + std::move(aggregateTypes), + std::move(aggregateRegisters), trx, count); + + // This fetcher will not be called! + // After Execute is done this fetcher shall be removed, the Executor does not need it anymore! + auto fakeUnusedBlock = VPackParser::fromJson("[ ]"); + SingleRowFetcherHelper<::arangodb::aql::BlockPassthrough::Disable> fetcher( + itemBlockManager, fakeUnusedBlock->steal(), false); + + // This is the relevant part of the test + HashedCollectExecutor testee(fetcher, infos); + SharedAqlItemBlockPtr inBlock = buildBlock<1>(itemBlockManager, {{R"(1)"}, {R"(2)"}}); + AqlItemBlockInputRange input{ExecutorState::DONE, inBlock, 0, inBlock->size()}; + + OutputAqlItemRow output(std::move(block), infos.getOutputRegisters(), + infos.registersToKeep(), infos.registersToClear()); + + EXPECT_EQ(output.numRowsWritten(), 0); + auto const [state, stats, call] = testee.produceRows(1000, input, output); + EXPECT_EQ(state, ExecutorState::DONE); + EXPECT_EQ(output.numRowsWritten(), 2); + + std::vector myNumbers; + std::vector myCountNumbers; + auto newBlock = output.stealBlock(); + + // check for types + AqlValue x = newBlock->getValue(0, 1); + ASSERT_TRUE(x.isNumber()); + myNumbers.emplace_back(x.slice().getInt()); + + // Check the count register + AqlValue xx = newBlock->getValue(0, 2); + ASSERT_TRUE(xx.isNumber()); + myCountNumbers.emplace_back(xx.slice().getDouble()); + + AqlValue z = newBlock->getValue(1, 1); + ASSERT_TRUE(z.isNumber()); + myNumbers.emplace_back(z.slice().getInt()); + + // Check the count register + AqlValue zz = newBlock->getValue(1, 2); + ASSERT_TRUE(zz.isNumber()); + myCountNumbers.emplace_back(zz.slice().getDouble()); + + // now sort vector and check for appearances + std::sort(myNumbers.begin(), myNumbers.end()); + + std::sort(myCountNumbers.begin(), myCountNumbers.end()); + ASSERT_EQ(myNumbers.at(0), 1); + ASSERT_EQ(myNumbers.at(1), 2); + ASSERT_EQ(myCountNumbers.at(0), 1); + ASSERT_EQ(myCountNumbers.at(1), 2); +} + +TEST(HashedCollectExecutorTestRowsCountNumbers, test_produce_datarange_sum_length) { + ResourceMonitor monitor; + AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; + + mocks::MockAqlServer server{}; + std::unique_ptr fakedQuery = server.createFakeQuery(); + arangodb::transaction::Methods* trx = fakedQuery->trx(); + + std::unordered_set regToClear; + std::unordered_set regToKeep; + std::vector> groupRegisters; + groupRegisters.emplace_back(std::make_pair(1, 0)); + + std::unordered_set readableInputRegisters; + readableInputRegisters.insert(0); + + std::unordered_set writeableOutputRegisters; + writeableOutputRegisters.insert(1); + + RegisterId nrOutputRegister = 3; + + std::vector> aggregateRegisters; + aggregateRegisters.emplace_back(std::make_pair(1, 0)); + + std::vector aggregateTypes; + aggregateTypes.emplace_back("LENGTH"); + + // if count = true, then we need to set a valid countRegister + bool count = true; + RegisterId collectRegister = 2; + writeableOutputRegisters.insert(2); + + HashedCollectExecutorInfos infos(1, nrOutputRegister, regToClear, regToKeep, + std::move(readableInputRegisters), + std::move(writeableOutputRegisters), + std::move(groupRegisters), collectRegister, + std::move(aggregateTypes), + std::move(aggregateRegisters), trx, count); + SharedAqlItemBlockPtr block{new AqlItemBlock(itemBlockManager, 1000, nrOutputRegister)}; + + // This fetcher will not be called! + // After Execute is done this fetcher shall be removed, the Executor does not need it anymore! + auto fakeUnusedBlock = VPackParser::fromJson("[ ]"); + SingleRowFetcherHelper<::arangodb::aql::BlockPassthrough::Disable> fetcher( + itemBlockManager, fakeUnusedBlock->steal(), false); + + // This is the relevant part of the test + HashedCollectExecutor testee(fetcher, infos); + SharedAqlItemBlockPtr inBlock = + buildBlock<1>(itemBlockManager, {{R"(1)"}, {R"(2)"}, {R"(3)"}}); + AqlItemBlockInputRange input{ExecutorState::DONE, inBlock, 0, inBlock->size()}; + + OutputAqlItemRow output(std::move(block), infos.getOutputRegisters(), + infos.registersToKeep(), infos.registersToClear()); + + EXPECT_EQ(output.numRowsWritten(), 0); + auto const [state, stats, call] = testee.produceRows(1000, input, output); + EXPECT_EQ(state, ExecutorState::DONE); + EXPECT_EQ(output.numRowsWritten(), 3); + + std::vector myNumbers; + std::vector myCountNumbers; + auto newBlock = output.stealBlock(); + + // check for types + AqlValue x = newBlock->getValue(0, 1); + ASSERT_TRUE(x.isNumber()); + myNumbers.emplace_back(x.slice().getInt()); + + // Check the count register + AqlValue xx = newBlock->getValue(0, 2); + ASSERT_TRUE(xx.isNumber()); + myCountNumbers.emplace_back(xx.slice().getInt()); + + AqlValue z = newBlock->getValue(1, 1); + ASSERT_TRUE(z.isNumber()); + myNumbers.emplace_back(z.slice().getInt()); + + // Check the count register + AqlValue zz = newBlock->getValue(1, 2); + ASSERT_TRUE(zz.isNumber()); + myCountNumbers.emplace_back(zz.slice().getInt()); + + AqlValue y = newBlock->getValue(2, 1); + ASSERT_TRUE(y.isNumber()); + myNumbers.emplace_back(y.slice().getInt()); + + // Check the count register + AqlValue yy = newBlock->getValue(2, 2); + ASSERT_TRUE(yy.isNumber()); + myCountNumbers.emplace_back(yy.slice().getInt()); + + // now sort vector and check for appearances + std::sort(myNumbers.begin(), myNumbers.end()); + + std::sort(myCountNumbers.begin(), myCountNumbers.end()); + ASSERT_EQ(myNumbers.at(0), 1); + ASSERT_EQ(myNumbers.at(1), 2); + ASSERT_EQ(myNumbers.at(2), 3); + ASSERT_EQ(myCountNumbers.at(0), 1); + ASSERT_EQ(myCountNumbers.at(1), 1); + ASSERT_EQ(myCountNumbers.at(2), 1); +} + +TEST(HashedCollectExecutorTestRowsCountStrings, test_produce_datarange_sum_length_2) { + ResourceMonitor monitor; + AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; + + mocks::MockAqlServer server{}; + std::unique_ptr fakedQuery = server.createFakeQuery(); + arangodb::transaction::Methods* trx = fakedQuery->trx(); + + std::unordered_set regToClear; + std::unordered_set regToKeep; + std::vector> groupRegisters; + groupRegisters.emplace_back(std::make_pair(1, 0)); + + std::unordered_set readableInputRegisters; + readableInputRegisters.insert(0); + + std::unordered_set writeableOutputRegisters; + writeableOutputRegisters.insert(1); + + RegisterId nrOutputRegister = 3; + + std::vector> aggregateRegisters; + aggregateRegisters.emplace_back(std::make_pair(1, 0)); + + std::vector aggregateTypes; + aggregateTypes.emplace_back("LENGTH"); + + // if count = true, then we need to set a valid countRegister + bool count = true; + RegisterId collectRegister = 2; + writeableOutputRegisters.insert(2); + + HashedCollectExecutorInfos infos(1, nrOutputRegister, regToClear, regToKeep, + std::move(readableInputRegisters), + std::move(writeableOutputRegisters), + std::move(groupRegisters), collectRegister, + std::move(aggregateTypes), + std::move(aggregateRegisters), trx, count); + + SharedAqlItemBlockPtr block{new AqlItemBlock(itemBlockManager, 1000, nrOutputRegister)}; + + // This fetcher will not be called! + // After Execute is done this fetcher shall be removed, the Executor does not need it anymore! + auto fakeUnusedBlock = VPackParser::fromJson("[ ]"); + SingleRowFetcherHelper<::arangodb::aql::BlockPassthrough::Disable> fetcher( + itemBlockManager, fakeUnusedBlock->steal(), false); + + // This is the relevant part of the test + HashedCollectExecutor testee(fetcher, infos); + SharedAqlItemBlockPtr inBlock = + buildBlock<1>(itemBlockManager, {{R"("a")"}, {R"("aa")"}, {R"("aaa")"}}); + AqlItemBlockInputRange input{ExecutorState::DONE, inBlock, 0, inBlock->size()}; + + OutputAqlItemRow output(std::move(block), infos.getOutputRegisters(), + infos.registersToKeep(), infos.registersToClear()); + + EXPECT_EQ(output.numRowsWritten(), 0); + auto const [state, stats, call] = testee.produceRows(1000, input, output); + EXPECT_EQ(state, ExecutorState::DONE); + EXPECT_EQ(output.numRowsWritten(), 3); + + std::vector myStrings; + std::vector myCountNumbers; + auto newBlock = output.stealBlock(); + + // check for types + AqlValue x = newBlock->getValue(0, 1); + ASSERT_TRUE(x.isString()); + myStrings.emplace_back(x.slice().copyString()); + + // Check the count register + AqlValue xx = newBlock->getValue(0, 2); + ASSERT_TRUE(xx.isNumber()); + myCountNumbers.emplace_back(xx.slice().getInt()); + + AqlValue z = newBlock->getValue(1, 1); + ASSERT_TRUE(z.isString()); + myStrings.emplace_back(z.slice().copyString()); + + // Check the count register + AqlValue zz = newBlock->getValue(1, 2); + ASSERT_TRUE(zz.isNumber()); + myCountNumbers.emplace_back(zz.slice().getInt()); + + AqlValue y = newBlock->getValue(2, 1); + ASSERT_TRUE(y.isString()); + myStrings.emplace_back(y.slice().copyString()); + + // Check the count register + AqlValue yy = newBlock->getValue(2, 2); + ASSERT_TRUE(yy.isNumber()); + myCountNumbers.emplace_back(yy.slice().getInt()); + + // now sort vector and check for appearances + std::sort(myStrings.begin(), myStrings.end()); + + std::sort(myCountNumbers.begin(), myCountNumbers.end()); + ASSERT_EQ(myStrings.at(0), "a"); + ASSERT_EQ(myStrings.at(1), "aa"); + ASSERT_EQ(myStrings.at(2), "aaa"); + ASSERT_EQ(myCountNumbers.at(0), 1); + ASSERT_EQ(myCountNumbers.at(1), 1); + ASSERT_EQ(myCountNumbers.at(2), 1); +} + } // namespace aql } // namespace tests } // namespace arangodb