mirror of https://gitee.com/bigwinds/arangodb
Reimplement ReturnExecutor with a simple pass-through (#8876)
This commit is contained in:
parent
79cd45f89c
commit
6a56130ff8
|
@ -96,10 +96,10 @@ class ExecutionBlock {
|
||||||
/// DESTRUCTOR
|
/// DESTRUCTOR
|
||||||
|
|
||||||
/// @brief initializeCursor, could be called multiple times
|
/// @brief initializeCursor, could be called multiple times
|
||||||
virtual std::pair<ExecutionState, Result> initializeCursor(InputAqlItemRow const& input) = 0;
|
virtual std::pair<ExecutionState, Result> initializeCursor(InputAqlItemRow const& input);
|
||||||
|
|
||||||
/// @brief shutdown, will be called exactly once for the whole query
|
/// @brief shutdown, will be called exactly once for the whole query
|
||||||
virtual std::pair<ExecutionState, Result> shutdown(int errorCode) = 0;
|
virtual std::pair<ExecutionState, Result> shutdown(int errorCode);
|
||||||
|
|
||||||
/// @brief getSome, gets some more items, semantic is as follows: not
|
/// @brief getSome, gets some more items, semantic is as follows: not
|
||||||
/// more than atMost items may be delivered. The method tries to
|
/// more than atMost items may be delivered. The method tries to
|
||||||
|
|
|
@ -523,12 +523,10 @@ std::pair<ExecutionState, SharedAqlItemBlockPtr> ExecutionBlockImpl<Executor>::r
|
||||||
TRI_ASSERT(block->getNrRegs() == nrRegs);
|
TRI_ASSERT(block->getNrRegs() == nrRegs);
|
||||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||||
// Check that all output registers are empty.
|
// Check that all output registers are empty.
|
||||||
if (!std::is_same<Executor, ReturnExecutor<true>>::value) {
|
for (auto const& reg : *infos().getOutputRegisters()) {
|
||||||
for (auto const& reg : *infos().getOutputRegisters()) {
|
for (size_t row = 0; row < block->size(); row++) {
|
||||||
for (size_t row = 0; row < block->size(); row++) {
|
AqlValue const& val = block->getValueReference(row, reg);
|
||||||
AqlValue const& val = block->getValueReference(row, reg);
|
TRI_ASSERT(val.isEmpty());
|
||||||
TRI_ASSERT(val.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -599,8 +597,7 @@ template class ::arangodb::aql::ExecutionBlockImpl<SingleRemoteModificationExecu
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<SingleRemoteModificationExecutor<Replace>>;
|
template class ::arangodb::aql::ExecutionBlockImpl<SingleRemoteModificationExecutor<Replace>>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<SingleRemoteModificationExecutor<Upsert>>;
|
template class ::arangodb::aql::ExecutionBlockImpl<SingleRemoteModificationExecutor<Upsert>>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<NoResultsExecutor>;
|
template class ::arangodb::aql::ExecutionBlockImpl<NoResultsExecutor>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<ReturnExecutor<false>>;
|
template class ::arangodb::aql::ExecutionBlockImpl<ReturnExecutor>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<ReturnExecutor<true>>;
|
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<ShortestPathExecutor>;
|
template class ::arangodb::aql::ExecutionBlockImpl<ShortestPathExecutor>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<KShortestPathsExecutor>;
|
template class ::arangodb::aql::ExecutionBlockImpl<KShortestPathsExecutor>;
|
||||||
template class ::arangodb::aql::ExecutionBlockImpl<SortedCollectExecutor>;
|
template class ::arangodb::aql::ExecutionBlockImpl<SortedCollectExecutor>;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "Aql/ExecutionBlockImpl.h"
|
#include "Aql/ExecutionBlockImpl.h"
|
||||||
#include "Aql/ExecutionNode.h"
|
#include "Aql/ExecutionNode.h"
|
||||||
#include "Aql/GraphNode.h"
|
#include "Aql/GraphNode.h"
|
||||||
|
#include "Aql/IdExecutor.h"
|
||||||
#include "Aql/Query.h"
|
#include "Aql/Query.h"
|
||||||
#include "Aql/QueryRegistry.h"
|
#include "Aql/QueryRegistry.h"
|
||||||
#include "Aql/RemoteExecutor.h"
|
#include "Aql/RemoteExecutor.h"
|
||||||
|
@ -588,14 +589,12 @@ ExecutionEngine* ExecutionEngine::instantiateFromPlan(QueryRegistry* queryRegist
|
||||||
|
|
||||||
bool const returnInheritedResults = !isDBServer;
|
bool const returnInheritedResults = !isDBServer;
|
||||||
if (returnInheritedResults) {
|
if (returnInheritedResults) {
|
||||||
auto returnNode = dynamic_cast<ExecutionBlockImpl<ReturnExecutor<true>>*>(root);
|
auto returnNode = dynamic_cast<ExecutionBlockImpl<IdExecutor<void>>*>(root);
|
||||||
TRI_ASSERT(returnNode != nullptr);
|
TRI_ASSERT(returnNode != nullptr);
|
||||||
engine->resultRegister(returnNode->infos().getInputRegisterId());
|
engine->resultRegister(returnNode->getOutputRegisterId());
|
||||||
TRI_ASSERT(returnNode->infos().returnInheritedResults() == returnInheritedResults);
|
|
||||||
} else {
|
} else {
|
||||||
auto returnNode = dynamic_cast<ExecutionBlockImpl<ReturnExecutor<false>>*>(root);
|
auto returnNode = dynamic_cast<ExecutionBlockImpl<ReturnExecutor>*>(root);
|
||||||
TRI_ASSERT(returnNode != nullptr);
|
TRI_ASSERT(returnNode != nullptr);
|
||||||
TRI_ASSERT(returnNode->infos().returnInheritedResults() == returnInheritedResults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2046,18 +2046,21 @@ std::unique_ptr<ExecutionBlock> ReturnNode::createBlock(
|
||||||
// and do not modify it in any way.
|
// and do not modify it in any way.
|
||||||
// In the other case it is important to shrink the matrix to exactly
|
// In the other case it is important to shrink the matrix to exactly
|
||||||
// one register that is stored within the DOCVEC.
|
// one register that is stored within the DOCVEC.
|
||||||
|
RegisterId const numberInputRegisters =
|
||||||
|
getRegisterPlan()->nrRegs[previousNode->getDepth()];
|
||||||
RegisterId const numberOutputRegisters =
|
RegisterId const numberOutputRegisters =
|
||||||
returnInheritedResults ? getRegisterPlan()->nrRegs[getDepth()] : 1;
|
returnInheritedResults ? getRegisterPlan()->nrRegs[getDepth()] : 1;
|
||||||
|
|
||||||
ReturnExecutorInfos infos(inputRegister,
|
|
||||||
getRegisterPlan()->nrRegs[previousNode->getDepth()],
|
|
||||||
numberOutputRegisters, _count, returnInheritedResults);
|
|
||||||
if (returnInheritedResults) {
|
if (returnInheritedResults) {
|
||||||
return std::make_unique<ExecutionBlockImpl<ReturnExecutor<true>>>(&engine, this,
|
return std::make_unique<ExecutionBlockImpl<IdExecutor<void>>>(&engine, this,
|
||||||
std::move(infos));
|
inputRegister, _count);
|
||||||
} else {
|
} else {
|
||||||
return std::make_unique<ExecutionBlockImpl<ReturnExecutor<false>>>(&engine, this,
|
TRI_ASSERT(!returnInheritedResults);
|
||||||
std::move(infos));
|
ReturnExecutorInfos infos(inputRegister, numberInputRegisters,
|
||||||
|
numberOutputRegisters, _count);
|
||||||
|
|
||||||
|
return std::make_unique<ExecutionBlockImpl<ReturnExecutor>>(&engine, this,
|
||||||
|
std::move(infos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,12 +34,12 @@ using namespace arangodb::aql;
|
||||||
|
|
||||||
IdExecutorInfos::IdExecutorInfos(RegisterId nrInOutRegisters,
|
IdExecutorInfos::IdExecutorInfos(RegisterId nrInOutRegisters,
|
||||||
// cppcheck-suppress passedByValue
|
// cppcheck-suppress passedByValue
|
||||||
std::unordered_set<RegisterId> toKeep,
|
std::unordered_set<RegisterId> registersToKeep,
|
||||||
// cppcheck-suppress passedByValue
|
// cppcheck-suppress passedByValue
|
||||||
std::unordered_set<RegisterId> registersToClear)
|
std::unordered_set<RegisterId> registersToClear)
|
||||||
: ExecutorInfos(make_shared_unordered_set(), make_shared_unordered_set(),
|
: ExecutorInfos(make_shared_unordered_set(), make_shared_unordered_set(),
|
||||||
nrInOutRegisters, nrInOutRegisters,
|
nrInOutRegisters, nrInOutRegisters,
|
||||||
std::move(registersToClear), std::move(toKeep)) {}
|
std::move(registersToClear), std::move(registersToKeep)) {}
|
||||||
|
|
||||||
template <class UsedFetcher>
|
template <class UsedFetcher>
|
||||||
IdExecutor<UsedFetcher>::IdExecutor(Fetcher& fetcher, IdExecutorInfos& infos)
|
IdExecutor<UsedFetcher>::IdExecutor(Fetcher& fetcher, IdExecutorInfos& infos)
|
||||||
|
|
|
@ -46,9 +46,7 @@ struct SortRegister;
|
||||||
|
|
||||||
class IdExecutorInfos : public ExecutorInfos {
|
class IdExecutorInfos : public ExecutorInfos {
|
||||||
public:
|
public:
|
||||||
// whiteList will be used for slicing in the ExecutionBlockImpl
|
IdExecutorInfos(RegisterId nrInOutRegisters, std::unordered_set<RegisterId> registersToKeep,
|
||||||
// whiteListClean is the same as our registersToKeep
|
|
||||||
IdExecutorInfos(RegisterId nrInOutRegisters, std::unordered_set<RegisterId> toKeep,
|
|
||||||
std::unordered_set<RegisterId> registersToClear);
|
std::unordered_set<RegisterId> registersToClear);
|
||||||
|
|
||||||
IdExecutorInfos() = delete;
|
IdExecutorInfos() = delete;
|
||||||
|
@ -57,6 +55,103 @@ class IdExecutorInfos : public ExecutorInfos {
|
||||||
~IdExecutorInfos() = default;
|
~IdExecutorInfos() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// forward declaration
|
||||||
|
template <class T>
|
||||||
|
class IdExecutor;
|
||||||
|
|
||||||
|
// (empty) implementation of IdExecutor<void>
|
||||||
|
template <>
|
||||||
|
class IdExecutor<void> {};
|
||||||
|
|
||||||
|
// implementation of ExecutionBlockImpl<IdExecutor<void>>
|
||||||
|
template <>
|
||||||
|
class ExecutionBlockImpl<IdExecutor<void>> : public ExecutionBlock {
|
||||||
|
public:
|
||||||
|
ExecutionBlockImpl(ExecutionEngine* engine, ExecutionNode const* node,
|
||||||
|
RegisterId outputRegister, bool doCount)
|
||||||
|
: ExecutionBlock(engine, node),
|
||||||
|
_currentDependency(0),
|
||||||
|
_outputRegister(outputRegister),
|
||||||
|
_doCount(doCount) {
|
||||||
|
|
||||||
|
// already insert ourselves into the statistics results
|
||||||
|
if (_profile >= PROFILE_LEVEL_BLOCKS) {
|
||||||
|
_engine->_stats.nodes.emplace(node->id(), ExecutionStats::Node());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ExecutionBlockImpl() override = default;
|
||||||
|
|
||||||
|
std::pair<ExecutionState, SharedAqlItemBlockPtr> getSome(size_t atMost) override {
|
||||||
|
traceGetSomeBegin(atMost);
|
||||||
|
if (isDone()) {
|
||||||
|
return traceGetSomeEnd(ExecutionState::DONE, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionState state;
|
||||||
|
SharedAqlItemBlockPtr block;
|
||||||
|
std::tie(state, block) = currentDependency().getSome(atMost);
|
||||||
|
|
||||||
|
countStats(block);
|
||||||
|
|
||||||
|
if (state == ExecutionState::DONE) {
|
||||||
|
nextDependency();
|
||||||
|
}
|
||||||
|
|
||||||
|
return traceGetSomeEnd(state, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<ExecutionState, size_t> skipSome(size_t atMost) override {
|
||||||
|
traceSkipSomeBegin(atMost);
|
||||||
|
if (isDone()) {
|
||||||
|
return traceSkipSomeEnd(ExecutionState::DONE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionState state;
|
||||||
|
size_t skipped;
|
||||||
|
std::tie(state, skipped) = currentDependency().skipSome(atMost);
|
||||||
|
|
||||||
|
if (state == ExecutionState::DONE) {
|
||||||
|
nextDependency();
|
||||||
|
}
|
||||||
|
|
||||||
|
return traceSkipSomeEnd(state, skipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterId getOutputRegisterId() const noexcept { return _outputRegister; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isDone() const noexcept {
|
||||||
|
// I'd like to assert this in the constructor, but the dependencies are
|
||||||
|
// added after construction.
|
||||||
|
TRI_ASSERT(!_dependencies.empty());
|
||||||
|
return _currentDependency >= _dependencies.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutionBlock& currentDependency() const {
|
||||||
|
TRI_ASSERT(_currentDependency < _dependencies.size());
|
||||||
|
TRI_ASSERT(_dependencies[_currentDependency] != nullptr);
|
||||||
|
return *_dependencies[_currentDependency];
|
||||||
|
}
|
||||||
|
|
||||||
|
void nextDependency() noexcept { ++_currentDependency; }
|
||||||
|
|
||||||
|
bool doCount() const noexcept { return _doCount; }
|
||||||
|
|
||||||
|
void countStats(SharedAqlItemBlockPtr& block) {
|
||||||
|
if (doCount() && block != nullptr) {
|
||||||
|
CountStats stats;
|
||||||
|
stats.setCounted(block->size());
|
||||||
|
_engine->_stats += stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t _currentDependency;
|
||||||
|
RegisterId const _outputRegister;
|
||||||
|
bool const _doCount;
|
||||||
|
};
|
||||||
|
|
||||||
template <class UsedFetcher>
|
template <class UsedFetcher>
|
||||||
class IdExecutor {
|
class IdExecutor {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -32,25 +32,16 @@ using namespace arangodb;
|
||||||
using namespace arangodb::aql;
|
using namespace arangodb::aql;
|
||||||
|
|
||||||
ReturnExecutorInfos::ReturnExecutorInfos(RegisterId inputRegister, RegisterId nrInputRegisters,
|
ReturnExecutorInfos::ReturnExecutorInfos(RegisterId inputRegister, RegisterId nrInputRegisters,
|
||||||
RegisterId nrOutputRegisters,
|
RegisterId nrOutputRegisters, bool doCount)
|
||||||
bool doCount, bool returnInheritedResults)
|
|
||||||
: ExecutorInfos(make_shared_unordered_set({inputRegister}),
|
: ExecutorInfos(make_shared_unordered_set({inputRegister}),
|
||||||
returnInheritedResults ? make_shared_unordered_set({})
|
make_shared_unordered_set({0}), nrInputRegisters, nrOutputRegisters,
|
||||||
: make_shared_unordered_set({0}),
|
std::unordered_set<RegisterId>{} /*to clear*/,
|
||||||
nrInputRegisters,
|
|
||||||
nrOutputRegisters, std::unordered_set<RegisterId>{} /*to clear*/, // std::move(registersToClear) // use this once register planning is fixed
|
|
||||||
std::unordered_set<RegisterId>{} /*to keep*/
|
std::unordered_set<RegisterId>{} /*to keep*/
|
||||||
),
|
),
|
||||||
_inputRegisterId(inputRegister),
|
_inputRegisterId(inputRegister),
|
||||||
_doCount(doCount),
|
_doCount(doCount) {}
|
||||||
_returnInheritedResults(returnInheritedResults) {}
|
|
||||||
|
|
||||||
template <bool passBlocksThrough>
|
ReturnExecutor::ReturnExecutor(Fetcher& fetcher, ReturnExecutorInfos& infos)
|
||||||
ReturnExecutor<passBlocksThrough>::ReturnExecutor(Fetcher& fetcher, ReturnExecutorInfos& infos)
|
|
||||||
: _infos(infos), _fetcher(fetcher){};
|
: _infos(infos), _fetcher(fetcher){};
|
||||||
|
|
||||||
template <bool passBlocksThrough>
|
ReturnExecutor::~ReturnExecutor() = default;
|
||||||
ReturnExecutor<passBlocksThrough>::~ReturnExecutor() = default;
|
|
||||||
|
|
||||||
template class ::arangodb::aql::ReturnExecutor<true>;
|
|
||||||
template class ::arangodb::aql::ReturnExecutor<false>;
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "Aql/ExecutorInfos.h"
|
#include "Aql/ExecutorInfos.h"
|
||||||
#include "Aql/InputAqlItemRow.h"
|
#include "Aql/InputAqlItemRow.h"
|
||||||
#include "Aql/OutputAqlItemRow.h"
|
#include "Aql/OutputAqlItemRow.h"
|
||||||
|
#include "Aql/SingleRowFetcher.h"
|
||||||
#include "Aql/Stats.h"
|
#include "Aql/Stats.h"
|
||||||
|
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
|
@ -36,16 +37,13 @@ class Methods;
|
||||||
|
|
||||||
namespace aql {
|
namespace aql {
|
||||||
|
|
||||||
template <bool>
|
|
||||||
class SingleRowFetcher;
|
|
||||||
class ExecutorInfos;
|
class ExecutorInfos;
|
||||||
class NoStats;
|
class NoStats;
|
||||||
|
|
||||||
class ReturnExecutorInfos : public ExecutorInfos {
|
class ReturnExecutorInfos : public ExecutorInfos {
|
||||||
public:
|
public:
|
||||||
ReturnExecutorInfos(RegisterId inputRegister, RegisterId nrInputRegisters,
|
ReturnExecutorInfos(RegisterId inputRegister, RegisterId nrInputRegisters,
|
||||||
RegisterId nrOutputRegisters, bool doCount,
|
RegisterId nrOutputRegisters, bool doCount);
|
||||||
bool returnInheritedResults);
|
|
||||||
|
|
||||||
ReturnExecutorInfos() = delete;
|
ReturnExecutorInfos() = delete;
|
||||||
ReturnExecutorInfos(ReturnExecutorInfos&&) = default;
|
ReturnExecutorInfos(ReturnExecutorInfos&&) = default;
|
||||||
|
@ -54,34 +52,34 @@ class ReturnExecutorInfos : public ExecutorInfos {
|
||||||
|
|
||||||
RegisterId getInputRegisterId() const { return _inputRegisterId; }
|
RegisterId getInputRegisterId() const { return _inputRegisterId; }
|
||||||
|
|
||||||
RegisterId getOutputRegisterId() const {
|
RegisterId getOutputRegisterId() const { return 0; }
|
||||||
// Should not be used with returnInheritedResults.
|
|
||||||
TRI_ASSERT(!returnInheritedResults());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool doCount() const { return _doCount; }
|
bool doCount() const { return _doCount; }
|
||||||
|
|
||||||
bool returnInheritedResults() const { return _returnInheritedResults; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @brief the variable produced by Return
|
/// @brief the variable produced by Return
|
||||||
RegisterId _inputRegisterId;
|
RegisterId _inputRegisterId;
|
||||||
bool _doCount;
|
bool _doCount;
|
||||||
bool _returnInheritedResults;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Implementation of Return Node
|
* @brief Implementation of Return Node
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The return executor projects some column, given by _infos.getInputRegisterId(),
|
||||||
|
* to the first and only column in the output. This is used for return nodes
|
||||||
|
* in subqueries.
|
||||||
|
* Return nodes on the top level use the IdExecutor instead.
|
||||||
*/
|
*/
|
||||||
template <bool passBlocksThrough>
|
|
||||||
class ReturnExecutor {
|
class ReturnExecutor {
|
||||||
public:
|
public:
|
||||||
struct Properties {
|
struct Properties {
|
||||||
static const bool preservesOrder = true;
|
static const bool preservesOrder = true;
|
||||||
static const bool allowsBlockPassthrough = passBlocksThrough;
|
// The return executor is now only used for projecting some register to
|
||||||
/* This could be set to true after some investigation/fixes */
|
// register 0. So it does not pass through, but copy one column into a new
|
||||||
static const bool inputSizeRestrictsOutputSize = false;
|
// block with only this column.
|
||||||
|
static const bool allowsBlockPassthrough = false;
|
||||||
|
static const bool inputSizeRestrictsOutputSize = true;
|
||||||
};
|
};
|
||||||
using Fetcher = SingleRowFetcher<Properties::allowsBlockPassthrough>;
|
using Fetcher = SingleRowFetcher<Properties::allowsBlockPassthrough>;
|
||||||
using Infos = ReturnExecutorInfos;
|
using Infos = ReturnExecutorInfos;
|
||||||
|
@ -112,29 +110,21 @@ class ReturnExecutor {
|
||||||
return {state, stats};
|
return {state, stats};
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_ASSERT(passBlocksThrough == _infos.returnInheritedResults());
|
AqlValue val = inputRow.stealValue(_infos.getInputRegisterId());
|
||||||
if (_infos.returnInheritedResults()) {
|
AqlValueGuard guard(val, true);
|
||||||
output.copyRow(inputRow);
|
TRI_IF_FAILURE("ReturnBlock::getSome") {
|
||||||
} else {
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
AqlValue val = inputRow.stealValue(_infos.getInputRegisterId());
|
|
||||||
AqlValueGuard guard(val, true);
|
|
||||||
TRI_IF_FAILURE("ReturnBlock::getSome") {
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
|
||||||
}
|
|
||||||
output.moveValueInto(_infos.getOutputRegisterId(), inputRow, guard);
|
|
||||||
}
|
}
|
||||||
|
output.moveValueInto(_infos.getOutputRegisterId(), inputRow, guard);
|
||||||
|
|
||||||
if (_infos.doCount()) {
|
if (_infos.doCount()) {
|
||||||
stats.incrCounted();
|
stats.incrCounted();
|
||||||
}
|
}
|
||||||
return {state, stats};
|
return {state, stats};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::pair<ExecutionState, size_t> expectedNumberOfRows(size_t) const {
|
inline std::pair<ExecutionState, size_t> expectedNumberOfRows(size_t atMost) const {
|
||||||
TRI_ASSERT(false);
|
return _fetcher.preFetchNumberOfRows(atMost);
|
||||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
|
||||||
TRI_ERROR_INTERNAL,
|
|
||||||
"Logic_error, prefetching number fo rows not supported");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -57,14 +57,12 @@ class ReturnExecutorTest : public ::testing::Test {
|
||||||
inputRegister(0) {}
|
inputRegister(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, PassThroughNoRowsUpstreamProducerDoesNotWait) {
|
TEST_F(ReturnExecutorTest, NoRowsUpstreamProducerDoesNotWait) {
|
||||||
constexpr bool passBlocksThrough = true;
|
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/, true /*do count*/);
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
auto& outputRegisters = infos.getOutputRegisters();
|
||||||
VPackBuilder input;
|
VPackBuilder input;
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input.steal(), false);
|
SingleRowFetcherHelper<false> fetcher(input.steal(), false);
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
ReturnExecutor testee(fetcher, infos);
|
||||||
CountStats stats{};
|
CountStats stats{};
|
||||||
|
|
||||||
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
||||||
|
@ -74,31 +72,12 @@ TEST_F(ReturnExecutorTest, PassThroughNoRowsUpstreamProducerDoesNotWait) {
|
||||||
ASSERT_TRUE(!result.produced());
|
ASSERT_TRUE(!result.produced());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, NoPassThroughNoRowsUpstreamProducerDoesNotWait) {
|
TEST_F(ReturnExecutorTest, NoRowsUpstreamProducerWaits) {
|
||||||
constexpr bool passBlocksThrough = false;
|
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/, true /*do count*/);
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
auto& outputRegisters = infos.getOutputRegisters();
|
||||||
VPackBuilder input;
|
VPackBuilder input;
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input.steal(), false);
|
SingleRowFetcherHelper<false> fetcher(input.steal(), true);
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
ReturnExecutor testee(fetcher, infos);
|
||||||
CountStats stats{};
|
|
||||||
|
|
||||||
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
|
||||||
infos.registersToClear());
|
|
||||||
std::tie(state, stats) = testee.produceRows(result);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(!result.produced());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, PassThroughNoRowsUpstreamProducerWaits) {
|
|
||||||
constexpr bool passBlocksThrough = true;
|
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
|
||||||
VPackBuilder input;
|
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input.steal(), true);
|
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
|
||||||
CountStats stats{};
|
CountStats stats{};
|
||||||
|
|
||||||
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
||||||
|
@ -112,40 +91,14 @@ TEST_F(ReturnExecutorTest, PassThroughNoRowsUpstreamProducerWaits) {
|
||||||
ASSERT_TRUE(!result.produced());
|
ASSERT_TRUE(!result.produced());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, NoPassThroughNoRowsUpstreamProducerWaits) {
|
TEST_F(ReturnExecutorTest, RowsUpstreamProducerDoesNotWait) {
|
||||||
constexpr bool passBlocksThrough = false;
|
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/, true /*do count*/);
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
|
||||||
VPackBuilder input;
|
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input.steal(), true);
|
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
|
||||||
CountStats stats{};
|
|
||||||
|
|
||||||
OutputAqlItemRow result(std::move(block), outputRegisters, registersToKeep,
|
|
||||||
infos.registersToClear());
|
|
||||||
std::tie(state, stats) = testee.produceRows(result);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::WAITING);
|
|
||||||
ASSERT_TRUE(!result.produced());
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(result);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(!result.produced());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, PassThroughRowsUpstreamProducerDoesNotWait) {
|
|
||||||
constexpr bool passBlocksThrough = true;
|
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
auto& outputRegisters = infos.getOutputRegisters();
|
||||||
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input->buffer(), false);
|
SingleRowFetcherHelper<false> fetcher(input->buffer(), false);
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
ReturnExecutor testee(fetcher, infos);
|
||||||
CountStats stats{};
|
CountStats stats{};
|
||||||
|
|
||||||
if (passBlocksThrough) {
|
|
||||||
block = fetcher.getItemBlock();
|
|
||||||
}
|
|
||||||
OutputAqlItemRow row(std::move(block), outputRegisters, registersToKeep,
|
OutputAqlItemRow row(std::move(block), outputRegisters, registersToKeep,
|
||||||
infos.registersToClear());
|
infos.registersToClear());
|
||||||
|
|
||||||
|
@ -178,112 +131,14 @@ TEST_F(ReturnExecutorTest, PassThroughRowsUpstreamProducerDoesNotWait) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, NoPassThroughRowsUpstreamProducerDoesNotWait) {
|
TEST_F(ReturnExecutorTest, RowsUpstreamProducerWaits) {
|
||||||
constexpr bool passBlocksThrough = false;
|
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/, true /*do count*/);
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
auto& outputRegisters = infos.getOutputRegisters();
|
||||||
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input->buffer(), false);
|
SingleRowFetcherHelper<false> fetcher(input->steal(), true);
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
ReturnExecutor testee(fetcher, infos);
|
||||||
CountStats stats{};
|
CountStats stats{};
|
||||||
|
|
||||||
if (passBlocksThrough) {
|
|
||||||
block = fetcher.getItemBlock();
|
|
||||||
}
|
|
||||||
OutputAqlItemRow row(std::move(block), outputRegisters, registersToKeep,
|
|
||||||
infos.registersToClear());
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::HASMORE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::HASMORE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(!row.produced());
|
|
||||||
|
|
||||||
// verify result
|
|
||||||
AqlValue value;
|
|
||||||
auto block = row.stealBlock();
|
|
||||||
for (std::size_t index = 0; index < 3; index++) {
|
|
||||||
value = block->getValue(index, 0);
|
|
||||||
ASSERT_TRUE(value.isBoolean());
|
|
||||||
ASSERT_TRUE(value.toBoolean() == input->slice().at(index).at(0).getBool());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, PassThroughRowsUpstreamProducerWaits) {
|
|
||||||
constexpr bool passBlocksThrough = true;
|
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
|
||||||
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input->steal(), true);
|
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
|
||||||
CountStats stats{};
|
|
||||||
|
|
||||||
if (passBlocksThrough) {
|
|
||||||
block = fetcher.getItemBlock();
|
|
||||||
}
|
|
||||||
OutputAqlItemRow row{std::move(block), outputRegisters, registersToKeep,
|
|
||||||
infos.registersToClear()};
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::WAITING);
|
|
||||||
ASSERT_TRUE(!row.produced());
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::HASMORE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::WAITING);
|
|
||||||
ASSERT_TRUE(!row.produced());
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::HASMORE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::WAITING);
|
|
||||||
ASSERT_TRUE(!row.produced());
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(row.produced());
|
|
||||||
row.advanceRow();
|
|
||||||
|
|
||||||
std::tie(state, stats) = testee.produceRows(row);
|
|
||||||
ASSERT_TRUE(state == ExecutionState::DONE);
|
|
||||||
ASSERT_TRUE(!row.produced());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ReturnExecutorTest, NoPassThroughRowsUpstreamProducerWaits) {
|
|
||||||
constexpr bool passBlocksThrough = false;
|
|
||||||
ReturnExecutorInfos infos(inputRegister, 1 /*nr in*/, 1 /*nr out*/,
|
|
||||||
true /*do count*/, passBlocksThrough /*return inherit*/);
|
|
||||||
auto& outputRegisters = infos.getOutputRegisters();
|
|
||||||
auto input = VPackParser::fromJson("[ [true], [false], [true] ]");
|
|
||||||
SingleRowFetcherHelper<passBlocksThrough> fetcher(input->steal(), true);
|
|
||||||
ReturnExecutor<passBlocksThrough> testee(fetcher, infos);
|
|
||||||
CountStats stats{};
|
|
||||||
|
|
||||||
if (passBlocksThrough) {
|
|
||||||
block = fetcher.getItemBlock();
|
|
||||||
}
|
|
||||||
OutputAqlItemRow row{std::move(block), outputRegisters, registersToKeep,
|
OutputAqlItemRow row{std::move(block), outputRegisters, registersToKeep,
|
||||||
infos.registersToClear()};
|
infos.registersToClear()};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue