From f549dbe8cd85d7b2bd23d9a91ce27f0b7c20edde Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 17 Sep 2018 13:46:17 +0200 Subject: [PATCH] add ability to 3.3 to read compressed AqlItemBlocks (#6517) but not the ability to produce them. This way we can support running 3.3 as part of a mixed 3.3/3.4 cluster, and do not break any clusters that run mixed versions of 3.3 current and either 3.2 or 3.3 older versions --- arangod/Aql/AqlItemBlock.cpp | 176 ++++++++++++++++++++++++----------- 1 file changed, 122 insertions(+), 54 deletions(-) diff --git a/arangod/Aql/AqlItemBlock.cpp b/arangod/Aql/AqlItemBlock.cpp index f381d82ade..0d27b84d2d 100644 --- a/arangod/Aql/AqlItemBlock.cpp +++ b/arangod/Aql/AqlItemBlock.cpp @@ -100,77 +100,145 @@ AqlItemBlock::AqlItemBlock(ResourceMonitor* resourceMonitor, VPackSlice const sl VPackArrayIterator dataIterator(data); VPackArrayIterator rawIterator(raw); + auto storeSingleValue = [this](size_t row, size_t column, VPackArrayIterator& it, std::vector& madeHere) { + AqlValue a(it.value()); + it.next(); + try { + setValue(row, column, a); // if this throws, a is destroyed again + } catch (...) { + a.destroy(); + throw; + } + madeHere.emplace_back(a); + }; + + enum RunType { + NoRun = 0, + EmptyRun, + NextRun, + PositionalRun + }; + + int64_t runLength = 0; + size_t tablePos = 0; + RunType runType = NoRun; + try { // skip the first two records rawIterator.next(); rawIterator.next(); - int64_t emptyRun = 0; - + for (RegisterId column = 0; column < _nrRegs; column++) { for (size_t i = 0; i < _nrItems; i++) { - if (emptyRun > 0) { - emptyRun--; - } else { - VPackSlice dataEntry = dataIterator.value(); - dataIterator.next(); - if (!dataEntry.isNumber()) { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "data must contain only numbers"); + if (runLength > 0) { + switch (runType) { + case EmptyRun: + // nothing to do + break; + + case NextRun: + storeSingleValue(i, column, rawIterator, madeHere); + break; + + case PositionalRun: + TRI_ASSERT(tablePos < madeHere.size()); + setValue(i, column, madeHere[tablePos]); + break; + + case NoRun: { + TRI_ASSERT(false); + } } - int64_t n = dataEntry.getNumericValue(); - if (n == 0) { - // empty, do nothing here - } else if (n == -1) { - // empty run: - VPackSlice runLength = dataIterator.value(); - dataIterator.next(); - TRI_ASSERT(runLength.isNumber()); - emptyRun = runLength.getNumericValue(); - emptyRun--; - } else if (n == -2) { - // a range - VPackSlice lowBound = dataIterator.value(); - dataIterator.next(); - VPackSlice highBound = dataIterator.value(); - dataIterator.next(); - - int64_t low = - VelocyPackHelper::getNumericValue(lowBound, 0); - int64_t high = - VelocyPackHelper::getNumericValue(highBound, 0); - AqlValue a(low, high); - try { - setValue(i, column, a); - } catch (...) { - a.destroy(); - throw; + + --runLength; + if (runLength == 0) { + runType = NoRun; + tablePos = 0; + } + + continue; + } + + VPackSlice dataEntry = dataIterator.value(); + dataIterator.next(); + if (!dataEntry.isNumber()) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, + "data must contain only numbers"); + } + + int64_t n = dataEntry.getNumericValue(); + if (n == 0) { + // empty, do nothing here + } else if (n == 1) { + // a VelocyPack value + storeSingleValue(i, column, rawIterator, madeHere); + } else if (n == -1 || n == -3 || n == -4) { + // -1: empty run, -3: run of "next" values, -4: run of positional values + VPackSlice v = dataIterator.value(); + dataIterator.next(); + TRI_ASSERT(v.isNumber()); + runLength = v.getNumericValue(); + runLength--; + switch (n) { + case -1: + runType = EmptyRun; + break; + + case -3: + runType = NextRun; + storeSingleValue(i, column, rawIterator, madeHere); + break; + + case -4: { + runType = PositionalRun; + VPackSlice v = dataIterator.value(); + dataIterator.next(); + TRI_ASSERT(v.isNumber()); + tablePos = v.getNumericValue(); + if (tablePos >= madeHere.size()) { + // safeguard against out-of-bounds accesses + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, + "found undefined data value"); + } + + setValue(i, column, madeHere[tablePos]); } - } else if (n == 1) { - // a VelocyPack value - AqlValue a(rawIterator.value()); - rawIterator.next(); - try { - setValue(i, column, a); // if this throws, a is destroyed again - } catch (...) { - a.destroy(); - throw; - } - madeHere.emplace_back(a); - } else if (n >= 2) { - setValue(i, column, madeHere[static_cast(n)]); - // If this throws, all is OK, because it was already put into - // the block elsewhere. - } else { + } + } else if (n == -2) { + // a range + VPackSlice lowBound = dataIterator.value(); + dataIterator.next(); + VPackSlice highBound = dataIterator.value(); + dataIterator.next(); + + int64_t low = + VelocyPackHelper::getNumericValue(lowBound, 0); + int64_t high = + VelocyPackHelper::getNumericValue(highBound, 0); + emplaceValue(i, column, low, high); + } else if (n >= 2) { + if (static_cast(n) >= madeHere.size()) { + // safeguard against out-of-bounds accesses THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "found undefined data value"); } + + setValue(i, column, madeHere[static_cast(n)]); + // If this throws, all is OK, because it was already put into + // the block elsewhere. + } else { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, + "found invalid data encoding value"); } } } } catch (...) { destroy(); - // TODO: rethrow? + throw; } + + TRI_ASSERT(runLength == 0); + TRI_ASSERT(runType == NoRun); } /// @brief destroy the block, used in the destructor and elsewhere