1
0
Fork 0

Refactor doOutput

This commit is contained in:
Markus Pfeiffer 2019-10-24 17:04:00 +01:00
parent 6b339a0a09
commit f80cf8d1b0
7 changed files with 195 additions and 186 deletions

View File

@ -50,17 +50,23 @@ using namespace arangodb::basics;
namespace arangodb {
namespace aql {
std::ostream& operator<<(std::ostream& ostream, ModifierIteratorMode mode) {
switch (mode) {
case ModifierIteratorMode::Full:
ostream << "Full";
break;
case ModifierIteratorMode::OperationsOnly:
ostream << "OperationsOnly";
break;
}
return ostream;
}
ModifierOutput::ModifierOutput(InputAqlItemRow const inputRow, bool const error)
: _inputRow(inputRow), _error(error), _oldValue(nullptr), _newValue(nullptr) {}
ModifierOutput::ModifierOutput(InputAqlItemRow const inputRow, bool const error,
std::unique_ptr<AqlValue>&& oldValue,
std::unique_ptr<AqlValue>&& newValue)
: _inputRow(inputRow),
_error(error),
_oldValue(std::move(oldValue)),
_newValue(std::move(newValue)) {}
InputAqlItemRow ModifierOutput::getInputRow() const { return _inputRow; }
bool ModifierOutput::isError() const { return _error; }
bool ModifierOutput::hasOldValue() const { return _oldValue != nullptr; }
AqlValue&& ModifierOutput::getOldValue() const { return std::move(*_oldValue); }
bool ModifierOutput::hasNewValue() const { return _newValue != nullptr; }
AqlValue&& ModifierOutput::getNewValue() const { return std::move(*_newValue); }
template <typename FetcherType, typename ModifierType>
ModificationExecutor<FetcherType, ModifierType>::ModificationExecutor(Fetcher& fetcher,
@ -115,105 +121,32 @@ ModificationExecutor<FetcherType, ModifierType>::doCollect(size_t const maxOutpu
template <typename FetcherType, typename ModifierType>
void ModificationExecutor<FetcherType, ModifierType>::doOutput(OutputAqlItemRow& output,
Stats& stats) {
// If we have made no modifications or are silent,
// we can just copy rows; this is an optimisation for silent
// queries
// if (_modifier.nrOfDocuments() == 0 || _infos._options.silent) {
// ModOperationType modOp;
// InputAqlItemRow row{CreateInvalidInputRowHint{}};
// _modifier.setupIterator(ModifierIteratorMode::OperationsOnly);
// while (!_modifier.isFinishedIterator()) {
// std::tie(modOp, row, std::ignore) = _modifier.getOutput();
// output.copyRow(row);
// if (_infos._doCount) {
// switch (modOp) {
// case ModOperationType::APPLY_RETURN: {
// stats.incrWritesExecuted();
// break;
// }
// case ModOperationType::IGNORE_RETURN: {
// stats.incrWritesIgnored();
// break;
// }
// case ModOperationType::IGNORE_SKIP: {
// stats.incrWritesIgnored();
// break;
// }
// default: {
// TRI_ASSERT(false);
// }
// }
// }
// _modifier.advanceIterator();
// output.advanceRow();
// }
// } else {
ModOperationType modOp;
InputAqlItemRow row{CreateInvalidInputRowHint{}};
VPackSlice elm;
_modifier.setupIterator(ModifierIteratorMode::Full);
_modifier.setupIterator();
while (!_modifier.isFinishedIterator()) {
std::tie(modOp, row, elm) = _modifier.getOutput();
ModifierOutput modifierOutput{_modifier.getOutput()};
bool error = VelocyPackHelper::getBooleanValue(elm, StaticStrings::Error, false);
if (!error) {
switch (modOp) {
case ModOperationType::APPLY_RETURN: {
if (_infos._options.returnNew) {
AqlValue value(elm.get(StaticStrings::New));
AqlValueGuard guard(value, true);
output.moveValueInto(_infos._outputNewRegisterId, row, guard);
}
if (_infos._options.returnOld) {
AqlValue value(elm.get(StaticStrings::Old));
AqlValueGuard guard(value, true);
output.moveValueInto(_infos._outputOldRegisterId, row, guard);
}
if (_infos._doCount) {
stats.incrWritesExecuted();
}
break;
}
case ModOperationType::IGNORE_RETURN: {
output.copyRow(row);
if (_infos._doCount) {
stats.incrWritesIgnored();
}
break;
}
case ModOperationType::IGNORE_SKIP: {
output.copyRow(row);
if (_infos._doCount) {
stats.incrWritesIgnored();
}
break;
}
case ModOperationType::APPLY_UPDATE:
case ModOperationType::APPLY_INSERT: {
// These values should not appear here anymore
// As we handle them in the UPSERT modifier and translate them
// into APPLY_RETURN
TRI_ASSERT(false);
}
default: {
TRI_ASSERT(false);
break;
}
if (!modifierOutput.isError()) {
if (_infos._options.returnOld) {
output.cloneValueInto(_infos._outputOldRegisterId, modifierOutput.getInputRow(),
modifierOutput.getOldValue());
}
if (_infos._options.returnNew) {
output.cloneValueInto(_infos._outputNewRegisterId, modifierOutput.getInputRow(),
modifierOutput.getNewValue());
}
if (!_infos._options.returnOld && !_infos._options.returnNew) {
output.copyRow(modifierOutput.getInputRow());
}
// only advance row if we produced something
output.advanceRow();
} else {
if (_infos._doCount) {
stats.incrWritesIgnored();
}
}
_modifier.advanceIterator();
}
// }
if (_infos._doCount) {
stats.addWritesExecuted(_modifier.nrOfWritesExecuted());
stats.addWritesIgnored(_modifier.nrOfWritesIgnored());
}
}
template <typename FetcherType, typename ModifierType>
@ -242,6 +175,13 @@ ModificationExecutor<FetcherType, ModifierType>::produceRows(OutputAqlItemRow& o
_modifier.transact();
// If the query is silent, there is no way to relate
// the results slice contents and the submitted documents
// If the query is *not* silent, we should get one result
// for every document.
// Yes. Really.
TRI_ASSERT(_infos._options.silent || _modifier.nrOfDocuments() == _modifier.nrOfResults());
doOutput(output, stats);
return {_lastState, std::move(stats)};

View File

@ -99,14 +99,35 @@ enum class ModOperationType : uint8_t {
// The first component has to be the operation type, the second an
// InputAqlItemRow, and the third is a VPackSlice containing the result of the
// transaction for this row.
using ModifierOutput = std::tuple<ModOperationType, InputAqlItemRow, VPackSlice>;
// using ModifierOutput = std::tuple<ModOperationType, InputAqlItemRow, VPackSlice>;
// We have to switch between "Full" and "OperationsOnly" iterator mode because
// if the transaction is silent we do not actually have a Velocypack to iterate
// over This can go away once iterating over results of the operation is done
// with a bespoke iterator class
enum class ModifierIteratorMode { OperationsOnly, Full };
std::ostream& operator<<(std::ostream& ostream, ModifierIteratorMode mode);
class ModifierOutput {
public:
ModifierOutput() = delete;
ModifierOutput(InputAqlItemRow const inputRow, bool const error);
ModifierOutput(InputAqlItemRow const inputRow, bool const error,
std::unique_ptr<AqlValue>&& oldValue, std::unique_ptr<AqlValue>&& newValue);
ModifierOutput(ModifierOutput&& o);
ModifierOutput& operator=(ModifierOutput&& o);
InputAqlItemRow getInputRow() const;
bool isError() const;
bool hasOldValue() const;
AqlValue&& getOldValue() const;
bool hasNewValue() const;
AqlValue&& getNewValue() const;
private:
// No copying or copy assignment allowed of this class or any derived class
ModifierOutput(ModifierOutput const&);
ModifierOutput& operator=(ModifierOutput const&);
InputAqlItemRow const _inputRow;
bool const _error;
std::unique_ptr<AqlValue> _oldValue;
std::unique_ptr<AqlValue> _newValue;
};
template <typename FetcherType, typename ModifierType>
class ModificationExecutor {

View File

@ -35,6 +35,8 @@
#include <string>
#include "Logger/LogMacros.h"
using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::basics;
@ -107,6 +109,11 @@ void ModificationExecutorHelpers::throwOperationResultException(
ModificationExecutorInfos& infos, OperationResult const& result) {
auto const& errorCounter = result.countErrorCodes;
// LOG_DEVEL << "ok, here's the shit";
// if (result.hasSlice()) {
// LOG_DEVEL << result.slice().toJson();
// }
// Early escape if we are ignoring errors.
if (infos._ignoreErrors == true || errorCounter.empty()) {
return;

View File

@ -29,6 +29,7 @@
#include "Aql/ModificationExecutorHelpers.h"
#include "Aql/OutputAqlItemRow.h"
#include "Basics/Common.h"
#include "Basics/VelocyPackHelper.h"
#include "VocBase/LogicalCollection.h"
#include <velocypack/Collection.h>
@ -41,6 +42,7 @@ class CollectionNameResolver;
using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::aql::ModificationExecutorHelpers;
using namespace arangodb::basics;
template <class ModifierCompletion, typename Enable>
SimpleModifier<ModifierCompletion, Enable>::SimpleModifier(ModificationExecutorInfos& infos)
@ -86,14 +88,32 @@ size_t SimpleModifier<ModifierCompletion, Enable>::nrOfDocuments() const {
}
template <typename ModifierCompletion, typename Enable>
Result SimpleModifier<ModifierCompletion, Enable>::setupIterator(ModifierIteratorMode const mode) {
_iteratorMode = mode;
_operationsIterator = _operations.begin();
if (mode == ModifierIteratorMode::Full) {
TRI_ASSERT(_results.slice().isArray());
_resultsIterator = VPackArrayIterator{_results.slice()};
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfResults() const {
if (_results.hasSlice() && _results.slice().isArray()) {
return _results.slice().length();
}
return 0;
}
template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesExecuted() const {
return 0;
}
template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesIgnored() const {
return 0;
}
template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::setupIterator() {
_operationsIterator = _operations.begin();
if (resultAvailable()) {
TRI_ASSERT(_results.hasSlice() && _results.slice().isArray());
_resultsIterator = VPackArrayIterator{_results.slice()};
} else {
_resultsIterator = VPackArrayIterator{VPackSlice::emptyArraySlice()};
}
return Result{};
}
template <typename ModifierCompletion, typename Enable>
@ -121,22 +141,26 @@ size_t SimpleModifier<ModifierCompletion, Enable>::getBatchSize() const {
return _batchSize;
}
template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::resultAvailable() const {
return (nrOfDocuments() > 0 && !_infos._options.silent);
}
template <typename ModifierCompletion, typename Enable>
ModifierOutput SimpleModifier<ModifierCompletion, Enable>::getOutput() {
switch (_iteratorMode) {
case ModifierIteratorMode::Full: {
return ModifierOutput{_operationsIterator->first,
_operationsIterator->second, *_resultsIterator};
}
case ModifierIteratorMode::OperationsOnly: {
return ModifierOutput{_operationsIterator->first,
_operationsIterator->second, VPackSlice::noneSlice()};
}
if (_operationsIterator->first == ModOperationType::APPLY_RETURN && resultAvailable()) {
VPackSlice elm = *_resultsIterator;
bool error = VelocyPackHelper::getBooleanValue(elm, StaticStrings::Error, false);
return ModifierOutput{_operationsIterator->second, error,
std::make_unique<AqlValue>(elm.get(StaticStrings::Old)),
std::make_unique<AqlValue>(elm.get(StaticStrings::New))};
} else {
return ModifierOutput{_operationsIterator->second, false};
}
TRI_ASSERT(false);
return ModifierOutput{ModOperationType::IGNORE_SKIP,
InputAqlItemRow{CreateInvalidInputRowHint()},
VPackSlice::noneSlice()};
return ModifierOutput{InputAqlItemRow{CreateInvalidInputRowHint{}}, true};
}
template class ::arangodb::aql::SimpleModifier<InsertModifierCompletion>;

View File

@ -94,9 +94,14 @@ class SimpleModifier {
size_t nrOfOperations() const;
// The number of documents in the accumulator
size_t nrOfDocuments() const;
// The number of entries in the results slice
size_t nrOfResults() const;
size_t nrOfWritesExecuted() const;
size_t nrOfWritesIgnored() const;
// TODO: Make this a real iterator
Result setupIterator(ModifierIteratorMode const mode);
void setupIterator();
bool isFinishedIterator();
ModifierOutput getOutput();
void advanceIterator();
@ -105,6 +110,8 @@ class SimpleModifier {
size_t getBatchSize() const;
private:
bool resultAvailable() const;
ModificationExecutorInfos& _infos;
ModifierCompletion _completion;
@ -115,7 +122,6 @@ class SimpleModifier {
std::vector<ModOp>::const_iterator _operationsIterator;
VPackArrayIterator _resultsIterator;
ModifierIteratorMode _iteratorMode;
size_t const _batchSize;
};

View File

@ -29,6 +29,7 @@
#include "Aql/ModificationExecutorHelpers.h"
#include "Aql/OutputAqlItemRow.h"
#include "Basics/Common.h"
#include "Basics/VelocyPackHelper.h"
#include "Transaction/Methods.h"
#include "VocBase/LogicalCollection.h"
@ -42,6 +43,7 @@ class CollectionNameResolver;
using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::aql::ModificationExecutorHelpers;
using namespace arangodb::basics;
UpsertModifier::UpsertModifier(ModificationExecutorInfos& infos)
: _infos(infos),
@ -128,6 +130,10 @@ ModOperationType UpsertModifier::insertCase(ModificationExecutorAccumulator& acc
}
}
bool UpsertModifier::resultAvailable() const {
return (nrOfDocuments() > 0 && !_infos._options.silent);
}
Result UpsertModifier::accumulate(InputAqlItemRow& row) {
RegisterId const inDocReg = _infos._input1RegisterId;
RegisterId const insertReg = _infos._input2RegisterId;
@ -180,28 +186,39 @@ size_t UpsertModifier::nrOfDocuments() const {
size_t UpsertModifier::nrOfOperations() const { return _operations.size(); }
Result UpsertModifier::setupIterator(ModifierIteratorMode const mode) {
_iteratorMode = mode;
_operationsIterator = _operations.begin();
if (mode == ModifierIteratorMode::Full) {
if (!_insertResults.hasSlice() || _insertResults.slice().isNone()) {
_insertResultsIterator = VPackArrayIterator(VPackSlice::emptyArraySlice());
} else if (_insertResults.slice().isArray()) {
_insertResultsIterator = VPackArrayIterator(_insertResults.slice());
} else {
TRI_ASSERT(false);
}
size_t UpsertModifier::nrOfResults() const {
size_t n{0};
if (!_updateResults.hasSlice() || _updateResults.slice().isNone()) {
_updateResultsIterator = VPackArrayIterator(VPackSlice::emptyArraySlice());
} else if (_updateResults.slice().isArray()) {
_updateResultsIterator = VPackArrayIterator(_updateResults.slice());
} else {
TRI_ASSERT(false);
}
if (_insertResults.hasSlice() && _insertResults.slice().isArray()) {
n += _insertResults.slice().length();
}
if (_updateResults.hasSlice() && _updateResults.slice().isArray()) {
n += _updateResults.slice().length();
}
return n;
}
size_t UpsertModifier::nrOfWritesExecuted() const { return 0; }
size_t UpsertModifier::nrOfWritesIgnored() const { return 0; }
void UpsertModifier::setupIterator() {
_operationsIterator = _operations.begin();
if (!_insertResults.hasSlice() || _insertResults.slice().isNone()) {
_insertResultsIterator = VPackArrayIterator(VPackSlice::emptyArraySlice());
} else if (_insertResults.slice().isArray()) {
_insertResultsIterator = VPackArrayIterator(_insertResults.slice());
} else {
TRI_ASSERT(false);
}
return Result{};
if (!_updateResults.hasSlice() || _updateResults.slice().isNone()) {
_updateResultsIterator = VPackArrayIterator(VPackSlice::emptyArraySlice());
} else if (_updateResults.slice().isArray()) {
_updateResultsIterator = VPackArrayIterator(_updateResults.slice());
} else {
TRI_ASSERT(false);
}
}
bool UpsertModifier::isFinishedIterator() {
@ -209,14 +226,10 @@ bool UpsertModifier::isFinishedIterator() {
}
void UpsertModifier::advanceIterator() {
if (_iteratorMode == ModifierIteratorMode::Full) {
if (_operationsIterator->first == ModOperationType::APPLY_UPDATE) {
_updateResultsIterator++;
} else if (_operationsIterator->first == ModOperationType::APPLY_INSERT) {
_insertResultsIterator++;
}
// If IGNORE_SKIP or IGNORE_RETURN the transaction results will
// not have an entry for this, so do not move any iterator.
if (_operationsIterator->first == ModOperationType::APPLY_UPDATE) {
_updateResultsIterator++;
} else if (_operationsIterator->first == ModOperationType::APPLY_INSERT) {
_insertResultsIterator++;
}
_operationsIterator++;
}
@ -225,36 +238,31 @@ void UpsertModifier::advanceIterator() {
// operation in question was APPLY_UPDATE or APPLY_INSERT to determine which
// of the results slices (UpdateReplace or Insert) we have to look in and
// increment.
UpsertModifier::ModifierOutput UpsertModifier::getOutput() {
switch (_iteratorMode) {
case ModifierIteratorMode::Full: {
if (_operationsIterator->first == ModOperationType::APPLY_UPDATE) {
return ModifierOutput{ModOperationType::APPLY_RETURN,
_operationsIterator->second, *_updateResultsIterator};
} else if (_operationsIterator->first == ModOperationType::APPLY_INSERT) {
return ModifierOutput{ModOperationType::APPLY_RETURN,
_operationsIterator->second, *_insertResultsIterator};
} else {
return ModifierOutput{_operationsIterator->first,
_operationsIterator->second, VPackSlice::noneSlice()};
}
}
case ModifierIteratorMode::OperationsOnly: {
if (_operationsIterator->first == ModOperationType::APPLY_UPDATE ||
_operationsIterator->first == ModOperationType::APPLY_INSERT) {
return ModifierOutput{ModOperationType::APPLY_RETURN,
_operationsIterator->second, VPackSlice::noneSlice()};
} else {
return ModifierOutput{_operationsIterator->first,
_operationsIterator->second, VPackSlice::noneSlice()};
}
ModifierOutput UpsertModifier::getOutput() {
if (resultAvailable()) {
VPackSlice elm;
switch (_operationsIterator->first) {
case ModOperationType::APPLY_UPDATE:
elm = *_updateResultsIterator;
break;
case ModOperationType::APPLY_INSERT:
elm = *_insertResultsIterator;
break;
default:
TRI_ASSERT(false);
}
bool error = VelocyPackHelper::getBooleanValue(elm, StaticStrings::Error, false);
return ModifierOutput{_operationsIterator->second, error,
std::make_unique<AqlValue>(elm.get(StaticStrings::Old)),
std::make_unique<AqlValue>(elm.get(StaticStrings::New))};
} else {
return ModifierOutput{_operationsIterator->second, false};
}
// shut up compiler
TRI_ASSERT(false);
return ModifierOutput{ModOperationType::IGNORE_SKIP,
InputAqlItemRow{CreateInvalidInputRowHint()},
VPackSlice::noneSlice()};
return ModifierOutput{InputAqlItemRow{CreateInvalidInputRowHint{}}, true};
}
size_t UpsertModifier::getBatchSize() const { return _batchSize; }

View File

@ -37,7 +37,6 @@ struct ModificationExecutorInfos;
class UpsertModifier {
public:
using ModifierOutput = std::tuple<ModOperationType, InputAqlItemRow, VPackSlice>;
using ModOp = std::pair<ModOperationType, InputAqlItemRow>;
public:
@ -51,9 +50,12 @@ class UpsertModifier {
size_t nrOfOperations() const;
size_t nrOfDocuments() const;
size_t nrOfResults() const;
size_t nrOfWritesExecuted() const;
size_t nrOfWritesIgnored() const;
// TODO: Make this a real iterator
Result setupIterator(ModifierIteratorMode mode);
void setupIterator();
bool isFinishedIterator();
ModifierOutput getOutput();
void advanceIterator();
@ -61,6 +63,8 @@ class UpsertModifier {
size_t getBatchSize() const;
private:
bool resultAvailable() const;
ModOperationType updateReplaceCase(ModificationExecutorAccumulator& accu,
AqlValue const& inDoc, AqlValue const& updateDoc);
ModOperationType insertCase(ModificationExecutorAccumulator& accu, AqlValue const& insertDoc);
@ -78,7 +82,6 @@ class UpsertModifier {
VPackArrayIterator _updateResultsIterator;
VPackArrayIterator _insertResultsIterator;
ModifierIteratorMode _iteratorMode;
size_t const _batchSize;
};