1
0
Fork 0
arangodb/arangod/Aql/ModificationExecutorHelpers...

223 lines
8.0 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// 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 Markus Pfeiffer
////////////////////////////////////////////////////////////////////////////////
#include "ModificationExecutorHelpers.h"
#include "Aql/AqlValue.h"
#include "Aql/ModificationExecutorInfos.h"
#include "Basics/Result.h"
#include "Utils/CollectionNameResolver.h"
#include "Utils/OperationResult.h"
#include <velocypack/Builder.h>
#include <velocypack/Iterator.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
#include <string>
#include "Logger/LogMacros.h"
using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::basics;
Result ModificationExecutorHelpers::getKey(CollectionNameResolver const& resolver,
AqlValue const& value, std::string& key) {
TRI_ASSERT(key.empty());
// If `value` is a string, this is our _key entry, so we use that.
if (value.isString()) {
key.assign(value.slice().copyString());
return Result{};
}
if (!value.isObject()) {
return Result(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID,
std::string{"Expected object or string, but got "} +
value.slice().typeName());
}
if (!value.hasKey(StaticStrings::KeyString)) {
return Result{TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING,
std::string{"Expected _key to be present in document."}};
}
// Extract key from `value`, and make sure it is a string
bool mustDestroyKey;
AqlValue keyEntry = value.get(resolver, StaticStrings::KeyString, mustDestroyKey, false);
AqlValueGuard keyGuard(keyEntry, mustDestroyKey);
if (!keyEntry.isString()) {
return Result{TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING,
std::string{"Expected _key to be present in document."}};
}
// Key found and assigned, note rev is empty by assertion
key.assign(keyEntry.slice().copyString());
return Result{};
}
Result ModificationExecutorHelpers::getRevision(CollectionNameResolver const& resolver,
AqlValue const& value, std::string& rev) {
TRI_ASSERT(rev.empty());
if (!value.isObject()) {
return Result(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID,
std::string{"Expected object, but got "} + value.slice().typeName());
}
if (value.hasKey(StaticStrings::RevString)) {
bool mustDestroyRev;
AqlValue revEntry =
value.get(resolver, StaticStrings::RevString, mustDestroyRev, false);
AqlValueGuard revGuard(revEntry, mustDestroyRev);
if (!revEntry.isString()) {
return Result(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID,
std::string{"Expected _rev as string, but got "} +
value.slice().typeName());
}
// Rev found and assigned
rev.assign(revEntry.slice().copyString());
} // else we leave rev empty
return Result{};
}
Result ModificationExecutorHelpers::getKeyAndRevision(CollectionNameResolver const& resolver,
AqlValue const& value,
std::string& key, std::string& rev) {
Result result;
result = getKey(resolver, value, key);
// The key can either be a string, or contained in an object
// If it is passed in as a string, then there is no revision
// and there is no point in extracting it further on.
if (!result.ok() || value.isString()) {
return result;
}
return getRevision(resolver, value, rev);
}
void ModificationExecutorHelpers::buildKeyDocument(VPackBuilder& builder,
std::string const& key) {
builder.openObject();
builder.add(StaticStrings::KeyString, VPackValue(key));
builder.close();
}
void ModificationExecutorHelpers::buildKeyAndRevDocument(VPackBuilder& builder,
std::string const& key,
std::string const& rev) {
builder.openObject();
builder.add(StaticStrings::KeyString, VPackValue(key));
if (rev.empty()) {
// Necessary to sometimes remove _rev entries
builder.add(StaticStrings::RevString, VPackValue(VPackValueType::Null));
} else {
builder.add(StaticStrings::RevString, VPackValue(rev));
}
builder.close();
}
bool ModificationExecutorHelpers::writeRequired(ModificationExecutorInfos const& infos,
VPackSlice const& doc,
std::string const& key) {
return (!infos._consultAqlWriteFilter ||
!infos._aqlCollection->getCollection()->skipForAqlWrite(doc, key));
}
void ModificationExecutorHelpers::throwOperationResultException(
ModificationExecutorInfos const& infos, OperationResult const& result) {
auto const& errorCounter = result.countErrorCodes;
// Early escape if we are ignoring errors.
if (infos._ignoreErrors == true || errorCounter.empty()) {
return;
}
// Find the first relevant error for which we want to throw.
// If _ignoreDocumentNotFound is true, then this is any error other than
// TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND, otherwise it is just any error.
//
// Find the first error with a message and throw that
// This mirrors previous behaviour and might not be entirely ideal.
for (auto const p : errorCounter) {
auto const errorCode = p.first;
if (!(infos._ignoreDocumentNotFound && errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND)) {
// Find the first error and throw with message.
for (auto doc : VPackArrayIterator(result.slice())) {
if (doc.isObject() && doc.hasKey(StaticStrings::ErrorNum) &&
doc.get(StaticStrings::ErrorNum).getInt() == errorCode) {
VPackSlice s = doc.get(StaticStrings::ErrorMessage);
if (s.isString()) {
THROW_ARANGO_EXCEPTION_MESSAGE(errorCode, s.copyString());
}
}
}
// if we did not find a message, we still throw something, because we
// know that a relevant error has happened
THROW_ARANGO_EXCEPTION(errorCode);
}
}
}
// Convert ModificationOptions to OperationOptions struct
OperationOptions ModificationExecutorHelpers::convertOptions(ModificationOptions const& in,
Variable const* outVariableNew,
Variable const* outVariableOld) {
OperationOptions out;
// commented out OperationOptions attributesare not provided
// by the ModificationOptions or the information given by the
// Variable pointer.
// in.ignoreErrors;
out.waitForSync = in.waitForSync;
out.keepNull = !in.nullMeansRemove;
out.mergeObjects = in.mergeObjects;
// in.ignoreDocumentNotFound;
// in.readCompleteInput;
out.isRestore = in.useIsRestore;
// in.consultAqlWriteFilter;
// in.exclusive;
out.overwrite = in.overwrite;
out.ignoreRevs = in.ignoreRevs;
out.returnNew = (outVariableNew != nullptr);
out.returnOld = (outVariableOld != nullptr);
out.silent = !(out.returnNew || out.returnOld);
return out;
}
AqlValue ModificationExecutorHelpers::getDocumentOrNull(VPackSlice const& elm,
std::string const& key) {
if (elm.hasKey(key)) {
return AqlValue{elm.get(key)};
}
return AqlValue{VPackSlice::nullSlice()};
}