1
0
Fork 0

Feature/aql subquery splicing with gather (#10341)

Allow Splicing with GATHER nodes
This commit is contained in:
Tobias Gödderz 2019-11-18 19:05:44 +01:00 committed by Michael Hackstein
parent bd1316b300
commit 7a57a72620
56 changed files with 740 additions and 328 deletions

View File

@ -24,6 +24,7 @@
#include "Aggregator.h" #include "Aggregator.h"
#include "Aql/AqlValue.h" #include "Aql/AqlValue.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Transaction/Context.h" #include "Transaction/Context.h"
#include "Transaction/Helpers.h" #include "Transaction/Helpers.h"

View File

@ -33,6 +33,8 @@
#include "Aql/SharedAqlItemBlockPtr.h" #include "Aql/SharedAqlItemBlockPtr.h"
#include "Basics/StaticStrings.h" #include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
#include <velocypack/Iterator.h> #include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
@ -566,7 +568,7 @@ SharedAqlItemBlockPtr AqlItemBlock::steal(std::vector<size_t> const& chosen,
/// corresponding position /// corresponding position
/// "raw": List of actual values, positions 0 and 1 are always null /// "raw": List of actual values, positions 0 and 1 are always null
/// such that actual indices start at 2 /// such that actual indices start at 2
void AqlItemBlock::toVelocyPack(transaction::Methods* trx, VPackBuilder& result) const { void AqlItemBlock::toVelocyPack(velocypack::Options const* const trxOptions, VPackBuilder& result) const {
TRI_ASSERT(result.isOpenObject()); TRI_ASSERT(result.isOpenObject());
VPackOptions options(VPackOptions::Defaults); VPackOptions options(VPackOptions::Defaults);
options.buildUnindexedArrays = true; options.buildUnindexedArrays = true;
@ -650,7 +652,7 @@ void AqlItemBlock::toVelocyPack(transaction::Methods* trx, VPackBuilder& result)
if (it == table.end()) { if (it == table.end()) {
currentState = Next; currentState = Next;
a.toVelocyPack(trx, raw, false); a.toVelocyPack(trxOptions, raw, false);
table.try_emplace(a, pos++); table.try_emplace(a, pos++);
} else { } else {
currentState = Positional; currentState = Positional;
@ -699,20 +701,21 @@ void AqlItemBlock::toVelocyPack(transaction::Methods* trx, VPackBuilder& result)
result.add("raw", raw.slice()); result.add("raw", raw.slice());
} }
void AqlItemBlock::rowToSimpleVPack(size_t const row, transaction::Methods* trx, arangodb::velocypack::Builder& builder) const { void AqlItemBlock::rowToSimpleVPack(size_t const row, velocypack::Options const* options, arangodb::velocypack::Builder& builder) const {
VPackArrayBuilder rowBuilder{&builder}; VPackArrayBuilder rowBuilder{&builder};
if (isShadowRow(row)) { if (isShadowRow(row)) {
getShadowRowDepth(row).toVelocyPack(trx, *rowBuilder, false); getShadowRowDepth(row).toVelocyPack(options, *rowBuilder, false);
} else { } else {
AqlValue{AqlValueHintNull{}}.toVelocyPack(trx, *rowBuilder, false); AqlValue{AqlValueHintNull{}}.toVelocyPack(options, *rowBuilder, false);
} }
for (RegisterId reg = 0; reg < getNrRegs(); ++reg) { for (RegisterId reg = 0; reg < getNrRegs(); ++reg) {
getValueReference(row, reg).toVelocyPack(trx, *rowBuilder, false); getValueReference(row, reg).toVelocyPack(options, *rowBuilder, false);
} }
} }
void AqlItemBlock::toSimpleVPack(transaction::Methods* trx, arangodb::velocypack::Builder& builder) const { void AqlItemBlock::toSimpleVPack(velocypack::Options const* options,
arangodb::velocypack::Builder& builder) const {
VPackObjectBuilder block{&builder}; VPackObjectBuilder block{&builder};
block->add("nrItems", VPackValue(size())); block->add("nrItems", VPackValue(size()));
block->add("nrRegs", VPackValue(getNrRegs())); block->add("nrRegs", VPackValue(getNrRegs()));
@ -720,7 +723,7 @@ void AqlItemBlock::toSimpleVPack(transaction::Methods* trx, arangodb::velocypack
{ {
VPackArrayBuilder matrixBuilder{block.builder}; VPackArrayBuilder matrixBuilder{block.builder};
for (size_t row = 0; row < size(); ++row) { for (size_t row = 0; row < size(); ++row) {
rowToSimpleVPack(row, trx, *matrixBuilder.builder); rowToSimpleVPack(row, options, *matrixBuilder.builder);
} }
} }
} }

View File

@ -208,7 +208,7 @@ class AqlItemBlock {
/// @brief toJson, transfer a whole AqlItemBlock to Json, the result can /// @brief toJson, transfer a whole AqlItemBlock to Json, the result can
/// be used to recreate the AqlItemBlock via the Json constructor /// be used to recreate the AqlItemBlock via the Json constructor
void toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Builder&) const; void toVelocyPack(velocypack::Options const*, arangodb::velocypack::Builder&) const;
/// @brief Creates a human-readable velocypack of the block. Adds an object /// @brief Creates a human-readable velocypack of the block. Adds an object
/// `{nrItems, nrRegs, matrix}` to the builder. /// `{nrItems, nrRegs, matrix}` to the builder.
@ -217,9 +217,9 @@ class AqlItemBlock {
// (of length nrRegs+1 (sic)). The first entry contains the shadow row depth, // (of length nrRegs+1 (sic)). The first entry contains the shadow row depth,
// or `null` for data rows. The entries with indexes 1..nrRegs contain the // or `null` for data rows. The entries with indexes 1..nrRegs contain the
// registers 0..nrRegs-1, respectively. // registers 0..nrRegs-1, respectively.
void toSimpleVPack(transaction::Methods* trx, arangodb::velocypack::Builder&) const; void toSimpleVPack(velocypack::Options const*, arangodb::velocypack::Builder&) const;
void rowToSimpleVPack(size_t row, transaction::Methods* trx, void rowToSimpleVPack(size_t row, velocypack::Options const*,
velocypack::Builder& builder) const; velocypack::Builder& builder) const;
/// @brief test if the given row is a shadow row and conveys subquery /// @brief test if the given row is a shadow row and conveys subquery

View File

@ -40,8 +40,6 @@
#include <velocypack/StringRef.h> #include <velocypack/StringRef.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
#include <array>
using namespace arangodb; using namespace arangodb;
using namespace arangodb::aql; using namespace arangodb::aql;
@ -933,7 +931,7 @@ v8::Handle<v8::Value> AqlValue::toV8(v8::Isolate* isolate, transaction::Methods*
} }
/// @brief materializes a value into the builder /// @brief materializes a value into the builder
void AqlValue::toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Builder& builder, void AqlValue::toVelocyPack(VPackOptions const* options, arangodb::velocypack::Builder& builder,
bool resolveExternals) const { bool resolveExternals) const {
switch (type()) { switch (type()) {
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
@ -949,7 +947,7 @@ void AqlValue::toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Bui
bool const sanitizeCustom = true; bool const sanitizeCustom = true;
arangodb::basics::VelocyPackHelper::sanitizeNonClientTypes( arangodb::basics::VelocyPackHelper::sanitizeNonClientTypes(
slice(), VPackSlice::noneSlice(), builder, slice(), VPackSlice::noneSlice(), builder,
trx->transactionContextPtr()->getVPackOptions(), sanitizeExternals, options, sanitizeExternals,
sanitizeCustom); sanitizeCustom);
} else { } else {
builder.add(slice()); builder.add(slice());
@ -961,7 +959,7 @@ void AqlValue::toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Bui
for (auto const& it : *_data.docvec) { for (auto const& it : *_data.docvec) {
size_t const n = it->size(); size_t const n = it->size();
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
it->getValueReference(i, 0).toVelocyPack(trx, builder, resolveExternals); it->getValueReference(i, 0).toVelocyPack(options, builder, resolveExternals);
} }
} }
builder.close(); builder.close();
@ -980,8 +978,13 @@ void AqlValue::toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Bui
} }
} }
void AqlValue::toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Builder& builder,
bool resolveExternals) const {
toVelocyPack(trx->transactionContextPtr()->getVPackOptions(), builder, resolveExternals);
}
/// @brief materializes a value into the builder /// @brief materializes a value into the builder
AqlValue AqlValue::materialize(transaction::Methods* trx, bool& hasCopied, AqlValue AqlValue::materialize(VPackOptions const* options, bool& hasCopied,
bool resolveExternals) const { bool resolveExternals) const {
switch (type()) { switch (type()) {
case VPACK_INLINE: case VPACK_INLINE:
@ -997,7 +1000,7 @@ AqlValue AqlValue::materialize(transaction::Methods* trx, bool& hasCopied,
ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete); ConditionalDeleter<VPackBuffer<uint8_t>> deleter(shouldDelete);
std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>, deleter); std::shared_ptr<VPackBuffer<uint8_t>> buffer(new VPackBuffer<uint8_t>, deleter);
VPackBuilder builder(buffer); VPackBuilder builder(buffer);
toVelocyPack(trx, builder, resolveExternals); toVelocyPack(options, builder, resolveExternals);
hasCopied = true; hasCopied = true;
return AqlValue(buffer.get(), shouldDelete); return AqlValue(buffer.get(), shouldDelete);
} }
@ -1008,6 +1011,11 @@ AqlValue AqlValue::materialize(transaction::Methods* trx, bool& hasCopied,
return AqlValue(); return AqlValue();
} }
AqlValue AqlValue::materialize(transaction::Methods* trx, bool& hasCopied,
bool resolveExternals) const {
return materialize(trx->transactionContextPtr()->getVPackOptions(), hasCopied, resolveExternals);
}
/// @brief clone a value /// @brief clone a value
AqlValue AqlValue::clone() const { AqlValue AqlValue::clone() const {
switch (type()) { switch (type()) {
@ -1173,23 +1181,24 @@ AqlValue AqlValue::CreateFromBlocks(transaction::Methods* trx,
} }
/// @brief comparison for AqlValue objects /// @brief comparison for AqlValue objects
int AqlValue::Compare(transaction::Methods* trx, AqlValue const& left, int AqlValue::Compare(velocypack::Options const* options, AqlValue const& left,
AqlValue const& right, bool compareUtf8) { AqlValue const& right, bool compareUtf8) {
AqlValue::AqlValueType const leftType = left.type(); AqlValue::AqlValueType const leftType = left.type();
AqlValue::AqlValueType const rightType = right.type(); AqlValue::AqlValueType const rightType = right.type();
if (leftType != rightType) { if (leftType != rightType) {
// TODO implement this case more efficiently
if (leftType == RANGE || rightType == RANGE || leftType == DOCVEC || rightType == DOCVEC) { if (leftType == RANGE || rightType == RANGE || leftType == DOCVEC || rightType == DOCVEC) {
// range|docvec against x // range|docvec against x
transaction::BuilderLeaser leftBuilder(trx); VPackBuilder leftBuilder;
left.toVelocyPack(trx, *leftBuilder.get(), false); left.toVelocyPack(options, leftBuilder, false);
transaction::BuilderLeaser rightBuilder(trx); VPackBuilder rightBuilder;
right.toVelocyPack(trx, *rightBuilder.get(), false); right.toVelocyPack(options, rightBuilder, false);
return arangodb::basics::VelocyPackHelper::compare( return arangodb::basics::VelocyPackHelper::compare(leftBuilder.slice(),
leftBuilder->slice(), rightBuilder->slice(), compareUtf8, rightBuilder.slice(),
trx->transactionContextPtr()->getVPackOptions()); compareUtf8, options);
} }
// fall-through to other types intentional // fall-through to other types intentional
} }
@ -1201,9 +1210,8 @@ int AqlValue::Compare(transaction::Methods* trx, AqlValue const& left,
case VPACK_SLICE_POINTER: case VPACK_SLICE_POINTER:
case VPACK_MANAGED_SLICE: case VPACK_MANAGED_SLICE:
case VPACK_MANAGED_BUFFER: { case VPACK_MANAGED_BUFFER: {
return arangodb::basics::VelocyPackHelper::compare( return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(),
left.slice(), right.slice(), compareUtf8, compareUtf8, options);
trx->transactionContextPtr()->getVPackOptions());
} }
case DOCVEC: { case DOCVEC: {
// use lexicographic ordering of AqlValues regardless of block, // use lexicographic ordering of AqlValues regardless of block,
@ -1232,7 +1240,7 @@ int AqlValue::Compare(transaction::Methods* trx, AqlValue const& left,
AqlValue const& rval = AqlValue const& rval =
right._data.docvec->at(rblock)->getValueReference(ritem, 0); right._data.docvec->at(rblock)->getValueReference(ritem, 0);
int cmp = Compare(trx, lval, rval, compareUtf8); int cmp = Compare(options, lval, rval, compareUtf8);
if (cmp != 0) { if (cmp != 0) {
return cmp; return cmp;
@ -1280,6 +1288,11 @@ int AqlValue::Compare(transaction::Methods* trx, AqlValue const& left,
return 0; return 0;
} }
int AqlValue::Compare(transaction::Methods* trx, AqlValue const& left,
AqlValue const& right, bool compareUtf8) {
return Compare(trx->transactionContextPtr()->getVPackOptions(), left, right, compareUtf8);
}
AqlValue::AqlValue(std::vector<arangodb::aql::SharedAqlItemBlockPtr>* docvec) noexcept { AqlValue::AqlValue(std::vector<arangodb::aql::SharedAqlItemBlockPtr>* docvec) noexcept {
TRI_ASSERT(docvec != nullptr); TRI_ASSERT(docvec != nullptr);
_data.docvec = docvec; _data.docvec = docvec;
@ -1610,68 +1623,8 @@ AqlValueGuard::~AqlValueGuard() {
} }
void AqlValueGuard::steal() { _destroy = false; } void AqlValueGuard::steal() { _destroy = false; }
AqlValue& AqlValueGuard::value() { return _value; } AqlValue& AqlValueGuard::value() { return _value; }
AqlValueMaterializer::AqlValueMaterializer(transaction::Methods* trx)
: trx(trx), materialized(), hasCopied(false) {}
AqlValueMaterializer::AqlValueMaterializer(AqlValueMaterializer const& other)
: trx(other.trx), materialized(other.materialized), hasCopied(other.hasCopied) {
if (other.hasCopied) {
// copy other's slice
materialized = other.materialized.clone();
}
}
AqlValueMaterializer& AqlValueMaterializer::operator=(AqlValueMaterializer const& other) {
if (this != &other) {
TRI_ASSERT(trx == other.trx); // must be from same transaction
trx = other.trx; // to shut up cppcheck
if (hasCopied) {
// destroy our own slice
materialized.destroy();
hasCopied = false;
}
// copy other's slice
materialized = other.materialized.clone();
hasCopied = other.hasCopied;
}
return *this;
}
AqlValueMaterializer::AqlValueMaterializer(AqlValueMaterializer&& other) noexcept
: trx(other.trx), materialized(other.materialized), hasCopied(other.hasCopied) {
// reset other
other.hasCopied = false;
// cppcheck-suppress *
other.materialized = AqlValue();
}
AqlValueMaterializer& AqlValueMaterializer::operator=(AqlValueMaterializer&& other) noexcept {
if (this != &other) {
TRI_ASSERT(trx == other.trx); // must be from same transaction
trx = other.trx; // to shut up cppcheck
if (hasCopied) {
// destroy our own slice
materialized.destroy();
}
// reset other
materialized = other.materialized;
hasCopied = other.hasCopied;
other.materialized = AqlValue();
}
return *this;
}
AqlValueMaterializer::~AqlValueMaterializer() {
if (hasCopied) {
materialized.destroy();
}
}
arangodb::velocypack::Slice AqlValueMaterializer::slice(AqlValue const& value,
bool resolveExternals) {
materialized = value.materialize(trx, hasCopied, resolveExternals);
return materialized.slice();
}
size_t std::hash<arangodb::aql::AqlValue>::operator()(arangodb::aql::AqlValue const& x) const size_t std::hash<arangodb::aql::AqlValue>::operator()(arangodb::aql::AqlValue const& x) const
noexcept { noexcept {

View File

@ -47,6 +47,7 @@ namespace velocypack {
template <typename T> template <typename T>
class Buffer; class Buffer;
class Builder; class Builder;
struct Options;
class Slice; class Slice;
class StringRef; class StringRef;
} }
@ -329,11 +330,12 @@ struct AqlValue final {
v8::Handle<v8::Value> toV8(v8::Isolate* isolate, transaction::Methods*) const; v8::Handle<v8::Value> toV8(v8::Isolate* isolate, transaction::Methods*) const;
/// @brief materializes a value into the builder /// @brief materializes a value into the builder
void toVelocyPack(transaction::Methods*, arangodb::velocypack::Builder& builder, void toVelocyPack(velocypack::Options const*, arangodb::velocypack::Builder&, bool resolveExternals) const;
bool resolveExternals) const; void toVelocyPack(transaction::Methods*, arangodb::velocypack::Builder&, bool resolveExternals) const;
/// @brief materialize a value into a new one. this expands docvecs and /// @brief materialize a value into a new one. this expands docvecs and
/// ranges /// ranges
AqlValue materialize(velocypack::Options const*, bool& hasCopied, bool resolveExternals) const;
AqlValue materialize(transaction::Methods*, bool& hasCopied, bool resolveExternals) const; AqlValue materialize(transaction::Methods*, bool& hasCopied, bool resolveExternals) const;
/// @brief return the slice for the value /// @brief return the slice for the value
@ -364,6 +366,8 @@ struct AqlValue final {
arangodb::aql::RegisterId); arangodb::aql::RegisterId);
/// @brief compare function for two values /// @brief compare function for two values
static int Compare(velocypack::Options const*, AqlValue const& left,
AqlValue const& right, bool useUtf8);
static int Compare(transaction::Methods*, AqlValue const& left, static int Compare(transaction::Methods*, AqlValue const& left,
AqlValue const& right, bool useUtf8); AqlValue const& right, bool useUtf8);
@ -399,28 +403,6 @@ class AqlValueGuard {
bool _destroy; bool _destroy;
}; };
struct AqlValueMaterializer {
explicit AqlValueMaterializer(transaction::Methods* trx);
AqlValueMaterializer(AqlValueMaterializer const& other);
// cppcheck-suppress operatorEqVarError
AqlValueMaterializer& operator=(AqlValueMaterializer const& other);
AqlValueMaterializer(AqlValueMaterializer&& other) noexcept;
// cppcheck-suppress operatorEqVarError
AqlValueMaterializer& operator=(AqlValueMaterializer&& other) noexcept;
~AqlValueMaterializer();
arangodb::velocypack::Slice slice(AqlValue const& value, bool resolveExternals);
transaction::Methods* trx;
AqlValue materialized;
bool hasCopied;
};
static_assert(sizeof(AqlValue) == 16, "invalid AqlValue size"); static_assert(sizeof(AqlValue) == 16, "invalid AqlValue size");
} // namespace aql } // namespace aql

View File

@ -0,0 +1,107 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Jan Steemann
/// @author Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#include "AqlValueMaterializer.h"
#include "Basics/debugging.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
#include <velocypack/Slice.h>
using namespace arangodb;
using namespace arangodb::aql;
AqlValueMaterializer::AqlValueMaterializer(velocypack::Options const* options)
: options(options),
materialized(),
hasCopied(false) {}
AqlValueMaterializer::AqlValueMaterializer(transaction::Methods* trx)
: options(trx->transactionContextPtr()->getVPackOptions()),
materialized(),
hasCopied(false) {}
AqlValueMaterializer::AqlValueMaterializer(AqlValueMaterializer const& other)
: options(other.options),
materialized(other.materialized),
hasCopied(other.hasCopied) {
if (other.hasCopied) {
// copy other's slice
materialized = other.materialized.clone();
}
}
AqlValueMaterializer& AqlValueMaterializer::operator=(AqlValueMaterializer const& other) {
if (this != &other) {
TRI_ASSERT(options == other.options); // must be from same transaction
options = other.options;
if (hasCopied) {
// destroy our own slice
materialized.destroy();
hasCopied = false;
}
// copy other's slice
materialized = other.materialized.clone();
hasCopied = other.hasCopied;
}
return *this;
}
AqlValueMaterializer::AqlValueMaterializer(AqlValueMaterializer&& other) noexcept
: options(other.options),
materialized(other.materialized),
hasCopied(other.hasCopied) {
// reset other
other.hasCopied = false;
// cppcheck-suppress *
other.materialized = AqlValue();
}
AqlValueMaterializer& AqlValueMaterializer::operator=(AqlValueMaterializer&& other) noexcept {
if (this != &other) {
TRI_ASSERT(options == other.options); // must be from same transaction
options = other.options;
if (hasCopied) {
// destroy our own slice
materialized.destroy();
}
// reset other
materialized = other.materialized;
hasCopied = other.hasCopied;
other.materialized = AqlValue();
}
return *this;
}
AqlValueMaterializer::~AqlValueMaterializer() {
if (hasCopied) {
materialized.destroy();
}
}
arangodb::velocypack::Slice AqlValueMaterializer::slice(AqlValue const& value,
bool resolveExternals) {
materialized = value.materialize(options, hasCopied, resolveExternals);
return materialized.slice();
}

View File

@ -0,0 +1,70 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Jan Steemann
/// @author Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_AQLVALUEMATERIALIZER_H
#define ARANGOD_AQL_AQLVALUEMATERIALIZER_H
#include "Aql/AqlValue.h"
namespace arangodb {
namespace transaction {
class Methods;
}
namespace velocypack {
class Slice;
struct Options;
} // namespace velocypack
namespace aql {
/// @brief Helper class to materialize AqlValues (see AqlValue::materialize).
struct AqlValueMaterializer {
explicit AqlValueMaterializer(velocypack::Options const* options);
explicit AqlValueMaterializer(arangodb::transaction::Methods* trx);
AqlValueMaterializer(AqlValueMaterializer const& other);
// cppcheck-suppress operatorEqVarError
AqlValueMaterializer& operator=(AqlValueMaterializer const& other);
AqlValueMaterializer(AqlValueMaterializer&& other) noexcept;
// cppcheck-suppress operatorEqVarError
AqlValueMaterializer& operator=(AqlValueMaterializer&& other) noexcept;
~AqlValueMaterializer();
arangodb::velocypack::Slice slice(arangodb::aql::AqlValue const& value, bool resolveExternals);
arangodb::velocypack::Options const* options;
arangodb::aql::AqlValue materialized;
bool hasCopied;
};
} // namespace aql
} // namespace arangodb
#endif // ARANGOD_AQL_AQLVALUEMATERIALIZER_H

View File

@ -37,6 +37,7 @@
#include "Aql/Graphs.h" #include "Aql/Graphs.h"
#include "Aql/ModificationOptions.h" #include "Aql/ModificationOptions.h"
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
#include "Basics/StringUtils.h" #include "Basics/StringUtils.h"
#include "Basics/tri-strings.h" #include "Basics/tri-strings.h"

View File

@ -49,6 +49,7 @@
#include "Aql/SortRegister.h" #include "Aql/SortRegister.h"
#include "Aql/SortingGatherExecutor.h" #include "Aql/SortingGatherExecutor.h"
#include "Aql/UnsortedGatherExecutor.h" #include "Aql/UnsortedGatherExecutor.h"
#include "Aql/UnsortingGatherExecutor.h"
#include "Aql/types.h" #include "Aql/types.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Cluster/ServerState.h" #include "Cluster/ServerState.h"
@ -492,27 +493,16 @@ std::unique_ptr<ExecutionBlock> GatherNode::createBlock(
if (_elements.empty()) { if (_elements.empty()) {
TRI_ASSERT(getRegisterPlan()->nrRegs[previousNode->getDepth()] == TRI_ASSERT(getRegisterPlan()->nrRegs[previousNode->getDepth()] ==
getRegisterPlan()->nrRegs[getDepth()]); getRegisterPlan()->nrRegs[getDepth()]);
if (ServerState::instance()->isCoordinator() && _parallelism == Parallelism::Parallel) {
if (ServerState::instance()->isCoordinator()) {
// In the coordinator case the GatherBlock will fetch from RemoteBlocks.
if (_parallelism == Parallelism::Parallel) {
UnsortedGatherExecutorInfos infos(getRegisterPlan()->nrRegs[getDepth()], UnsortedGatherExecutorInfos infos(getRegisterPlan()->nrRegs[getDepth()],
calcRegsToKeep(), getRegsToClear()); calcRegsToKeep(), getRegsToClear());
return std::make_unique<ExecutionBlockImpl<UnsortedGatherExecutor>>(&engine, this, std::move(infos)); return std::make_unique<ExecutionBlockImpl<UnsortedGatherExecutor>>(&engine, this, std::move(infos));
}
IdExecutorInfos infos(getRegisterPlan()->nrRegs[getDepth()],
calcRegsToKeep(), getRegsToClear());
// We want to immediately move the block on and not wait for additional requests here (hence passthrough)
return std::make_unique<ExecutionBlockImpl<IdExecutor<BlockPassthrough::Enable, SingleRowFetcher<BlockPassthrough::Enable>>>>(
&engine, this, std::move(infos));
} else { } else {
IdExecutorInfos infos(getRegisterPlan()->nrRegs[getDepth()], IdExecutorInfos infos(getRegisterPlan()->nrRegs[getDepth()],
calcRegsToKeep(), getRegsToClear()); calcRegsToKeep(), getRegsToClear());
// In the DBServer case the GatherBlock will merge local results and then expose them (directly or indirectly)
// to the RemoteBlock on coordinator. We want to trigger as few requests as possible, so we invest the little return std::make_unique<ExecutionBlockImpl<UnsortingGatherExecutor>>(&engine, this,
// memory inefficiency that we have here in favor of a better grouping of requests. std::move(infos));
return std::make_unique<ExecutionBlockImpl<IdExecutor<BlockPassthrough::Disable, SingleRowFetcher<BlockPassthrough::Disable>>>>(
&engine, this, std::move(infos));
} }
} }

View File

@ -54,9 +54,9 @@ void eraseRow(SharedAqlItemBlockPtr& block, size_t row) {
/// @brief OurLessThan /// @brief OurLessThan
class arangodb::aql::ConstrainedLessThan { class arangodb::aql::ConstrainedLessThan {
public: public:
ConstrainedLessThan(arangodb::transaction::Methods* trx, ConstrainedLessThan(velocypack::Options const* options,
std::vector<arangodb::aql::SortRegister>& sortRegisters) noexcept std::vector<arangodb::aql::SortRegister>& sortRegisters) noexcept
: _trx(trx), _heapBuffer(nullptr), _sortRegisters(sortRegisters) {} : _vpackOptions(options), _heapBuffer(nullptr), _sortRegisters(sortRegisters) {}
void setBuffer(arangodb::aql::AqlItemBlock* heap) { _heapBuffer = heap; } void setBuffer(arangodb::aql::AqlItemBlock* heap) { _heapBuffer = heap; }
@ -67,7 +67,7 @@ class arangodb::aql::ConstrainedLessThan {
auto const& lhs = _heapBuffer->getValueReference(a, sortReg.reg); auto const& lhs = _heapBuffer->getValueReference(a, sortReg.reg);
auto const& rhs = _heapBuffer->getValueReference(b, sortReg.reg); auto const& rhs = _heapBuffer->getValueReference(b, sortReg.reg);
int const cmp = arangodb::aql::AqlValue::Compare(_trx, lhs, rhs, true); int const cmp = arangodb::aql::AqlValue::Compare(_vpackOptions, lhs, rhs, true);
if (cmp < 0) { if (cmp < 0) {
return sortReg.asc; return sortReg.asc;
@ -80,7 +80,7 @@ class arangodb::aql::ConstrainedLessThan {
} }
private: private:
arangodb::transaction::Methods* _trx; velocypack::Options const* const _vpackOptions;
arangodb::aql::AqlItemBlock* _heapBuffer; arangodb::aql::AqlItemBlock* _heapBuffer;
std::vector<arangodb::aql::SortRegister>& _sortRegisters; std::vector<arangodb::aql::SortRegister>& _sortRegisters;
}; // ConstrainedLessThan }; // ConstrainedLessThan
@ -123,7 +123,7 @@ bool ConstrainedSortExecutor::compareInput(size_t const& rowPos, InputAqlItemRow
auto const& lhs = _heapBuffer->getValueReference(rowPos, reg.reg); auto const& lhs = _heapBuffer->getValueReference(rowPos, reg.reg);
auto const& rhs = row.getValue(reg.reg); auto const& rhs = row.getValue(reg.reg);
int const cmp = arangodb::aql::AqlValue::Compare(_infos.trx(), lhs, rhs, true); int const cmp = arangodb::aql::AqlValue::Compare(_infos.vpackOptions(), lhs, rhs, true);
if (cmp < 0) { if (cmp < 0) {
return reg.asc; return reg.asc;
@ -144,7 +144,8 @@ ConstrainedSortExecutor::ConstrainedSortExecutor(Fetcher& fetcher, SortExecutorI
_skippedAfter(0), _skippedAfter(0),
_heapBuffer(_infos._manager.requestBlock(_infos._limit, _heapBuffer(_infos._manager.requestBlock(_infos._limit,
_infos.numberOfOutputRegisters())), _infos.numberOfOutputRegisters())),
_cmpHeap(std::make_unique<ConstrainedLessThan>(_infos.trx(), _infos.sortRegisters())), _cmpHeap(std::make_unique<ConstrainedLessThan>(_infos.vpackOptions(),
_infos.sortRegisters())),
_heapOutputRow{_heapBuffer, make_shared_unordered_set(), _heapOutputRow{_heapBuffer, make_shared_unordered_set(),
make_shared_unordered_set(_infos.numberOfOutputRegisters()), make_shared_unordered_set(_infos.numberOfOutputRegisters()),
_infos.registersToClear()} { _infos.registersToClear()} {

View File

@ -255,7 +255,8 @@ template <BlockPassthrough blockPassthrough>
DependencyProxy<blockPassthrough>::DependencyProxy( DependencyProxy<blockPassthrough>::DependencyProxy(
std::vector<ExecutionBlock*> const& dependencies, AqlItemBlockManager& itemBlockManager, std::vector<ExecutionBlock*> const& dependencies, AqlItemBlockManager& itemBlockManager,
std::shared_ptr<std::unordered_set<RegisterId> const> inputRegisters, std::shared_ptr<std::unordered_set<RegisterId> const> inputRegisters,
RegisterId nrInputRegisters) RegisterId nrInputRegisters,
velocypack::Options const* const options)
: _dependencies(dependencies), : _dependencies(dependencies),
_itemBlockManager(itemBlockManager), _itemBlockManager(itemBlockManager),
_inputRegisters(std::move(inputRegisters)), _inputRegisters(std::move(inputRegisters)),
@ -264,7 +265,8 @@ DependencyProxy<blockPassthrough>::DependencyProxy(
_blockQueue(), _blockQueue(),
_blockPassThroughQueue(), _blockPassThroughQueue(),
_currentDependency(0), _currentDependency(0),
_skipped(0) {} _skipped(0),
_vpackOptions(options) {}
template <BlockPassthrough blockPassthrough> template <BlockPassthrough blockPassthrough>
RegisterId DependencyProxy<blockPassthrough>::getNrInputRegisters() const { RegisterId DependencyProxy<blockPassthrough>::getNrInputRegisters() const {
@ -316,5 +318,11 @@ bool DependencyProxy<blockPassthrough>::advanceDependency() {
return true; return true;
} }
template <BlockPassthrough allowBlockPassthrough>
velocypack::Options const* DependencyProxy<allowBlockPassthrough>::velocypackOptions() const
noexcept {
return _vpackOptions;
}
template class ::arangodb::aql::DependencyProxy<BlockPassthrough::Enable>; template class ::arangodb::aql::DependencyProxy<BlockPassthrough::Enable>;
template class ::arangodb::aql::DependencyProxy<BlockPassthrough::Disable>; template class ::arangodb::aql::DependencyProxy<BlockPassthrough::Disable>;

View File

@ -68,7 +68,8 @@ class DependencyProxy {
DependencyProxy(std::vector<ExecutionBlock*> const& dependencies, DependencyProxy(std::vector<ExecutionBlock*> const& dependencies,
AqlItemBlockManager& itemBlockManager, AqlItemBlockManager& itemBlockManager,
std::shared_ptr<std::unordered_set<RegisterId> const> inputRegisters, std::shared_ptr<std::unordered_set<RegisterId> const> inputRegisters,
RegisterId nrInputRegisters); RegisterId nrInputRegisters,
velocypack::Options const*);
TEST_VIRTUAL ~DependencyProxy() = default; TEST_VIRTUAL ~DependencyProxy() = default;
@ -109,6 +110,8 @@ class DependencyProxy {
void setDistributeId(std::string const& distId) { _distributeId = distId; } void setDistributeId(std::string const& distId) { _distributeId = distId; }
[[nodiscard]] velocypack::Options const* velocypackOptions() const noexcept;
protected: protected:
[[nodiscard]] AqlItemBlockManager& itemBlockManager(); [[nodiscard]] AqlItemBlockManager& itemBlockManager();
[[nodiscard]] AqlItemBlockManager const& itemBlockManager() const; [[nodiscard]] AqlItemBlockManager const& itemBlockManager() const;
@ -134,6 +137,7 @@ class DependencyProxy {
// only modified in case of multiple dependencies + Passthrough otherwise always 0 // only modified in case of multiple dependencies + Passthrough otherwise always 0
size_t _currentDependency; size_t _currentDependency;
size_t _skipped; size_t _skipped;
velocypack::Options const* const _vpackOptions;
}; };
} // namespace arangodb::aql } // namespace arangodb::aql

View File

@ -251,11 +251,18 @@ std::pair<ExecutionState, bool> ExecutionBlockImpl<DistributeExecutor>::getBlock
SharedAqlItemBlockPtr cur = _buffer[_index]; SharedAqlItemBlockPtr cur = _buffer[_index];
while (_pos < cur->size()) { for (; _pos < cur->size(); ++_pos) {
if (!cur->isShadowRow(_pos)) {
// this may modify the input item buffer in place // this may modify the input item buffer in place
size_t const id = sendToClient(cur); size_t const id = sendToClient(cur);
_distBuffer[id].emplace_back(_index, _pos++); _distBuffer[id].emplace_back(_index, _pos);
} else {
// A shadow row must always be distributed to all clients.
for (auto& dist : _distBuffer) {
dist.emplace_back(_index, _pos);
}
}
} }
if (_pos == cur->size()) { if (_pos == cur->size()) {

View File

@ -70,7 +70,7 @@ std::string const& stateToString(aql::ExecutionState state) {
ExecutionBlock::ExecutionBlock(ExecutionEngine* engine, ExecutionNode const* ep) ExecutionBlock::ExecutionBlock(ExecutionEngine* engine, ExecutionNode const* ep)
: _engine(engine), : _engine(engine),
_trx(engine->getQuery()->trx()), _trxVpackOptions(engine->getQuery()->trx()->transactionContextPtr()->getVPackOptions()),
_shutdownResult(TRI_ERROR_NO_ERROR), _shutdownResult(TRI_ERROR_NO_ERROR),
_done(false), _done(false),
_isInSplicedSubquery(ep != nullptr ? ep->isInSplicedSubquery() : false), _isInSplicedSubquery(ep != nullptr ? ep->isInSplicedSubquery() : false),
@ -190,8 +190,8 @@ std::pair<ExecutionState, SharedAqlItemBlockPtr> ExecutionBlock::traceGetSomeEnd
<< "getSome type=" << node->getTypeString() << " result: nullptr"; << "getSome type=" << node->getTypeString() << " result: nullptr";
} else { } else {
VPackBuilder builder; VPackBuilder builder;
result->toSimpleVPack(transaction(), builder); auto const options = trxVpackOptions();
auto options = transaction()->transactionContextPtr()->getVPackOptions(); result->toSimpleVPack(options, builder);
LOG_TOPIC("fcd9c", INFO, Logger::QUERIES) LOG_TOPIC("fcd9c", INFO, Logger::QUERIES)
<< "[query#" << queryId << "] " << "[query#" << queryId << "] "
<< "getSome type=" << node->getTypeString() << "getSome type=" << node->getTypeString()
@ -272,7 +272,9 @@ ExecutionState ExecutionBlock::getHasMoreState() {
ExecutionNode const* ExecutionBlock::getPlanNode() const { return _exeNode; } ExecutionNode const* ExecutionBlock::getPlanNode() const { return _exeNode; }
transaction::Methods* ExecutionBlock::transaction() const { return _trx; } velocypack::Options const* ExecutionBlock::trxVpackOptions() const noexcept {
return _trxVpackOptions;
}
void ExecutionBlock::addDependency(ExecutionBlock* ep) { void ExecutionBlock::addDependency(ExecutionBlock* ep) {
TRI_ASSERT(ep != nullptr); TRI_ASSERT(ep != nullptr);

View File

@ -117,7 +117,7 @@ class ExecutionBlock {
// TODO: Can we get rid of this? Problem: Subquery Executor is using it. // TODO: Can we get rid of this? Problem: Subquery Executor is using it.
ExecutionNode const* getPlanNode() const; ExecutionNode const* getPlanNode() const;
transaction::Methods* transaction() const; [[nodiscard]] velocypack::Options const* trxVpackOptions() const noexcept;
/// @brief add a dependency /// @brief add a dependency
void addDependency(ExecutionBlock* ep); void addDependency(ExecutionBlock* ep);
@ -128,8 +128,7 @@ class ExecutionBlock {
/// @brief the execution engine /// @brief the execution engine
ExecutionEngine* _engine; ExecutionEngine* _engine;
/// @brief the transaction for this query velocypack::Options const* _trxVpackOptions;
transaction::Methods* _trx;
/// @brief the Result returned during the shutdown phase. Is kept for multiple /// @brief the Result returned during the shutdown phase. Is kept for multiple
/// waiting phases. /// waiting phases.

View File

@ -42,7 +42,6 @@
#include "Aql/IResearchViewExecutor.h" #include "Aql/IResearchViewExecutor.h"
#include "Aql/IdExecutor.h" #include "Aql/IdExecutor.h"
#include "Aql/IndexExecutor.h" #include "Aql/IndexExecutor.h"
#include "Aql/IndexNode.h"
#include "Aql/InputAqlItemRow.h" #include "Aql/InputAqlItemRow.h"
#include "Aql/KShortestPathsExecutor.h" #include "Aql/KShortestPathsExecutor.h"
#include "Aql/LimitExecutor.h" #include "Aql/LimitExecutor.h"
@ -65,6 +64,7 @@
#include "Aql/SubqueryStartExecutor.h" #include "Aql/SubqueryStartExecutor.h"
#include "Aql/TraversalExecutor.h" #include "Aql/TraversalExecutor.h"
#include "Aql/UnsortedGatherExecutor.h" #include "Aql/UnsortedGatherExecutor.h"
#include "Aql/UnsortingGatherExecutor.h"
#include "Aql/SimpleModifier.h" #include "Aql/SimpleModifier.h"
#include "Aql/UpsertModifier.h" #include "Aql/UpsertModifier.h"
@ -108,7 +108,8 @@ ExecutionBlockImpl<Executor>::ExecutionBlockImpl(ExecutionEngine* engine,
typename Executor::Infos&& infos) typename Executor::Infos&& infos)
: ExecutionBlock(engine, node), : ExecutionBlock(engine, node),
_dependencyProxy(_dependencies, engine->itemBlockManager(), _dependencyProxy(_dependencies, engine->itemBlockManager(),
infos.getInputRegisters(), infos.numberOfInputRegisters()), infos.getInputRegisters(), infos.numberOfInputRegisters(),
trxVpackOptions()),
_rowFetcher(_dependencyProxy), _rowFetcher(_dependencyProxy),
_infos(std::move(infos)), _infos(std::move(infos)),
_executor(_rowFetcher, _infos), _executor(_rowFetcher, _infos),
@ -370,9 +371,9 @@ static SkipVariants constexpr skipType() {
std::is_same<Executor, IResearchViewMergeExecutor<true, false>>::value || std::is_same<Executor, IResearchViewMergeExecutor<true, false>>::value ||
std::is_same<Executor, EnumerateCollectionExecutor>::value || std::is_same<Executor, EnumerateCollectionExecutor>::value ||
std::is_same<Executor, LimitExecutor>::value || std::is_same<Executor, LimitExecutor>::value ||
std::is_same<Executor, IdExecutor<BlockPassthrough::Disable, SingleRowFetcher<BlockPassthrough::Disable>>>::value ||
std::is_same<Executor, ConstrainedSortExecutor>::value || std::is_same<Executor, ConstrainedSortExecutor>::value ||
std::is_same<Executor, SortingGatherExecutor>::value || std::is_same<Executor, SortingGatherExecutor>::value ||
std::is_same<Executor, UnsortingGatherExecutor>::value ||
std::is_same<Executor, UnsortedGatherExecutor>::value || std::is_same<Executor, UnsortedGatherExecutor>::value ||
std::is_same<Executor, MaterializeExecutor<RegisterId>>::value || std::is_same<Executor, MaterializeExecutor<RegisterId>>::value ||
std::is_same<Executor, MaterializeExecutor<std::string const&>>::value), std::is_same<Executor, MaterializeExecutor<std::string const&>>::value),
@ -871,8 +872,6 @@ template class ::arangodb::aql::ExecutionBlockImpl<IResearchViewMergeExecutor<tr
template class ::arangodb::aql::ExecutionBlockImpl<IdExecutor<BlockPassthrough::Enable, ConstFetcher>>; template class ::arangodb::aql::ExecutionBlockImpl<IdExecutor<BlockPassthrough::Enable, ConstFetcher>>;
template class ::arangodb::aql::ExecutionBlockImpl< template class ::arangodb::aql::ExecutionBlockImpl<
IdExecutor<BlockPassthrough::Enable, SingleRowFetcher<BlockPassthrough::Enable>>>; IdExecutor<BlockPassthrough::Enable, SingleRowFetcher<BlockPassthrough::Enable>>>;
template class ::arangodb::aql::ExecutionBlockImpl<
IdExecutor<BlockPassthrough::Disable, SingleRowFetcher<BlockPassthrough::Disable>>>;
template class ::arangodb::aql::ExecutionBlockImpl<IndexExecutor>; template class ::arangodb::aql::ExecutionBlockImpl<IndexExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<LimitExecutor>; template class ::arangodb::aql::ExecutionBlockImpl<LimitExecutor>;
@ -897,6 +896,7 @@ template class ::arangodb::aql::ExecutionBlockImpl<SubqueryStartExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<TraversalExecutor>; template class ::arangodb::aql::ExecutionBlockImpl<TraversalExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<SortingGatherExecutor>; template class ::arangodb::aql::ExecutionBlockImpl<SortingGatherExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<UnsortedGatherExecutor>; template class ::arangodb::aql::ExecutionBlockImpl<UnsortedGatherExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<UnsortingGatherExecutor>;
template class ::arangodb::aql::ExecutionBlockImpl<MaterializeExecutor<RegisterId>>; template class ::arangodb::aql::ExecutionBlockImpl<MaterializeExecutor<RegisterId>>;
template class ::arangodb::aql::ExecutionBlockImpl<MaterializeExecutor<std::string const&>>; template class ::arangodb::aql::ExecutionBlockImpl<MaterializeExecutor<std::string const&>>;

View File

@ -567,6 +567,7 @@ bool ExecutionNode::isEqualTo(ExecutionNode const& other) const {
return ((this->getType() == other.getType()) && (_id == other._id) && return ((this->getType() == other.getType()) && (_id == other._id) &&
(_depth == other._depth) && (_depth == other._depth) &&
(isInSplicedSubquery() == other.isInSplicedSubquery()) &&
(std::equal(_dependencies.begin(), _dependencies.end(), (std::equal(_dependencies.begin(), _dependencies.end(),
other._dependencies.begin(), comparator))); other._dependencies.begin(), comparator)));
} }

View File

@ -37,6 +37,7 @@
#include "Aql/Range.h" #include "Aql/Range.h"
#include "Aql/V8Executor.h" #include "Aql/V8Executor.h"
#include "Aql/Variable.h" #include "Aql/Variable.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
#include "Basics/NumberUtils.h" #include "Basics/NumberUtils.h"
#include "Basics/StringBuffer.h" #include "Basics/StringBuffer.h"

View File

@ -32,6 +32,7 @@
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Aql/Range.h" #include "Aql/Range.h"
#include "Aql/V8Executor.h" #include "Aql/V8Executor.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
#include "Basics/HybridLogicalClock.h" #include "Basics/HybridLogicalClock.h"
#include "Basics/Mutex.h" #include "Basics/Mutex.h"

View File

@ -31,6 +31,8 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
// TODO Clean up unused variants of the IdExecutor - some of them aren't in use anymore.
namespace arangodb { namespace arangodb {
namespace transaction { namespace transaction {
class Methods; class Methods;
@ -57,6 +59,7 @@ class IdExecutorInfos : public ExecutorInfos {
std::string const& distributeId(); std::string const& distributeId();
// TODO This is probably needed only for UnsortingGather now, so can be removed here.
[[nodiscard]] bool isResponsibleForInitializeCursor() const; [[nodiscard]] bool isResponsibleForInitializeCursor() const;
private: private:

View File

@ -38,6 +38,7 @@
#include "Aql/OutputAqlItemRow.h" #include "Aql/OutputAqlItemRow.h"
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Aql/SingleRowFetcher.h" #include "Aql/SingleRowFetcher.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/ScopeGuard.h" #include "Basics/ScopeGuard.h"
#include "Cluster/ServerState.h" #include "Cluster/ServerState.h"
#include "ExecutorExpressionContext.h" #include "ExecutorExpressionContext.h"

View File

@ -119,7 +119,7 @@ SharedAqlItemBlockPtr InputAqlItemRow::cloneToBlock(AqlItemBlockManager& manager
/// corresponding position /// corresponding position
/// "raw": List of actual values, positions 0 and 1 are always null /// "raw": List of actual values, positions 0 and 1 are always null
/// such that actual indices start at 2 /// such that actual indices start at 2
void InputAqlItemRow::toVelocyPack(transaction::Methods* trx, VPackBuilder& result) const { void InputAqlItemRow::toVelocyPack(velocypack::Options const* const trxOptions, VPackBuilder& result) const {
TRI_ASSERT(isInitialized()); TRI_ASSERT(isInitialized());
TRI_ASSERT(result.isOpenObject()); TRI_ASSERT(result.isOpenObject());
VPackOptions options(VPackOptions::Defaults); VPackOptions options(VPackOptions::Defaults);
@ -203,7 +203,7 @@ void InputAqlItemRow::toVelocyPack(transaction::Methods* trx, VPackBuilder& resu
if (it == table.end()) { if (it == table.end()) {
currentState = Next; currentState = Next;
a.toVelocyPack(trx, raw, false); a.toVelocyPack(trxOptions, raw, false);
table.try_emplace(a, pos++); table.try_emplace(a, pos++);
} else { } else {
currentState = Positional; currentState = Positional;
@ -290,7 +290,8 @@ bool InputAqlItemRow::operator!=(InputAqlItemRow const& other) const noexcept {
return !(*this == other); return !(*this == other);
} }
bool InputAqlItemRow::equates(InputAqlItemRow const& other) const noexcept { bool InputAqlItemRow::equates(InputAqlItemRow const& other,
velocypack::Options const* const options) const noexcept {
if (!isInitialized() || !other.isInitialized()) { if (!isInitialized() || !other.isInitialized()) {
return isInitialized() == other.isInitialized(); return isInitialized() == other.isInitialized();
} }
@ -298,8 +299,9 @@ bool InputAqlItemRow::equates(InputAqlItemRow const& other) const noexcept {
if (getNrRegisters() != other.getNrRegisters()) { if (getNrRegisters() != other.getNrRegisters()) {
return false; return false;
} }
// NOLINTNEXTLINE(modernize-use-transparent-functors) auto const eq = [options](auto left, auto right) {
auto const eq = std::equal_to<AqlValue>{}; return 0 == AqlValue::Compare(options, left, right, false);
};
for (RegisterId i = 0; i < getNrRegisters(); ++i) { for (RegisterId i = 0; i < getNrRegisters(); ++i) {
if (!eq(getValue(i), other.getValue(i))) { if (!eq(getValue(i), other.getValue(i))) {
return false; return false;

View File

@ -33,11 +33,9 @@
#include <unordered_set> #include <unordered_set>
namespace arangodb { namespace arangodb {
namespace transaction {
class Methods;
}
namespace velocypack { namespace velocypack {
class Builder; class Builder;
struct Options;
} }
namespace aql { namespace aql {
@ -103,7 +101,8 @@ class InputAqlItemRow {
// blocks are equal, because comparing rows of blocks with different layouts // blocks are equal, because comparing rows of blocks with different layouts
// does not make sense. // does not make sense.
// Invalid rows are considered equivalent. // Invalid rows are considered equivalent.
bool equates(InputAqlItemRow const& other) const noexcept; [[nodiscard]] bool equates(InputAqlItemRow const& other,
velocypack::Options const* options) const noexcept;
bool isInitialized() const noexcept; bool isInitialized() const noexcept;
@ -132,7 +131,7 @@ class InputAqlItemRow {
/// @brief toVelocyPack, transfer a single AqlItemRow to Json, the result can /// @brief toVelocyPack, transfer a single AqlItemRow to Json, the result can
/// be used to recreate the AqlItemBlock via the Json constructor /// be used to recreate the AqlItemBlock via the Json constructor
/// Uses the same API as an AqlItemBlock with only a single row /// Uses the same API as an AqlItemBlock with only a single row
void toVelocyPack(transaction::Methods* trx, arangodb::velocypack::Builder&) const; void toVelocyPack(velocypack::Options const*, arangodb::velocypack::Builder&) const;
private: private:
AqlItemBlock& block() noexcept; AqlItemBlock& block() noexcept;

View File

@ -47,6 +47,7 @@
using namespace arangodb; using namespace arangodb;
using namespace arangodb::aql; using namespace arangodb::aql;
using namespace arangodb::basics; using namespace arangodb::basics;
namespace arangodb { namespace arangodb {
namespace aql { namespace aql {

View File

@ -25,6 +25,12 @@
#include "Aql/AqlItemBlock.h" #include "Aql/AqlItemBlock.h"
#include "Aql/DependencyProxy.h" #include "Aql/DependencyProxy.h"
#include "Aql/ShadowAqlItemRow.h" #include "Aql/ShadowAqlItemRow.h"
#include "Logger/LogMacros.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
#include <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>
using namespace arangodb; using namespace arangodb;
using namespace arangodb::aql; using namespace arangodb::aql;
@ -119,7 +125,8 @@ std::pair<ExecutionState, ShadowAqlItemRow> MultiDependencySingleRowFetcher::fet
} else { } else {
TRI_ASSERT(row.isInitialized()); TRI_ASSERT(row.isInitialized());
// All shadow rows must be equal! // All shadow rows must be equal!
TRI_ASSERT(row.equates(ShadowAqlItemRow{dep._currentBlock, dep._rowIndex})); auto const options = _dependencyProxy->velocypackOptions();
TRI_ASSERT(row.equates(ShadowAqlItemRow{dep._currentBlock, dep._rowIndex}, options));
} }
} }
} }

View File

@ -7319,6 +7319,8 @@ bool nodeMakesThisQueryLevelUnsuitableForSubquerySplicing(ExecutionNode const* c
case ExecutionNode::RETURN: case ExecutionNode::RETURN:
case ExecutionNode::DISTRIBUTE: case ExecutionNode::DISTRIBUTE:
case ExecutionNode::SCATTER: case ExecutionNode::SCATTER:
case ExecutionNode::GATHER:
case ExecutionNode::REMOTE:
case ExecutionNode::REMOTESINGLE: case ExecutionNode::REMOTESINGLE:
case ExecutionNode::MATERIALIZE: case ExecutionNode::MATERIALIZE:
case ExecutionNode::DISTRIBUTE_CONSUMER: case ExecutionNode::DISTRIBUTE_CONSUMER:
@ -7326,10 +7328,6 @@ bool nodeMakesThisQueryLevelUnsuitableForSubquerySplicing(ExecutionNode const* c
case ExecutionNode::SUBQUERY_END: case ExecutionNode::SUBQUERY_END:
// These nodes do not initiate a skip themselves, and thus are fine. // These nodes do not initiate a skip themselves, and thus are fine.
return false; return false;
// UnsortingGather currently does not work. Also, as we would possibly add
// them with remote nodes in the query, we exclude these here, too.
case ExecutionNode::REMOTE:
case ExecutionNode::GATHER:
case ExecutionNode::NORESULTS: case ExecutionNode::NORESULTS:
// no results currently cannot work, as they do not fetch from above. // no results currently cannot work, as they do not fetch from above.
case ExecutionNode::LIMIT: case ExecutionNode::LIMIT:
@ -7558,6 +7556,8 @@ void arangodb::aql::spliceSubqueriesRule(Optimizer* opt, std::unique_ptr<Executi
plan->createNode<RemoteNode>(plan.get(), plan->nextId(), plan->createNode<RemoteNode>(plan.get(), plan->nextId(),
&plan->getAst()->query()->vocbase(), &plan->getAst()->query()->vocbase(),
"", "", ""); "", "", "");
scatterNode->setIsInSplicedSubquery(true);
remoteNode->setIsInSplicedSubquery(true);
plan->insertAfter(start, scatterNode); plan->insertAfter(start, scatterNode);
plan->insertAfter(scatterNode, remoteNode); plan->insertAfter(scatterNode, remoteNode);

View File

@ -192,9 +192,9 @@ void OutputAqlItemRow::advanceRow() {
_numValuesWritten = 0; _numValuesWritten = 0;
} }
void OutputAqlItemRow::toVelocyPack(transaction::Methods& trx, VPackBuilder& builder) { void OutputAqlItemRow::toVelocyPack(velocypack::Options const* options, VPackBuilder& builder) {
TRI_ASSERT(produced()); TRI_ASSERT(produced());
block().rowToSimpleVPack(_baseIndex, &trx, builder); block().rowToSimpleVPack(_baseIndex, options, builder);
} }
SharedAqlItemBlockPtr OutputAqlItemRow::stealBlock() { SharedAqlItemBlockPtr OutputAqlItemRow::stealBlock() {

View File

@ -159,7 +159,7 @@ class OutputAqlItemRow {
// The data of this row will be copied. // The data of this row will be copied.
void decreaseShadowRowDepth(ShadowAqlItemRow const& sourceRow); void decreaseShadowRowDepth(ShadowAqlItemRow const& sourceRow);
void toVelocyPack(transaction::Methods& trx, velocypack::Builder& builder); void toVelocyPack(velocypack::Options const* options, velocypack::Builder& builder);
private: private:
[[nodiscard]] std::unordered_set<RegisterId> const& outputRegisters() const { [[nodiscard]] std::unordered_set<RegisterId> const& outputRegisters() const {

View File

@ -425,6 +425,7 @@ DistributeConsumerNode* QuerySnippet::createConsumerNode(ExecutionPlan* plan,
TRI_ASSERT(consumer != nullptr); TRI_ASSERT(consumer != nullptr);
// Hand over responsibility to plan, s.t. it can clean up if one of the below fails // Hand over responsibility to plan, s.t. it can clean up if one of the below fails
plan->registerNode(uniq_consumer.release()); plan->registerNode(uniq_consumer.release());
consumer->setIsInSplicedSubquery(internalScatter->isInSplicedSubquery());
consumer->addDependency(internalScatter); consumer->addDependency(internalScatter);
consumer->cloneRegisterPlan(internalScatter); consumer->cloneRegisterPlan(internalScatter);
internalScatter->addClient(consumer); internalScatter->addClient(consumer);

View File

@ -22,9 +22,11 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "RegexCache.h" #include "RegexCache.h"
#include "Aql/AqlValueMaterializer.h"
#include "Basics/StringUtils.h"
#include "Basics/Utf8Helper.h" #include "Basics/Utf8Helper.h"
#include <Basics/StringUtils.h> #include "Basics/tryEmplaceHelper.h"
#include <Basics/tryEmplaceHelper.h>
#include <velocypack/Collection.h> #include <velocypack/Collection.h>
#include <velocypack/Dumper.h> #include <velocypack/Dumper.h>

View File

@ -37,6 +37,8 @@
#include "Network/NetworkFeature.h" #include "Network/NetworkFeature.h"
#include "Network/Utils.h" #include "Network/Utils.h"
#include "Rest/CommonDefines.h" #include "Rest/CommonDefines.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
#include "VocBase/vocbase.h" #include "VocBase/vocbase.h"
#include <fuerte/connection.h> #include <fuerte/connection.h>
@ -304,7 +306,7 @@ std::pair<ExecutionState, Result> ExecutionBlockImpl<RemoteExecutor>::initialize
builder.add("pos", VPackValue(0)); builder.add("pos", VPackValue(0));
builder.add(VPackValue("items")); builder.add(VPackValue("items"));
builder.openObject(/*unindexed*/ true); builder.openObject(/*unindexed*/ true);
input.toVelocyPack(_engine->getQuery()->trx(), builder); input.toVelocyPack(_engine->getQuery()->trx()->transactionContextPtr()->getVPackOptions(), builder);
builder.close(); builder.close();
builder.close(); builder.close();

View File

@ -692,7 +692,8 @@ RestStatus RestAqlHandler::handleUseQuery(std::string const& operation,
// Backwards Compatibility // Backwards Compatibility
answerBuilder.add(StaticStrings::Error, VPackValue(false)); answerBuilder.add(StaticStrings::Error, VPackValue(false));
} else { } else {
items->toVelocyPack(_query->trx(), answerBuilder); items->toVelocyPack(_query->trx()->transactionContextPtr()->getVPackOptions(),
answerBuilder);
} }
} else if (operation == "skipSome") { } else if (operation == "skipSome") {
auto atMost = auto atMost =

View File

@ -22,6 +22,10 @@
#include "ShadowAqlItemRow.h" #include "ShadowAqlItemRow.h"
#include "Basics/VelocyPackHelper.h"
#include "Transaction/Methods.h"
#include "Transaction/Context.h"
using namespace arangodb; using namespace arangodb;
using namespace arangodb::aql; using namespace arangodb::aql;
@ -92,7 +96,8 @@ bool ShadowAqlItemRow::operator!=(ShadowAqlItemRow const& other) const noexcept
return !(*this == other); return !(*this == other);
} }
bool ShadowAqlItemRow::equates(ShadowAqlItemRow const& other) const noexcept { bool ShadowAqlItemRow::equates(ShadowAqlItemRow const& other,
velocypack::Options const* options) const noexcept {
if (!isInitialized() || !other.isInitialized()) { if (!isInitialized() || !other.isInitialized()) {
return isInitialized() == other.isInitialized(); return isInitialized() == other.isInitialized();
} }
@ -103,8 +108,9 @@ bool ShadowAqlItemRow::equates(ShadowAqlItemRow const& other) const noexcept {
if (getDepth() != other.getDepth()) { if (getDepth() != other.getDepth()) {
return false; return false;
} }
// NOLINTNEXTLINE(modernize-use-transparent-functors) auto const eq = [options](auto left, auto right) {
auto const eq = std::equal_to<AqlValue>{}; return 0 == AqlValue::Compare(options, left, right, false);
};
for (RegisterId i = 0; i < getNrRegisters(); ++i) { for (RegisterId i = 0; i < getNrRegisters(); ++i) {
if (!eq(getValue(i), other.getValue(i))) { if (!eq(getValue(i), other.getValue(i))) {
return false; return false;

View File

@ -28,6 +28,9 @@
#include <cstddef> #include <cstddef>
namespace arangodb { namespace arangodb {
namespace velocypack {
struct Options;
}
namespace aql { namespace aql {
struct CreateInvalidShadowRowHint { struct CreateInvalidShadowRowHint {
@ -113,7 +116,8 @@ class ShadowAqlItemRow {
// blocks are equal, because comparing rows of blocks with different layouts // blocks are equal, because comparing rows of blocks with different layouts
// does not make sense. // does not make sense.
// Invalid rows are considered equivalent. // Invalid rows are considered equivalent.
bool equates(ShadowAqlItemRow const& other) const noexcept; [[nodiscard]] bool equates(ShadowAqlItemRow const& other,
velocypack::Options const* option) const noexcept;
private: private:
AqlItemBlock& block() noexcept; AqlItemBlock& block() noexcept;

View File

@ -43,9 +43,9 @@ namespace {
/// @brief OurLessThan /// @brief OurLessThan
class OurLessThan { class OurLessThan {
public: public:
OurLessThan(arangodb::transaction::Methods* trx, AqlItemMatrix const& input, OurLessThan(velocypack::Options const* options, AqlItemMatrix const& input,
std::vector<SortRegister> const& sortRegisters) noexcept std::vector<SortRegister> const& sortRegisters) noexcept
: _trx(trx), _input(input), _sortRegisters(sortRegisters) {} : _vpackOptions(options), _input(input), _sortRegisters(sortRegisters) {}
bool operator()(AqlItemMatrix::RowIndex const& a, AqlItemMatrix::RowIndex const& b) const { bool operator()(AqlItemMatrix::RowIndex const& a, AqlItemMatrix::RowIndex const& b) const {
InputAqlItemRow left = _input.getRow(a); InputAqlItemRow left = _input.getRow(a);
@ -54,7 +54,7 @@ class OurLessThan {
AqlValue const& lhs = left.getValue(reg.reg); AqlValue const& lhs = left.getValue(reg.reg);
AqlValue const& rhs = right.getValue(reg.reg); AqlValue const& rhs = right.getValue(reg.reg);
int const cmp = AqlValue::Compare(_trx, lhs, rhs, true); int const cmp = AqlValue::Compare(_vpackOptions, lhs, rhs, true);
if (cmp < 0) { if (cmp < 0) {
return reg.asc; return reg.asc;
@ -67,7 +67,7 @@ class OurLessThan {
} }
private: private:
arangodb::transaction::Methods* _trx; velocypack::Options const* _vpackOptions;
AqlItemMatrix const& _input; AqlItemMatrix const& _input;
std::vector<SortRegister> const& _sortRegisters; std::vector<SortRegister> const& _sortRegisters;
}; // OurLessThan }; // OurLessThan
@ -90,27 +90,29 @@ SortExecutorInfos::SortExecutorInfos(
// cppcheck-suppress passedByValue // cppcheck-suppress passedByValue
std::unordered_set<RegisterId> registersToClear, std::unordered_set<RegisterId> registersToClear,
// cppcheck-suppress passedByValue // cppcheck-suppress passedByValue
std::unordered_set<RegisterId> registersToKeep, transaction::Methods* trx, bool stable) std::unordered_set<RegisterId> registersToKeep,
velocypack::Options const* options, bool stable)
: ExecutorInfos(mapSortRegistersToRegisterIds(sortRegisters), nullptr, : ExecutorInfos(mapSortRegistersToRegisterIds(sortRegisters), nullptr,
nrInputRegisters, nrOutputRegisters, nrInputRegisters, nrOutputRegisters,
std::move(registersToClear), std::move(registersToKeep)), std::move(registersToClear), std::move(registersToKeep)),
_limit(limit), _limit(limit),
_manager(manager), _manager(manager),
_trx(trx), _vpackOptions(options),
_sortRegisters(std::move(sortRegisters)), _sortRegisters(std::move(sortRegisters)),
_stable(stable) { _stable(stable) {
TRI_ASSERT(trx != nullptr);
TRI_ASSERT(!_sortRegisters.empty()); TRI_ASSERT(!_sortRegisters.empty());
} }
transaction::Methods* SortExecutorInfos::trx() const { return _trx; }
std::vector<SortRegister>& SortExecutorInfos::sortRegisters() { std::vector<SortRegister>& SortExecutorInfos::sortRegisters() {
return _sortRegisters; return _sortRegisters;
} }
bool SortExecutorInfos::stable() const { return _stable; } bool SortExecutorInfos::stable() const { return _stable; }
velocypack::Options const* SortExecutorInfos::vpackOptions() const noexcept {
return _vpackOptions;
}
SortExecutor::SortExecutor(Fetcher& fetcher, SortExecutorInfos& infos) SortExecutor::SortExecutor(Fetcher& fetcher, SortExecutorInfos& infos)
: _infos(infos), _fetcher(fetcher), _input(nullptr), _returnNext(0) {} : _infos(infos), _fetcher(fetcher), _input(nullptr), _returnNext(0) {}
SortExecutor::~SortExecutor() = default; SortExecutor::~SortExecutor() = default;
@ -160,7 +162,7 @@ void SortExecutor::doSorting() {
TRI_ASSERT(_input != nullptr); TRI_ASSERT(_input != nullptr);
_sortedIndexes = _input->produceRowIndexes(); _sortedIndexes = _input->produceRowIndexes();
// comparison function // comparison function
OurLessThan ourLessThan(_infos.trx(), *_input, _infos.sortRegisters()); OurLessThan ourLessThan(_infos.vpackOptions(), *_input, _infos.sortRegisters());
if (_infos.stable()) { if (_infos.stable()) {
std::stable_sort(_sortedIndexes.begin(), _sortedIndexes.end(), ourLessThan); std::stable_sort(_sortedIndexes.begin(), _sortedIndexes.end(), ourLessThan);
} else { } else {

View File

@ -54,14 +54,14 @@ class SortExecutorInfos : public ExecutorInfos {
AqlItemBlockManager& manager, RegisterId nrInputRegisters, AqlItemBlockManager& manager, RegisterId nrInputRegisters,
RegisterId nrOutputRegisters, std::unordered_set<RegisterId> registersToClear, RegisterId nrOutputRegisters, std::unordered_set<RegisterId> registersToClear,
std::unordered_set<RegisterId> registersToKeep, std::unordered_set<RegisterId> registersToKeep,
transaction::Methods* trx, bool stable); velocypack::Options const*, bool stable);
SortExecutorInfos() = delete; SortExecutorInfos() = delete;
SortExecutorInfos(SortExecutorInfos&&) = default; SortExecutorInfos(SortExecutorInfos&&) = default;
SortExecutorInfos(SortExecutorInfos const&) = delete; SortExecutorInfos(SortExecutorInfos const&) = delete;
~SortExecutorInfos() = default; ~SortExecutorInfos() = default;
arangodb::transaction::Methods* trx() const; [[nodiscard]] velocypack::Options const* vpackOptions() const noexcept;
std::vector<SortRegister>& sortRegisters(); std::vector<SortRegister>& sortRegisters();
@ -69,9 +69,7 @@ class SortExecutorInfos : public ExecutorInfos {
std::size_t _limit; std::size_t _limit;
AqlItemBlockManager& _manager; AqlItemBlockManager& _manager;
velocypack::Options const* _vpackOptions;
private:
arangodb::transaction::Methods* _trx;
std::vector<SortRegister> _sortRegisters; std::vector<SortRegister> _sortRegisters;
bool _stable; bool _stable;
}; };

View File

@ -37,6 +37,8 @@
#include "Aql/WalkerWorker.h" #include "Aql/WalkerWorker.h"
#include "Basics/StringBuffer.h" #include "Basics/StringBuffer.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
namespace { namespace {
std::string const ConstrainedHeap = "constrained-heap"; std::string const ConstrainedHeap = "constrained-heap";
@ -237,8 +239,10 @@ std::unique_ptr<ExecutionBlock> SortNode::createBlock(
} }
SortExecutorInfos infos(std::move(sortRegs), _limit, engine.itemBlockManager(), SortExecutorInfos infos(std::move(sortRegs), _limit, engine.itemBlockManager(),
getRegisterPlan()->nrRegs[previousNode->getDepth()], getRegisterPlan()->nrRegs[previousNode->getDepth()],
getRegisterPlan()->nrRegs[getDepth()], getRegsToClear(), getRegisterPlan()->nrRegs[getDepth()],
calcRegsToKeep(), engine.getQuery()->trx(), _stable); getRegsToClear(), calcRegsToKeep(),
engine.getQuery()->trx()->transactionContextPtr()->getVPackOptions(),
_stable);
if (sorterType() == SorterType::Standard) { if (sorterType() == SorterType::Standard) {
return std::make_unique<ExecutionBlockImpl<SortExecutor>>(&engine, this, return std::make_unique<ExecutionBlockImpl<SortExecutor>>(&engine, this,
std::move(infos)); std::move(infos));

View File

@ -30,6 +30,8 @@
#include "Aql/RegisterPlan.h" #include "Aql/RegisterPlan.h"
#include "Aql/SubqueryEndExecutor.h" #include "Aql/SubqueryEndExecutor.h"
#include "Meta/static_assert_size.h" #include "Meta/static_assert_size.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h"
#include <velocypack/Iterator.h> #include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
@ -85,10 +87,11 @@ std::unique_ptr<ExecutionBlock> SubqueryEndNode::createBlock(
auto outReg = variableToRegisterId(_outVariable); auto outReg = variableToRegisterId(_outVariable);
outputRegisters->emplace(outReg); outputRegisters->emplace(outReg);
auto const vpackOptions = trx->transactionContextPtr()->getVPackOptions();
SubqueryEndExecutorInfos infos(inputRegisters, outputRegisters, SubqueryEndExecutorInfos infos(inputRegisters, outputRegisters,
getRegisterPlan()->nrRegs[previousNode->getDepth()], getRegisterPlan()->nrRegs[previousNode->getDepth()],
getRegisterPlan()->nrRegs[getDepth()], getRegisterPlan()->nrRegs[getDepth()], getRegsToClear(),
getRegsToClear(), calcRegsToKeep(), trx, inReg, outReg); calcRegsToKeep(), vpackOptions, inReg, outReg);
return std::make_unique<ExecutionBlockImpl<SubqueryEndExecutor>>(&engine, this, return std::make_unique<ExecutionBlockImpl<SubqueryEndExecutor>>(&engine, this,
std::move(infos)); std::move(infos));

View File

@ -45,10 +45,10 @@ SubqueryEndExecutorInfos::SubqueryEndExecutorInfos(
RegisterId nrInputRegisters, RegisterId nrOutputRegisters, RegisterId nrInputRegisters, RegisterId nrOutputRegisters,
std::unordered_set<RegisterId> const& registersToClear, std::unordered_set<RegisterId> const& registersToClear,
std::unordered_set<RegisterId> registersToKeep, std::unordered_set<RegisterId> registersToKeep,
transaction::Methods* trxPtr, RegisterId inReg, RegisterId outReg) velocypack::Options const* const options, RegisterId inReg, RegisterId outReg)
: ExecutorInfos(std::move(readableInputRegisters), std::move(writeableOutputRegisters), nrInputRegisters, : ExecutorInfos(std::move(readableInputRegisters), std::move(writeableOutputRegisters), nrInputRegisters,
nrOutputRegisters, registersToClear, std::move(registersToKeep)), nrOutputRegisters, registersToClear, std::move(registersToKeep)),
_trxPtr(trxPtr), _vpackOptions(options),
_outReg(outReg), _outReg(outReg),
_inReg(inReg) {} _inReg(inReg) {}
@ -60,6 +60,10 @@ bool SubqueryEndExecutorInfos::usesInputRegister() const {
return _inReg != RegisterPlan::MaxRegisterId; return _inReg != RegisterPlan::MaxRegisterId;
} }
velocypack::Options const* SubqueryEndExecutorInfos::vpackOptions() const noexcept {
return _vpackOptions;
}
SubqueryEndExecutor::SubqueryEndExecutor(Fetcher& fetcher, SubqueryEndExecutorInfos& infos) SubqueryEndExecutor::SubqueryEndExecutor(Fetcher& fetcher, SubqueryEndExecutorInfos& infos)
: _fetcher(fetcher), _infos(infos), _accumulator(nullptr), _state(ACCUMULATE) { : _fetcher(fetcher), _infos(infos), _accumulator(nullptr), _state(ACCUMULATE) {
resetAccumulator(); resetAccumulator();
@ -87,7 +91,7 @@ std::pair<ExecutionState, NoStats> SubqueryEndExecutor::produceRows(OutputAqlIte
if (inputRow.isInitialized() && _infos.usesInputRegister()) { if (inputRow.isInitialized() && _infos.usesInputRegister()) {
TRI_ASSERT(_accumulator->isOpenArray()); TRI_ASSERT(_accumulator->isOpenArray());
AqlValue value = inputRow.getValue(_infos.getInputRegister()); AqlValue value = inputRow.getValue(_infos.getInputRegister());
value.toVelocyPack(_infos.getTrxPtr(), *_accumulator, false); value.toVelocyPack(_infos.vpackOptions(), *_accumulator, false);
} }
// We have received DONE on data rows, so now // We have received DONE on data rows, so now

View File

@ -48,7 +48,7 @@ class SubqueryEndExecutorInfos : public ExecutorInfos {
RegisterId nrInputRegisters, RegisterId nrOutputRegisters, RegisterId nrInputRegisters, RegisterId nrOutputRegisters,
std::unordered_set<RegisterId> const& registersToClear, std::unordered_set<RegisterId> const& registersToClear,
std::unordered_set<RegisterId> registersToKeep, std::unordered_set<RegisterId> registersToKeep,
transaction::Methods* trxPtr, RegisterId inReg, velocypack::Options const* options, RegisterId inReg,
RegisterId outReg); RegisterId outReg);
SubqueryEndExecutorInfos() = delete; SubqueryEndExecutorInfos() = delete;
@ -56,13 +56,13 @@ class SubqueryEndExecutorInfos : public ExecutorInfos {
SubqueryEndExecutorInfos(SubqueryEndExecutorInfos const&) = delete; SubqueryEndExecutorInfos(SubqueryEndExecutorInfos const&) = delete;
~SubqueryEndExecutorInfos(); ~SubqueryEndExecutorInfos();
transaction::Methods* getTrxPtr() const noexcept { return _trxPtr; } [[nodiscard]] velocypack::Options const* vpackOptions() const noexcept;
inline RegisterId getOutputRegister() const { return _outReg; } inline RegisterId getOutputRegister() const { return _outReg; }
bool usesInputRegister() const; bool usesInputRegister() const;
inline RegisterId getInputRegister() const { return _inReg; } inline RegisterId getInputRegister() const { return _inReg; }
private: private:
transaction::Methods* _trxPtr; velocypack::Options const* _vpackOptions;
RegisterId const _outReg; RegisterId const _outReg;
RegisterId const _inReg; RegisterId const _inReg;
}; };

View File

@ -0,0 +1,130 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#include "UnsortingGatherExecutor.h"
#include <Logger/LogMacros.h>
#include "Aql/IdExecutor.h" // for IdExecutorInfos
#include "Aql/MultiDependencySingleRowFetcher.h"
#include "Aql/OutputAqlItemRow.h"
#include "Aql/Stats.h"
#include "Basics/Exceptions.h"
#include "Basics/debugging.h"
#include "Basics/voc-errors.h"
using namespace arangodb;
using namespace arangodb::aql;
UnsortingGatherExecutor::UnsortingGatherExecutor(Fetcher& fetcher, Infos& infos)
: _fetcher(fetcher) {}
UnsortingGatherExecutor::~UnsortingGatherExecutor() = default;
auto UnsortingGatherExecutor::produceRows(OutputAqlItemRow& output)
-> std::pair<ExecutionState, Stats> {
while (!output.isFull() && !done()) {
// Note that fetchNextRow may return DONE (because the current dependency is
// DONE), and also return an unitialized row in that case, but we are not
// DONE completely - that's what `done()` is for.
auto [state, inputRow] = fetchNextRow(output.numRowsLeft());
if (state == ExecutionState::WAITING) {
return {state, {}};
}
// HASMORE => inputRow.isInitialized()
TRI_ASSERT(state == ExecutionState::DONE || inputRow.isInitialized());
if (inputRow.isInitialized()) {
output.copyRow(inputRow);
TRI_ASSERT(output.produced());
output.advanceRow();
}
}
auto state = done() ? ExecutionState::DONE : ExecutionState::HASMORE;
return {state, {}};
}
auto UnsortingGatherExecutor::fetcher() const noexcept -> const Fetcher& {
return _fetcher;
}
auto UnsortingGatherExecutor::fetcher() noexcept -> Fetcher& {
return _fetcher;
}
auto UnsortingGatherExecutor::numDependencies() const
noexcept(noexcept(_fetcher.numberDependencies())) -> size_t {
return _fetcher.numberDependencies();
}
auto UnsortingGatherExecutor::fetchNextRow(size_t atMost)
-> std::pair<ExecutionState, InputAqlItemRow> {
auto res = fetcher().fetchRowForDependency(currentDependency(), atMost);
if (res.first == ExecutionState::DONE) {
advanceDependency();
}
return res;
}
auto UnsortingGatherExecutor::skipNextRows(size_t atMost)
-> std::pair<ExecutionState, size_t> {
auto res = fetcher().skipRowsForDependency(currentDependency(), atMost);
if (res.first == ExecutionState::DONE) {
advanceDependency();
}
return res;
}
auto UnsortingGatherExecutor::done() const noexcept -> bool {
return _currentDependency >= numDependencies();
}
auto UnsortingGatherExecutor::currentDependency() const noexcept -> size_t {
return _currentDependency;
}
auto UnsortingGatherExecutor::advanceDependency() noexcept -> void {
TRI_ASSERT(_currentDependency < numDependencies());
++_currentDependency;
}
auto UnsortingGatherExecutor::skipRows(size_t const atMost)
-> std::tuple<ExecutionState, UnsortingGatherExecutor::Stats, size_t> {
auto const rowsLeftToSkip = [&atMost, &skipped = this->_skipped]() {
TRI_ASSERT(atMost >= skipped);
return atMost - skipped;
};
while (rowsLeftToSkip() > 0 && !done()) {
// Note that skipNextRow may return DONE (because the current dependency is
// DONE), and also return an unitialized row in that case, but we are not
// DONE completely - that's what `done()` is for.
auto [state, skipped] = skipNextRows(rowsLeftToSkip());
_skipped += skipped;
if (state == ExecutionState::WAITING) {
return {state, {}, 0};
}
}
auto state = done() ? ExecutionState::DONE : ExecutionState::HASMORE;
auto skipped = size_t{0};
std::swap(skipped, _skipped);
return {state, {}, skipped};
}

View File

@ -0,0 +1,98 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_UNSORTINGGATHEREXECUTOR_H
#define ARANGOD_AQL_UNSORTINGGATHEREXECUTOR_H
#include "Aql/ExecutionState.h"
#include "Aql/ExecutorInfos.h"
#include "Aql/MultiDependencySingleRowFetcher.h"
#include "Aql/types.h"
#include <string>
#include <unordered_set>
namespace arangodb::aql {
class NoStats;
class InputAqlItemRow;
class OutputAqlItemRow;
class IdExecutorInfos;
class SharedAqlItemBlockPtr;
/**
* @brief Produces all rows from its dependencies, which may be more than one,
* in some unspecified order. It is, purposefully, strictly synchronous, and
* always waits for an answer before requesting the next row(s).
*
* The actual implementation fetches all available rows from the first
* dependency, then from the second, and so forth. But that is not guaranteed.
*/
class UnsortingGatherExecutor {
public:
struct Properties {
static constexpr bool preservesOrder = false;
static constexpr BlockPassthrough allowsBlockPassthrough = BlockPassthrough::Disable;
// TODO I think we can set this to true (but needs to implement
// hasExpectedNumberOfRows for that)
static constexpr bool inputSizeRestrictsOutputSize = false;
};
using Fetcher = MultiDependencySingleRowFetcher;
// TODO I should probably implement custom Infos, we don't need distributeId().
using Infos = IdExecutorInfos;
using Stats = NoStats;
UnsortingGatherExecutor(Fetcher& fetcher, Infos&);
~UnsortingGatherExecutor();
/**
* @brief produce the next Row of Aql Values.
*
* @return ExecutionState,
* if something was written output.hasValue() == true
*/
[[nodiscard]] auto produceRows(OutputAqlItemRow& output)
-> std::pair<ExecutionState, Stats>;
[[nodiscard]] auto skipRows(size_t atMost) -> std::tuple<ExecutionState, NoStats, size_t>;
private:
[[nodiscard]] auto numDependencies() const
noexcept(noexcept(static_cast<Fetcher*>(nullptr)->numberDependencies())) -> size_t;
[[nodiscard]] auto fetcher() const noexcept -> Fetcher const&;
[[nodiscard]] auto fetcher() noexcept -> Fetcher&;
[[nodiscard]] auto done() const noexcept -> bool;
[[nodiscard]] auto currentDependency() const noexcept -> size_t;
[[nodiscard]] auto fetchNextRow(size_t atMost)
-> std::pair<ExecutionState, InputAqlItemRow>;
[[nodiscard]] auto skipNextRows(size_t atMost) -> std::pair<ExecutionState, size_t>;
auto advanceDependency() noexcept -> void;
private:
Fetcher& _fetcher;
size_t _currentDependency{0};
size_t _skipped{0};
};
} // namespace arangodb::aql
#endif // ARANGOD_AQL_UNSORTINGGATHEREXECUTOR_H

View File

@ -223,6 +223,7 @@ set(LIB_ARANGO_AQL_SOURCES
Aql/AqlTransaction.cpp Aql/AqlTransaction.cpp
Aql/AqlValue.cpp Aql/AqlValue.cpp
Aql/AqlValueGroup.cpp Aql/AqlValueGroup.cpp
Aql/AqlValueMaterializer.cpp
Aql/Arithmetic.cpp Aql/Arithmetic.cpp
Aql/Ast.cpp Aql/Ast.cpp
Aql/AstHelper.cpp Aql/AstHelper.cpp
@ -349,6 +350,7 @@ set(LIB_ARANGO_AQL_SOURCES
Aql/TraversalExecutor.cpp Aql/TraversalExecutor.cpp
Aql/TraversalNode.cpp Aql/TraversalNode.cpp
Aql/UnsortedGatherExecutor.cpp Aql/UnsortedGatherExecutor.cpp
Aql/UnsortingGatherExecutor.cpp
Aql/UpdateReplaceModifier.cpp Aql/UpdateReplaceModifier.cpp
Aql/UpsertModifier.cpp Aql/UpsertModifier.cpp
Aql/V8Executor.cpp Aql/V8Executor.cpp

View File

@ -326,8 +326,10 @@ class ScopedAqlValue : private irs::util::noncopyable {
} }
void toVelocyPack(velocypack::Builder& builder) const { void toVelocyPack(velocypack::Builder& builder) const {
_node->isConstant() ? _node->toVelocyPackValue(builder) _node->isConstant()
: _value.toVelocyPack(nullptr, builder, false); ? _node->toVelocyPackValue(builder)
: _value.toVelocyPack(static_cast<velocypack::Options const*>(nullptr),
builder, false);
} }
private: private:

View File

@ -47,6 +47,7 @@ class AqlItemRowsTest : public ::testing::Test {
protected: protected:
ResourceMonitor monitor; ResourceMonitor monitor;
AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS};
velocypack::Options const* const options{&velocypack::Options::Defaults};
void AssertResultMatrix(AqlItemBlock* in, VPackSlice result, void AssertResultMatrix(AqlItemBlock* in, VPackSlice result,
std::unordered_set<RegisterId> const& regsToKeep, std::unordered_set<RegisterId> const& regsToKeep,
@ -320,6 +321,7 @@ class AqlItemRowsCommonEqTest : public ::testing::Test {
protected: protected:
ResourceMonitor monitor; ResourceMonitor monitor;
AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS};
velocypack::Options const* const options{&velocypack::Options::Defaults};
}; };
using RowTypes = ::testing::Types<InputAqlItemRow, ShadowAqlItemRow>; using RowTypes = ::testing::Types<InputAqlItemRow, ShadowAqlItemRow>;
@ -404,6 +406,7 @@ TYPED_TEST(AqlItemRowsCommonEqTest, row_eq_operators) {
TYPED_TEST(AqlItemRowsCommonEqTest, row_equivalence) { TYPED_TEST(AqlItemRowsCommonEqTest, row_equivalence) {
using RowType = TypeParam; using RowType = TypeParam;
auto const options = this->options;
SharedAqlItemBlockPtr block = SharedAqlItemBlockPtr block =
buildBlock<1>(this->itemBlockManager, {{{0}}, {{1}}}); buildBlock<1>(this->itemBlockManager, {{{0}}, {{1}}});
SharedAqlItemBlockPtr otherBlock = SharedAqlItemBlockPtr otherBlock =
@ -418,33 +421,34 @@ TYPED_TEST(AqlItemRowsCommonEqTest, row_equivalence) {
RowType const otherInvalidRow = createInvalidRow<RowType>(); RowType const otherInvalidRow = createInvalidRow<RowType>();
// same rows must be considered equivalent // same rows must be considered equivalent
EXPECT_TRUE((RowType{block, 0}.equates(RowType{block, 0}))); EXPECT_TRUE((RowType{block, 0}.equates(RowType{block, 0}, options)));
EXPECT_TRUE((RowType{block, 1}.equates(RowType{block, 1}))); EXPECT_TRUE((RowType{block, 1}.equates(RowType{block, 1}, options)));
// different rows must be non-equivalent // different rows must be non-equivalent
EXPECT_FALSE((RowType{block, 0}.equates(RowType{block, 1}))); EXPECT_FALSE((RowType{block, 0}.equates(RowType{block, 1}, options)));
EXPECT_FALSE((RowType{block, 1}.equates(RowType{block, 0}))); EXPECT_FALSE((RowType{block, 1}.equates(RowType{block, 0}, options)));
// different row in different block must be non-equivalent, even with the same index // different row in different block must be non-equivalent, even with the same index
EXPECT_FALSE((RowType{block, 0}.equates(RowType{otherBlock, 0}))); EXPECT_FALSE((RowType{block, 0}.equates(RowType{otherBlock, 0}, options)));
EXPECT_FALSE((RowType{otherBlock, 0}.equates(RowType{block, 0}))); EXPECT_FALSE((RowType{otherBlock, 0}.equates(RowType{block, 0}, options)));
// an equivalent row in a different block must be considered equivalent, even with a different index // an equivalent row in a different block must be considered equivalent, even with a different index
EXPECT_TRUE((RowType{block, 1}.equates(RowType{otherBlock, 0}))); EXPECT_TRUE((RowType{block, 1}.equates(RowType{otherBlock, 0}, options)));
EXPECT_TRUE((RowType{otherBlock, 0}.equates(RowType{block, 1}))); EXPECT_TRUE((RowType{otherBlock, 0}.equates(RowType{block, 1}, options)));
// comparisons with an invalid row must be false // comparisons with an invalid row must be false
EXPECT_FALSE((RowType{block, 0}.equates(invalidRow))); EXPECT_FALSE((RowType{block, 0}.equates(invalidRow, options)));
EXPECT_FALSE((invalidRow.equates(RowType{block, 0}))); EXPECT_FALSE((invalidRow.equates(RowType{block, 0}, options)));
// two invalid rows must be equal // two invalid rows must be equal
EXPECT_TRUE((invalidRow.equates(otherInvalidRow))); EXPECT_TRUE((invalidRow.equates(otherInvalidRow, options)));
} }
class AqlShadowRowsEqTest : public ::testing::Test { class AqlShadowRowsEqTest : public ::testing::Test {
protected: protected:
ResourceMonitor monitor; ResourceMonitor monitor;
AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS}; AqlItemBlockManager itemBlockManager{&monitor, SerializationFormat::SHADOWROWS};
velocypack::Options const* const options{&velocypack::Options::Defaults};
}; };
TEST_F(AqlShadowRowsEqTest, shadow_row_depth_equivalence) { TEST_F(AqlShadowRowsEqTest, shadow_row_depth_equivalence) {
@ -460,20 +464,20 @@ TEST_F(AqlShadowRowsEqTest, shadow_row_depth_equivalence) {
otherBlock->setShadowRowDepth(0, AqlValue{AqlValueHintUInt{1}}); otherBlock->setShadowRowDepth(0, AqlValue{AqlValueHintUInt{1}});
// same rows must be considered equivalent // same rows must be considered equivalent
EXPECT_TRUE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{block, 0}))); EXPECT_TRUE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{block, 0}, options)));
EXPECT_TRUE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{block, 1}))); EXPECT_TRUE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{block, 1}, options)));
// different rows must be non-equivalent // different rows must be non-equivalent
EXPECT_FALSE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{block, 1}))); EXPECT_FALSE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{block, 1}, options)));
EXPECT_FALSE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{block, 0}))); EXPECT_FALSE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{block, 0}, options)));
// different row in different block must be non-equivalent, even with the same index // different row in different block must be non-equivalent, even with the same index
EXPECT_FALSE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{otherBlock, 0}))); EXPECT_FALSE((ShadowAqlItemRow{block, 0}.equates(ShadowAqlItemRow{otherBlock, 0}, options)));
EXPECT_FALSE((ShadowAqlItemRow{otherBlock, 0}.equates(ShadowAqlItemRow{block, 0}))); EXPECT_FALSE((ShadowAqlItemRow{otherBlock, 0}.equates(ShadowAqlItemRow{block, 0}, options)));
// an equivalent row in a different block must be considered equivalent, even with a different index // an equivalent row in a different block must be considered equivalent, even with a different index
EXPECT_TRUE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{otherBlock, 0}))); EXPECT_TRUE((ShadowAqlItemRow{block, 1}.equates(ShadowAqlItemRow{otherBlock, 0}, options)));
EXPECT_TRUE((ShadowAqlItemRow{otherBlock, 0}.equates(ShadowAqlItemRow{block, 1}))); EXPECT_TRUE((ShadowAqlItemRow{otherBlock, 0}.equates(ShadowAqlItemRow{block, 1}, options)));
} }
} // namespace aql } // namespace aql

View File

@ -24,6 +24,8 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include <velocypack/Options.h>
namespace arangodb { namespace arangodb {
namespace tests { namespace tests {
namespace aql { namespace aql {
@ -42,7 +44,7 @@ DependencyProxyMock<passBlocksThrough>::DependencyProxyMock(arangodb::aql::Resou
::arangodb::aql::RegisterId nrRegisters) ::arangodb::aql::RegisterId nrRegisters)
: DependencyProxy<passBlocksThrough>({}, _itemBlockManager, : DependencyProxy<passBlocksThrough>({}, _itemBlockManager,
std::shared_ptr<std::unordered_set<RegisterId>>(), std::shared_ptr<std::unordered_set<RegisterId>>(),
nrRegisters), nrRegisters, &velocypack::Options::Defaults),
_itemsToReturn(), _itemsToReturn(),
_numFetchBlockCalls(0), _numFetchBlockCalls(0),
_monitor(monitor), _monitor(monitor),
@ -174,7 +176,7 @@ MultiDependencyProxyMock<passBlocksThrough>::MultiDependencyProxyMock(
::arangodb::aql::RegisterId nrRegisters, size_t nrDeps) ::arangodb::aql::RegisterId nrRegisters, size_t nrDeps)
: DependencyProxy<passBlocksThrough>({}, _itemBlockManager, : DependencyProxy<passBlocksThrough>({}, _itemBlockManager,
std::shared_ptr<std::unordered_set<RegisterId>>(), std::shared_ptr<std::unordered_set<RegisterId>>(),
nrRegisters), nrRegisters, &velocypack::Options::Defaults),
_itemBlockManager(&monitor, SerializationFormat::SHADOWROWS) { _itemBlockManager(&monitor, SerializationFormat::SHADOWROWS) {
_dependencyMocks.reserve(nrDeps); _dependencyMocks.reserve(nrDeps);
for (size_t i = 0; i < nrDeps; ++i) { for (size_t i = 0; i < nrDeps; ++i) {

View File

@ -36,6 +36,7 @@
#include "Aql/ExecutionEngine.h" #include "Aql/ExecutionEngine.h"
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Aql/SingleRowFetcher.h" #include "Aql/SingleRowFetcher.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h" #include "Transaction/Methods.h"
using namespace arangodb; using namespace arangodb;
@ -65,6 +66,10 @@ class ExecutionBlockImplTest : public ::testing::Test {
fakeit::Mock<transaction::Methods> mockTrx; fakeit::Mock<transaction::Methods> mockTrx;
transaction::Methods& trx; transaction::Methods& trx;
// Mock of the transaction context
fakeit::Mock<transaction::Context> mockContext;
transaction::Context& context;
// Mock of the Query // Mock of the Query
fakeit::Mock<Query> mockQuery; fakeit::Mock<Query> mockQuery;
Query& query; Query& query;
@ -90,6 +95,7 @@ class ExecutionBlockImplTest : public ::testing::Test {
: engine(mockEngine.get()), : engine(mockEngine.get()),
itemBlockManager(mockBlockManager.get()), itemBlockManager(mockBlockManager.get()),
trx(mockTrx.get()), trx(mockTrx.get()),
context(mockContext.get()),
query(mockQuery.get()), query(mockQuery.get()),
lqueryOptions(mockQueryOptions.get()), lqueryOptions(mockQueryOptions.get()),
profile(ProfileLevel(PROFILE_LEVEL_NONE)), profile(ProfileLevel(PROFILE_LEVEL_NONE)),
@ -116,6 +122,9 @@ class ExecutionBlockImplTest : public ::testing::Test {
fakeit::When(Method(mockQuery, trx)).AlwaysReturn(&trx); fakeit::When(Method(mockQuery, trx)).AlwaysReturn(&trx);
fakeit::When(Method(mockQueryOptions, getProfileLevel)).AlwaysReturn(profile); fakeit::When(Method(mockQueryOptions, getProfileLevel)).AlwaysReturn(profile);
fakeit::When(Method(mockTrx, transactionContextPtr)).AlwaysReturn(&context);
fakeit::When(Method(mockContext, getVPackOptions)).AlwaysReturn(&velocypack::Options::Defaults);
} }
}; };

View File

@ -35,6 +35,7 @@ using namespace arangodb::aql;
using namespace arangodb::tests; using namespace arangodb::tests;
using namespace arangodb::tests::aql; using namespace arangodb::tests::aql;
constexpr auto options = &velocypack::Options::Defaults;
void arangodb::tests::aql::runFetcher(arangodb::aql::MultiDependencySingleRowFetcher& testee, void arangodb::tests::aql::runFetcher(arangodb::aql::MultiDependencySingleRowFetcher& testee,
std::vector<FetcherIOPair> const& inputOutputPairs) { std::vector<FetcherIOPair> const& inputOutputPairs) {
@ -58,7 +59,9 @@ void arangodb::tests::aql::runFetcher(arangodb::aql::MultiDependencySingleRowFet
auto const& actualState = actual.first; auto const& actualState = actual.first;
auto const& actualRow = actual.second; auto const& actualRow = actual.second;
EXPECT_EQ(expectedState, actualState) << "during step " << i; EXPECT_EQ(expectedState, actualState) << "during step " << i;
EXPECT_TRUE(expectedRow.equates(actualRow)) << " expected: " << expectedRow << "\n actual: " << actualRow << "\n during step " << i; EXPECT_TRUE(expectedRow.equates(actualRow, options))
<< " expected: " << expectedRow << "\n actual: " << actualRow
<< "\n during step " << i;
} }
void operator()(ConcreteFetcherIOPair<SkipRowsForDependency> const& iop) { void operator()(ConcreteFetcherIOPair<SkipRowsForDependency> const& iop) {
auto const& args = iop.first; auto const& args = iop.first;
@ -75,7 +78,9 @@ void arangodb::tests::aql::runFetcher(arangodb::aql::MultiDependencySingleRowFet
auto const& actualState = actual.first; auto const& actualState = actual.first;
auto const& actualRow = actual.second; auto const& actualRow = actual.second;
EXPECT_EQ(expectedState, actualState) << "during step " << i; EXPECT_EQ(expectedState, actualState) << "during step " << i;
EXPECT_TRUE(expectedRow.equates(actualRow)) << " expected: " << expectedRow << "\n actual: " << actualRow << "\n during step " << i; EXPECT_TRUE(expectedRow.equates(actualRow, options))
<< " expected: " << expectedRow << "\n actual: " << actualRow
<< "\n during step " << i;
} }
private: private:

View File

@ -54,12 +54,6 @@ namespace arangodb {
namespace tests { namespace tests {
namespace aql { namespace aql {
int compareAqlValues(irs::sort::prepared const*, arangodb::transaction::Methods* trx,
arangodb::aql::AqlValue const& lhs,
arangodb::aql::AqlValue const& rhs) {
return arangodb::aql::AqlValue::Compare(trx, lhs, rhs, true);
}
class SortExecutorTest : public ::testing::Test { class SortExecutorTest : public ::testing::Test {
protected: protected:
ExecutionState state; ExecutionState state;
@ -67,14 +61,7 @@ class SortExecutorTest : public ::testing::Test {
AqlItemBlockManager itemBlockManager; AqlItemBlockManager itemBlockManager;
SharedAqlItemBlockPtr block; SharedAqlItemBlockPtr block;
// Mock of the Transaction velocypack::Options const* vpackOptions{&velocypack::Options::Defaults};
// Enough for this test, will only be passed through and accessed
// on documents alone.
fakeit::Mock<transaction::Methods> mockTrx;
transaction::Methods& trx;
fakeit::Mock<transaction::Context> mockContext;
transaction::Context& ctxt;
Variable sortVar; Variable sortVar;
SortElement sl; SortElement sl;
@ -84,13 +71,9 @@ class SortExecutorTest : public ::testing::Test {
SortExecutorTest() SortExecutorTest()
: itemBlockManager(&monitor, SerializationFormat::SHADOWROWS), : itemBlockManager(&monitor, SerializationFormat::SHADOWROWS),
block(new AqlItemBlock(itemBlockManager, 1000, 1)), block(new AqlItemBlock(itemBlockManager, 1000, 1)),
trx(mockTrx.get()),
ctxt(mockContext.get()),
sortVar("mySortVar", 0), sortVar("mySortVar", 0),
sl(&sortVar, true), sl(&sortVar, true),
sortReg(0, sl) { sortReg(0, sl) {
fakeit::When(Method(mockTrx, transactionContextPtr)).AlwaysReturn(&ctxt);
fakeit::When(Method(mockContext, getVPackOptions)).AlwaysReturn(&arangodb::velocypack::Options::Defaults);
sortRegisters.emplace_back(std::move(sortReg)); sortRegisters.emplace_back(std::move(sortReg));
} }
}; };
@ -98,7 +81,7 @@ class SortExecutorTest : public ::testing::Test {
TEST_F(SortExecutorTest, no_rows_upstream_producer_doesnt_wait) { TEST_F(SortExecutorTest, no_rows_upstream_producer_doesnt_wait) {
SortExecutorInfos infos(std::move(sortRegisters), SortExecutorInfos infos(std::move(sortRegisters),
/*limit (ignored for default sort)*/ 0, /*limit (ignored for default sort)*/ 0,
itemBlockManager, 1, 1, {}, {0}, &trx, false); itemBlockManager, 1, 1, {}, {0}, vpackOptions, false);
VPackBuilder input; VPackBuilder input;
AllRowsFetcherHelper fetcher(input.steal(), false); AllRowsFetcherHelper fetcher(input.steal(), false);
SortExecutor testee(fetcher, infos); SortExecutor testee(fetcher, infos);
@ -117,7 +100,7 @@ TEST_F(SortExecutorTest, no_rows_upstream_producer_doesnt_wait) {
TEST_F(SortExecutorTest, no_rows_upstream_producer_waits) { TEST_F(SortExecutorTest, no_rows_upstream_producer_waits) {
SortExecutorInfos infos(std::move(sortRegisters), SortExecutorInfos infos(std::move(sortRegisters),
/*limit (ignored for default sort)*/ 0, /*limit (ignored for default sort)*/ 0,
itemBlockManager, 1, 1, {}, {0}, &trx, false); itemBlockManager, 1, 1, {}, {0}, vpackOptions, false);
VPackBuilder input; VPackBuilder input;
AllRowsFetcherHelper fetcher(input.steal(), true); AllRowsFetcherHelper fetcher(input.steal(), true);
SortExecutor testee(fetcher, infos); SortExecutor testee(fetcher, infos);
@ -141,7 +124,7 @@ TEST_F(SortExecutorTest, no_rows_upstream_producer_waits) {
TEST_F(SortExecutorTest, rows_upstream_we_are_waiting_for_list_of_numbers) { TEST_F(SortExecutorTest, rows_upstream_we_are_waiting_for_list_of_numbers) {
SortExecutorInfos infos(std::move(sortRegisters), SortExecutorInfos infos(std::move(sortRegisters),
/*limit (ignored for default sort)*/ 0, /*limit (ignored for default sort)*/ 0,
itemBlockManager, 1, 1, {}, {0}, &trx, false); itemBlockManager, 1, 1, {}, {0}, vpackOptions, false);
std::shared_ptr<VPackBuilder> input = std::shared_ptr<VPackBuilder> input =
VPackParser::fromJson("[[5],[3],[1],[2],[4]]"); VPackParser::fromJson("[[5],[3],[1],[2],[4]]");
AllRowsFetcherHelper fetcher(input->steal(), true); AllRowsFetcherHelper fetcher(input->steal(), true);

View File

@ -34,6 +34,7 @@
#include "Aql/Functions.h" #include "Aql/Functions.h"
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Containers/SmallVector.h" #include "Containers/SmallVector.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h" #include "Transaction/Methods.h"
#include <velocypack/Builder.h> #include <velocypack/Builder.h>
@ -57,12 +58,20 @@ class GeoConstructorTest : public ::testing::Test {
fakeit::Mock<transaction::Methods> trxMock; fakeit::Mock<transaction::Methods> trxMock;
transaction::Methods& trx; transaction::Methods& trx;
fakeit::Mock<transaction::Context> contextMock;
transaction::Context& context;
SmallVector<AqlValue>::allocator_type::arena_type arena; SmallVector<AqlValue>::allocator_type::arena_type arena;
SmallVector<AqlValue> params; SmallVector<AqlValue> params;
GeoConstructorTest() GeoConstructorTest()
: expressionContext(expressionContextMock.get()), trx(trxMock.get()), params{arena} {} : expressionContext(expressionContextMock.get()),
trx(trxMock.get()),
context(contextMock.get()),
params{arena} {
fakeit::When(Method(trxMock, transactionContextPtr)).AlwaysReturn(&context);
fakeit::When(Method(contextMock, getVPackOptions)).AlwaysReturn(&velocypack::Options::Defaults);
}
}; };
namespace geo_point { namespace geo_point {

View File

@ -34,6 +34,7 @@
#include "Aql/Functions.h" #include "Aql/Functions.h"
#include "Aql/Query.h" #include "Aql/Query.h"
#include "Containers/SmallVector.h" #include "Containers/SmallVector.h"
#include "Transaction/Context.h"
#include "Transaction/Methods.h" #include "Transaction/Methods.h"
#include <velocypack/Builder.h> #include <velocypack/Builder.h>
@ -64,14 +65,23 @@ protected:
fakeit::Mock<transaction::Methods> trxMock; fakeit::Mock<transaction::Methods> trxMock;
transaction::Methods& trx; transaction::Methods& trx;
fakeit::Mock<transaction::Context> contextMock;
transaction::Context& context;
SmallVector<AqlValue>::allocator_type::arena_type arena; SmallVector<AqlValue>::allocator_type::arena_type arena;
SmallVector<AqlValue> paramsA; SmallVector<AqlValue> paramsA;
SmallVector<AqlValue> paramsB; SmallVector<AqlValue> paramsB;
SmallVector<AqlValue> paramsC; SmallVector<AqlValue> paramsC;
GeoEqualsTest() : expressionContext(expressionContextMock.get()), GeoEqualsTest()
trx(trxMock.get()), paramsA{arena}, paramsB{arena}, paramsC{arena} { : expressionContext(expressionContextMock.get()),
trx(trxMock.get()),
context(contextMock.get()),
paramsA{arena},
paramsB{arena},
paramsC{arena} {
fakeit::When(Method(trxMock, transactionContextPtr)).AlwaysReturn(&context);
fakeit::When(Method(contextMock, getVPackOptions)).AlwaysReturn(&velocypack::Options::Defaults);
} }
~GeoEqualsTest() { ~GeoEqualsTest() {

View File

@ -498,10 +498,39 @@ function gatherBlockTestSuite () {
testSubqueryValuePropagation : function () { testSubqueryValuePropagation : function () {
c4 = db._create(cn4, {numberOfShards:3}); c4 = db._create(cn4, {numberOfShards:3});
c4.insert({Hallo:1}); c4.insert({Hallo:1});
var query = "FOR i IN 1..1 LET s = (FOR j IN 1..i FOR k IN " + cn4 + " RETURN j) RETURN s"; const query = `FOR i IN 1..1 LET s = (FOR j IN 1..i FOR k IN ${cn4} RETURN j) RETURN s`;
// check the return value // check the return value
var expected = [ [ 1 ] ]; const expected = [ [ 1 ] ];
var actual = AQL_EXECUTE(query).json; const rules = ['-splice-subqueries'];
const opts = {optimizer:{rules}};
const plan = AQL_EXPLAIN(query, {}, opts).plan;
const nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(0, nodeTypes.filter(type => type === 'SubqueryNode').length);
assertEqual(0, nodeTypes.filter(type => type === 'SubqueryStartNode').length);
assertEqual(0, nodeTypes.filter(type => type === 'SubqueryEndNode').length);
const actual = AQL_EXECUTE(query, {}, opts).json;
assertEqual(expected, actual, query);
},
testSplicedSubqueryValuePropagation : function () {
c4 = db._create(cn4, {numberOfShards:3});
c4.insert({Hallo:1});
const query = `FOR i IN 1..1 LET s = (FOR j IN 1..i FOR k IN ${cn4} RETURN j) RETURN s`;
// check the return value
const expected = [ [ 1 ] ];
const rules = ['+splice-subqueries'];
const opts = {optimizer:{rules}};
const plan = AQL_EXPLAIN(query, {}, opts).plan;
const nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertEqual(0, nodeTypes.filter(type => type === 'SubqueryNode').length);
assertNotEqual(0, nodeTypes.filter(type => type === 'SubqueryStartNode').length);
assertNotEqual(0, nodeTypes.filter(type => type === 'SubqueryEndNode').length);
const actual = AQL_EXECUTE(query, {}, opts).json;
assertEqual(expected, actual, query); assertEqual(expected, actual, query);
}, },

View File

@ -904,15 +904,6 @@ function optimizerIndexesTestSuite () {
const subqueryStartIdx = nodeTypes.indexOf('SubqueryStartNode'); const subqueryStartIdx = nodeTypes.indexOf('SubqueryStartNode');
const subqueryEndIdx = nodeTypes.indexOf('SubqueryEndNode'); const subqueryEndIdx = nodeTypes.indexOf('SubqueryEndNode');
const hasSplicedSubquery = subqueryStartIdx !== -1 && subqueryEndIdx !== -1; const hasSplicedSubquery = subqueryStartIdx !== -1 && subqueryEndIdx !== -1;
{ // TODO Remove this block as soon as subquery splicing is enabled in the cluster again.
// It's here so the test will fail as soon as that happens, so the actual test will not be forgotten
// to be re-enabled.
const isCluster = require("@arangodb/cluster").isCluster();
if (isCluster) {
assertFalse(hasSplicedSubquery);
return;
}
}
assertTrue(hasSplicedSubquery, JSON.stringify({ assertTrue(hasSplicedSubquery, JSON.stringify({
subqueryStartIdx, subqueryStartIdx,
subqueryEndIdx, subqueryEndIdx,

View File

@ -1073,15 +1073,6 @@ function optimizerRuleTestSuite() {
const query = `FOR i IN [123] RETURN (FOR v IN ${colName} FILTER v.a == i SORT v.b ASC RETURN v)`; const query = `FOR i IN [123] RETURN (FOR v IN ${colName} FILTER v.a == i SORT v.b ASC RETURN v)`;
const plan = AQL_EXPLAIN(query, {}, {optimizer: {rules: ['+splice-subqueries']}}).plan; const plan = AQL_EXPLAIN(query, {}, {optimizer: {rules: ['+splice-subqueries']}}).plan;
const rules = plan.rules; const rules = plan.rules;
{ // TODO Remove this block as soon as subquery splicing is enabled in the cluster again.
// It's here so the test will fail as soon as that happens, so the actual test will not be forgotten
// to be re-enabled.
const isCluster = require("@arangodb/cluster").isCluster();
if (isCluster) {
assertEqual(-1, rules.indexOf("splice-subqueries"));
return;
}
}
assertNotEqual(-1, rules.indexOf(ruleName)); assertNotEqual(-1, rules.indexOf(ruleName));
assertNotEqual(-1, rules.indexOf(secondRuleName)); assertNotEqual(-1, rules.indexOf(secondRuleName));
assertNotEqual(-1, rules.indexOf("remove-filter-covered-by-index")); assertNotEqual(-1, rules.indexOf("remove-filter-covered-by-index"));
@ -1123,15 +1114,6 @@ function optimizerRuleTestSuite() {
const query = `FOR i IN [123] RETURN (FOR v IN ${colName} FILTER v.a == i SORT v.b DESC RETURN v)`; const query = `FOR i IN [123] RETURN (FOR v IN ${colName} FILTER v.a == i SORT v.b DESC RETURN v)`;
const plan = AQL_EXPLAIN(query, {}, {optimizer: {rules: ['+splice-subqueries']}}).plan; const plan = AQL_EXPLAIN(query, {}, {optimizer: {rules: ['+splice-subqueries']}}).plan;
const rules = plan.rules; const rules = plan.rules;
{ // TODO Remove this block as soon as subquery splicing is enabled in the cluster again.
// It's here so the test will fail as soon as that happens, so the actual test will not be forgotten
// to be re-enabled.
const isCluster = require("@arangodb/cluster").isCluster();
if (isCluster) {
assertEqual(-1, rules.indexOf("splice-subqueries"));
return;
}
}
assertNotEqual(-1, rules.indexOf(ruleName)); assertNotEqual(-1, rules.indexOf(ruleName));
assertNotEqual(-1, rules.indexOf(secondRuleName)); assertNotEqual(-1, rules.indexOf(secondRuleName));
assertNotEqual(-1, rules.indexOf("remove-filter-covered-by-index")); assertNotEqual(-1, rules.indexOf("remove-filter-covered-by-index"));

View File

@ -86,32 +86,27 @@ function ahuacatlProfilerTestSuite () {
fuzzy = fuzzy || db._engine().name === 'mmfiles'; fuzzy = fuzzy || db._engine().name === 'mmfiles';
return _.sum( return _.sum(
_.values(rowsPerClient) _.values(rowsPerClient)
.map(fuzzy ? mmfilesBatches: optimalBatches) .map(fuzzy ? mmfilesBatches : optimalBatches)
); );
}; };
const dbServerBatch = (rows, fuzzy = false) => {
fuzzy = fuzzy || db._engine().name === 'mmfiles';
return (fuzzy ? mmfilesBatches : optimalBatches)(rows);
};
const dbServerOptimalBatches = (rowsPerClient) => const dbServerOptimalBatches = (rowsPerClient) =>
_.sum( _.sum(
_.values(rowsPerClient) _.values(rowsPerClient)
.map(optimalBatches) .map(optimalBatches)
); );
const groupedDBServerBatches = (rowsPerShard) => { const groupedBatches = (rowsPerClient, fuzzy) => {
const shardIds = Object.keys(rowsPerShard); const callInfo = {calls: 0, overhead: 0};
const shardToServerMapping = getResponsibleServers(shardIds);
const callsPerServer = {};
for (const [shard, rows] of Object.entries(rowsPerShard)) { for (const [shard, rows] of Object.entries(rowsPerClient)) {
const server = shardToServerMapping[shard];
const callInfo = callsPerServer[server] || {calls: 0, overhead: 0};
const testHere = rows + callInfo.overhead; const testHere = rows + callInfo.overhead;
if (db._engine().name === 'mmfiles') { callInfo.calls += dbServerBatch(testHere, fuzzy);
callInfo.calls += mmfilesBatches(testHere);
} else {
callInfo.calls += optimalBatches(testHere);
}
callInfo.overhead = testHere % defaultBatchSize; callInfo.overhead = testHere % defaultBatchSize;
callsPerServer[server] = callInfo;
} }
return _.sum(_.values(callsPerServer).map(c => c.calls)); return callInfo.calls;
}; };
return { return {
@ -178,32 +173,34 @@ function ahuacatlProfilerTestSuite () {
// Number of local getSome calls that do not return WAITING. // Number of local getSome calls that do not return WAITING.
// This is at least 1. // This is at least 1.
// Batches are just passed through, but empty ones are skipped.
// DONE can only be returned when the last shard is asked, so iff the last // DONE can only be returned when the last shard is asked, so iff the last
// asked shard is empty, there is one more call (the last call returns // asked shard is empty, there is one more call (the last call returns
// DONE without any results). // DONE without any results).
// As there is no guaranteed order in which the shards are processed, we // As there is no guaranteed order in which the shards are processed, we
// have to allow a range. // have to allow a range.
const localCalls = (rowsPerShard) => { const localCalls = (rowsPerShard) => {
const batches = _.sum( const batches = optimalNonEmptyBatches(_.sum(_.values(rowsPerShard)));
_.values(rowsPerShard) return [
.map(optimalNonEmptyBatches) Math.max(1, batches),
); Math.max(1, batches+1)
return [Math.max(1, batches), Math.max(1, batches+1)]; ];
}; };
// If we figure out that we are done depends on randomness. // If we figure out that we are done depends on randomness.
// In some cases we get the full batch on last shard, in this case the DBServer knows it is done. // In some cases we get the full batch on last shard, in this case the DBServer knows it is done.
// In other cases we get the full batch on an early shard, but 0 documents later, in chis case the DBServer does not know it is done // In other cases we get the full batch on an early shard, but 0 documents later, in this case the DBServer does
// in advance. // not know it is done in advance.
const fuzzyDBServerBatches = rowsPerServer => [dbServerBatches(rowsPerServer, false), dbServerBatches(rowsPerServer, true)]; const fuzzyDBServerBatches = rowsPerServer => [
groupedBatches(rowsPerServer, false),
groupedBatches(rowsPerServer, true)
];
const coordinatorBatches = (rowsPerShard) => addIntervals(fuzzyDBServerBatches(rowsPerShard), localCalls(rowsPerShard)); const coordinatorBatches = (rowsPerShard) => addIntervals(fuzzyDBServerBatches(rowsPerShard), localCalls(rowsPerShard));
const genNodeList = (rowsPerShard, rowsPerServer) => [ const genNodeList = (rowsPerShard, rowsPerServer) => [
{ type : SingletonBlock, calls : numberOfShards, items : numberOfShards }, { type : SingletonBlock, calls : numberOfShards, items : numberOfShards },
{ type : EnumerateCollectionBlock, calls : groupedDBServerBatches(rowsPerShard), items : totalItems(rowsPerShard) }, { type : EnumerateCollectionBlock, calls : groupedBatches(rowsPerShard), items : totalItems(rowsPerShard) },
// Twice the number due to WAITING, fuzzy, because the Gather does not know // Twice the number due to WAITING, fuzzy, because the Gather does not know
{ type : RemoteBlock, calls : fuzzyDBServerBatches(rowsPerServer).map(i => i * 2), items : totalItems(rowsPerShard) }, { type : RemoteBlock, calls : fuzzyDBServerBatches(rowsPerServer).map(i => i * 2), items : totalItems(rowsPerShard) },
// We get dbServerBatches(rowsPerShard) times WAITING, plus the non-waiting getSome calls. // We get dbServerBatches(rowsPerShard) times WAITING, plus the non-waiting getSome calls.

View File

@ -196,18 +196,6 @@ function ahuacatlSubqueryTestSuite () {
testSpliceSubqueryOutVariableName : function () { testSpliceSubqueryOutVariableName : function () {
const explainResult = AQL_EXPLAIN("FOR u IN _users LET theLetVariable = (FOR j IN _users RETURN j) RETURN theLetVariable"); const explainResult = AQL_EXPLAIN("FOR u IN _users LET theLetVariable = (FOR j IN _users RETURN j) RETURN theLetVariable");
{ // TODO Remove this block as soon as subquery splicing is enabled in the cluster again.
// It's here so the test will fail as soon as that happens, so the actual test will not be forgotten
// to be re-enabled.
const isCluster = require("@arangodb/cluster").isCluster();
if (isCluster) {
const numSubqueryEndNode = findExecutionNodes(explainResult, "SubqueryEndNode").length;
assertEqual(0, numSubqueryEndNode);
return;
}
}
const subqueryEndNode = findExecutionNodes(explainResult, "SubqueryEndNode")[0]; const subqueryEndNode = findExecutionNodes(explainResult, "SubqueryEndNode")[0];
assertEqual(subqueryEndNode.outVariable.name, "theLetVariable"); assertEqual(subqueryEndNode.outVariable.name, "theLetVariable");