1
0
Fork 0

Merge branch 'spdvpk' of github.com:arangodb/arangodb into spdvpk

This commit is contained in:
Michael Hackstein 2016-03-18 14:29:23 +01:00
commit 16208d78f6
9 changed files with 334 additions and 196 deletions

View File

@ -902,6 +902,8 @@ AqlValue AqlValue::CreateFromBlocks(
int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
AqlValue const& right,
bool compareUtf8) {
VPackOptions* options = trx->transactionContext()->getVPackOptions();
AqlValue::AqlValueType const leftType = left.type();
AqlValue::AqlValueType const rightType = right.type();
@ -914,7 +916,7 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
VPackBuilder rightBuilder;
right.toVelocyPack(trx, rightBuilder);
return arangodb::basics::VelocyPackHelper::compare(leftBuilder.slice(), rightBuilder.slice(), compareUtf8);
return arangodb::basics::VelocyPackHelper::compare(leftBuilder.slice(), rightBuilder.slice(), compareUtf8, options);
}
// fall-through to other types intentional
}
@ -926,7 +928,7 @@ int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left,
case VPACK_POINTER:
case VPACK_INLINE:
case VPACK_EXTERNAL: {
return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(), compareUtf8);
return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(), compareUtf8, options);
}
case DOCVEC: {
// use lexicographic ordering of AqlValues regardless of block,

View File

@ -1946,7 +1946,8 @@ AqlValue Functions::SortedUnique(arangodb::aql::Query* query,
AqlValueMaterializer materializer(trx);
VPackSlice slice = materializer.slice(value);
std::set<VPackSlice, arangodb::basics::VelocyPackHelper::VPackLess<true>> values;
arangodb::basics::VelocyPackHelper::VPackLess<true> less(trx->transactionContext()->getVPackOptions(), &slice, &slice);
std::set<VPackSlice, arangodb::basics::VelocyPackHelper::VPackLess<true>> values(less);
for (auto const& it : VPackArrayIterator(slice)) {
if (!it.isNone()) {
values.insert(it);

View File

@ -200,142 +200,6 @@ void ModificationBlock::handleResult(int code, bool ignoreErrors,
THROW_ARANGO_EXCEPTION(code);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief execute an update or replace document modification operation
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* ModificationBlock::modify(std::vector<AqlItemBlock*>& blocks,
bool isReplace) {
size_t const count = countBlocksRows(blocks);
if (count == 0) {
return nullptr;
}
std::unique_ptr<AqlItemBlock> result;
auto ep = static_cast<UpdateNode const*>(getPlanNode());
auto it = ep->getRegisterPlan()->varInfo.find(ep->_inDocVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
RegisterId const docRegisterId = it->second.registerId;
RegisterId keyRegisterId = 0; // default initialization
bool const ignoreDocumentNotFound = ep->getOptions().ignoreDocumentNotFound;
bool const producesOutput =
(ep->_outVariableOld != nullptr || ep->_outVariableNew != nullptr);
bool const hasKeyVariable = (ep->_inKeyVariable != nullptr);
std::string errorMessage;
VPackBuilder keyBuilder;
VPackBuilder object;
if (hasKeyVariable) {
it = ep->getRegisterPlan()->varInfo.find(ep->_inKeyVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
keyRegisterId = it->second.registerId;
}
result.reset(new AqlItemBlock(
count,
getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]));
OperationOptions options;
options.silent = !producesOutput;
options.waitForSync = ep->_options.waitForSync;
options.mergeObjects = ep->_options.mergeObjects;
options.keepNull = !ep->_options.nullMeansRemove;
options.returnOld = (producesOutput && ep->_outVariableOld != nullptr);
options.returnNew = (producesOutput && ep->_outVariableNew != nullptr);
options.ignoreRevs = true;
// loop over all blocks
size_t dstRow = 0;
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
auto* res = (*it); // This is intentionally a copy!
throwIfKilled(); // check if we were aborted
size_t const n = res->size();
// loop over the complete block
for (size_t i = 0; i < n; ++i) {
AqlValue const& a = res->getValueReference(i, docRegisterId);
int errorCode = TRI_ERROR_NO_ERROR;
std::string key;
if (a.isObject()) {
// value is an object
if (hasKeyVariable) {
// seperate key specification
AqlValue const& k = res->getValueReference(i, keyRegisterId);
errorCode = extractKey(k, key);
} else {
errorCode = extractKey(a, key);
}
} else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
errorMessage += std::string("expecting 'Object', got: ") +
a.slice().typeName() + std::string(" while handling: ") +
_exeNode->getTypeString();
}
if (errorCode == TRI_ERROR_NO_ERROR) {
keyBuilder.clear();
keyBuilder.openObject();
keyBuilder.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
keyBuilder.close();
VPackSlice toUpdate;
if (hasKeyVariable) {
object = VPackCollection::merge(a.slice(), keyBuilder.slice(), false, false);
toUpdate = object.slice();
}
else {
// use original slice for updating
toUpdate = a.slice();
}
// fetch old revision
OperationResult opRes;
if (isReplace) {
opRes = _trx->replace(_collection->name, toUpdate, options);
} else {
opRes = _trx->update(_collection->name, toUpdate, options);
}
errorCode = opRes.code;
if (producesOutput && errorCode == TRI_ERROR_NO_ERROR) {
if (ep->_outVariableOld != nullptr) {
// store $OLD
result->setValue(dstRow, _outRegOld, AqlValue(opRes.slice().get("old")));
}
if (ep->_outVariableNew != nullptr) {
// store $NEW
result->setValue(dstRow, _outRegNew, AqlValue(opRes.slice().get("new")));
}
}
if (errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND && _isDBServer &&
ignoreDocumentNotFound) {
// Ignore document not found on the DBserver:
errorCode = TRI_ERROR_NO_ERROR;
}
}
handleResult(errorCode, ep->_options.ignoreErrors, &errorMessage);
++dstRow;
}
// done with a block
// now free it already
(*it) = nullptr;
delete res;
}
return result.release();
}
RemoveBlock::RemoveBlock(ExecutionEngine* engine, RemoveNode const* ep)
: ModificationBlock(engine, ep) {}
@ -522,7 +386,129 @@ UpdateBlock::UpdateBlock(ExecutionEngine* engine, UpdateNode const* ep)
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* UpdateBlock::work(std::vector<AqlItemBlock*>& blocks) {
return modify(blocks, false);
size_t const count = countBlocksRows(blocks);
if (count == 0) {
return nullptr;
}
std::unique_ptr<AqlItemBlock> result;
auto ep = static_cast<UpdateNode const*>(getPlanNode());
auto it = ep->getRegisterPlan()->varInfo.find(ep->_inDocVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
RegisterId const docRegisterId = it->second.registerId;
RegisterId keyRegisterId = 0; // default initialization
bool const ignoreDocumentNotFound = ep->getOptions().ignoreDocumentNotFound;
bool const producesOutput =
(ep->_outVariableOld != nullptr || ep->_outVariableNew != nullptr);
bool const hasKeyVariable = (ep->_inKeyVariable != nullptr);
std::string errorMessage;
VPackBuilder keyBuilder;
VPackBuilder object;
if (hasKeyVariable) {
it = ep->getRegisterPlan()->varInfo.find(ep->_inKeyVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
keyRegisterId = it->second.registerId;
}
result.reset(new AqlItemBlock(
count,
getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]));
OperationOptions options;
options.silent = !producesOutput;
options.waitForSync = ep->_options.waitForSync;
options.mergeObjects = ep->_options.mergeObjects;
options.keepNull = !ep->_options.nullMeansRemove;
options.returnOld = (producesOutput && ep->_outVariableOld != nullptr);
options.returnNew = (producesOutput && ep->_outVariableNew != nullptr);
options.ignoreRevs = true;
// loop over all blocks
size_t dstRow = 0;
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
auto* res = (*it); // This is intentionally a copy!
throwIfKilled(); // check if we were aborted
size_t const n = res->size();
// loop over the complete block
for (size_t i = 0; i < n; ++i) {
AqlValue const& a = res->getValueReference(i, docRegisterId);
int errorCode = TRI_ERROR_NO_ERROR;
std::string key;
if (a.isObject()) {
// value is an object
if (hasKeyVariable) {
// seperate key specification
AqlValue const& k = res->getValueReference(i, keyRegisterId);
errorCode = extractKey(k, key);
} else {
errorCode = extractKey(a, key);
}
} else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
errorMessage += std::string("expecting 'Object', got: ") +
a.slice().typeName() + std::string(" while handling: ") +
_exeNode->getTypeString();
}
if (errorCode == TRI_ERROR_NO_ERROR) {
keyBuilder.clear();
keyBuilder.openObject();
keyBuilder.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
keyBuilder.close();
VPackSlice toUpdate;
if (hasKeyVariable) {
object = VPackCollection::merge(a.slice(), keyBuilder.slice(), false, false);
toUpdate = object.slice();
}
else {
// use original slice for updating
toUpdate = a.slice();
}
// fetch old revision
OperationResult opRes = _trx->update(_collection->name, toUpdate, options);
errorCode = opRes.code;
if (producesOutput && errorCode == TRI_ERROR_NO_ERROR) {
if (ep->_outVariableOld != nullptr) {
// store $OLD
result->setValue(dstRow, _outRegOld, AqlValue(opRes.slice().get("old")));
}
if (ep->_outVariableNew != nullptr) {
// store $NEW
result->setValue(dstRow, _outRegNew, AqlValue(opRes.slice().get("new")));
}
}
if (errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND && _isDBServer &&
ignoreDocumentNotFound) {
// Ignore document not found on the DBserver:
errorCode = TRI_ERROR_NO_ERROR;
}
}
handleResult(errorCode, ep->_options.ignoreErrors, &errorMessage);
++dstRow;
}
// done with a block
// now free it already
(*it) = nullptr;
delete res;
}
return result.release();
}
UpsertBlock::UpsertBlock(ExecutionEngine* engine, UpsertNode const* ep)
@ -668,5 +654,127 @@ ReplaceBlock::ReplaceBlock(ExecutionEngine* engine, ReplaceNode const* ep)
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* ReplaceBlock::work(std::vector<AqlItemBlock*>& blocks) {
return modify(blocks, true);
size_t const count = countBlocksRows(blocks);
if (count == 0) {
return nullptr;
}
std::unique_ptr<AqlItemBlock> result;
auto ep = static_cast<ReplaceNode const*>(getPlanNode());
auto it = ep->getRegisterPlan()->varInfo.find(ep->_inDocVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
RegisterId const docRegisterId = it->second.registerId;
RegisterId keyRegisterId = 0; // default initialization
bool const ignoreDocumentNotFound = ep->getOptions().ignoreDocumentNotFound;
bool const producesOutput =
(ep->_outVariableOld != nullptr || ep->_outVariableNew != nullptr);
bool const hasKeyVariable = (ep->_inKeyVariable != nullptr);
std::string errorMessage;
VPackBuilder keyBuilder;
VPackBuilder object;
if (hasKeyVariable) {
it = ep->getRegisterPlan()->varInfo.find(ep->_inKeyVariable->id);
TRI_ASSERT(it != ep->getRegisterPlan()->varInfo.end());
keyRegisterId = it->second.registerId;
}
result.reset(new AqlItemBlock(
count,
getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]));
OperationOptions options;
options.silent = !producesOutput;
options.waitForSync = ep->_options.waitForSync;
options.mergeObjects = ep->_options.mergeObjects;
options.keepNull = !ep->_options.nullMeansRemove;
options.returnOld = (producesOutput && ep->_outVariableOld != nullptr);
options.returnNew = (producesOutput && ep->_outVariableNew != nullptr);
options.ignoreRevs = true;
// loop over all blocks
size_t dstRow = 0;
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
auto* res = (*it); // This is intentionally a copy!
throwIfKilled(); // check if we were aborted
size_t const n = res->size();
// loop over the complete block
for (size_t i = 0; i < n; ++i) {
AqlValue const& a = res->getValueReference(i, docRegisterId);
int errorCode = TRI_ERROR_NO_ERROR;
std::string key;
if (a.isObject()) {
// value is an object
if (hasKeyVariable) {
// seperate key specification
AqlValue const& k = res->getValueReference(i, keyRegisterId);
errorCode = extractKey(k, key);
} else {
errorCode = extractKey(a, key);
}
} else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
errorMessage += std::string("expecting 'Object', got: ") +
a.slice().typeName() + std::string(" while handling: ") +
_exeNode->getTypeString();
}
if (errorCode == TRI_ERROR_NO_ERROR) {
keyBuilder.clear();
keyBuilder.openObject();
keyBuilder.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
keyBuilder.close();
VPackSlice toUpdate;
if (hasKeyVariable) {
object = VPackCollection::merge(a.slice(), keyBuilder.slice(), false, false);
toUpdate = object.slice();
}
else {
// use original slice for updating
toUpdate = a.slice();
}
// fetch old revision
OperationResult opRes = _trx->replace(_collection->name, toUpdate, options);
errorCode = opRes.code;
if (producesOutput && errorCode == TRI_ERROR_NO_ERROR) {
if (ep->_outVariableOld != nullptr) {
// store $OLD
result->setValue(dstRow, _outRegOld, AqlValue(opRes.slice().get("old")));
}
if (ep->_outVariableNew != nullptr) {
// store $NEW
result->setValue(dstRow, _outRegNew, AqlValue(opRes.slice().get("new")));
}
}
if (errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND && _isDBServer &&
ignoreDocumentNotFound) {
// Ignore document not found on the DBserver:
errorCode = TRI_ERROR_NO_ERROR;
}
}
handleResult(errorCode, ep->_options.ignoreErrors, &errorMessage);
++dstRow;
}
// done with a block
// now free it already
(*it) = nullptr;
delete res;
}
return result.release();
}

View File

@ -65,24 +65,12 @@ class ModificationBlock : public ExecutionBlock {
int extractKey(AqlValue const&, std::string&);
//////////////////////////////////////////////////////////////////////////////
/// @brief check whether a shard key was set when it must not be set
//////////////////////////////////////////////////////////////////////////////
bool isShardKeyError(struct TRI_json_t const*) const;
//////////////////////////////////////////////////////////////////////////////
/// @brief process the result of a data-modification operation
//////////////////////////////////////////////////////////////////////////////
void handleResult(int, bool, std::string const* errorMessage = nullptr);
//////////////////////////////////////////////////////////////////////////////
/// @brief execute an update or replace document modification operation
//////////////////////////////////////////////////////////////////////////////
AqlItemBlock* modify(std::vector<AqlItemBlock*>& blocks, bool isReplace);
protected:
//////////////////////////////////////////////////////////////////////////////
/// @brief output register ($OLD)

View File

@ -1799,7 +1799,8 @@ static void ModifyVocbase(TRI_voc_document_operation_e operation,
VPackBuilder updateBuilder;
{ VPackObjectBuilder guard(&updateBuilder);
{
VPackObjectBuilder guard(&updateBuilder);
int res = V8ToVPackNoKeyRevId(isolate, updateBuilder, args[1]);
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);

View File

@ -3408,6 +3408,12 @@ int TRI_document_collection_t::update(Transaction* trx,
}
}
if (newSlice.length() <= 1) {
// no need to do anything
*mptr = *oldHeader;
return TRI_ERROR_NO_ERROR;
}
// merge old and new values
TransactionBuilderLeaser builder(trx);
if (options.recoveryMarker == nullptr) {

View File

@ -1965,7 +1965,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { } INTO @@cn", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { } INTO @@cn", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");
@ -1979,7 +1979,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");
@ -1993,7 +1993,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { _to: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { _to: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");
@ -2007,7 +2007,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { _to: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { _to: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");
@ -2021,7 +2021,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { _from: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { _from: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");
@ -2035,7 +2035,7 @@ function ahuacatlInsertSuite () {
db._drop("UnitTestsAhuacatlEdge");
var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge");
assertQueryError(errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, "FOR i IN 1..50 INSERT { _from: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertQueryError(errors.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code, "FOR i IN 1..50 INSERT { _from: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)) } INTO @@cn LET inserted = NEW RETURN inserted", { "@cn": edge.name() });
assertEqual(0, edge.count());
db._drop("UnitTestsAhuacatlEdge");

View File

@ -196,6 +196,8 @@ static int TypeWeight(VPackSlice const& slice) {
case VPackValueType::SmallInt:
return 2;
case VPackValueType::String:
case VPackValueType::Custom:
// custom type is used for id
return 3;
case VPackValueType::Array:
return 4;
@ -374,24 +376,19 @@ static bool PrintVelocyPack(int fd, VPackSlice const& slice,
bool VelocyPackHelper::velocyPackToFile(char const* filename,
VPackSlice const& slice,
bool syncFile) {
char* tmp = TRI_Concatenate2String(filename, ".tmp");
if (tmp == nullptr) {
return false;
}
std::string const tmp = std::string(filename) + ".tmp";
// remove a potentially existing temporary file
if (TRI_ExistsFile(tmp)) {
TRI_UnlinkFile(tmp);
if (TRI_ExistsFile(tmp.c_str())) {
TRI_UnlinkFile(tmp.c_str());
}
int fd = TRI_CREATE(tmp, O_CREAT | O_TRUNC | O_EXCL | O_RDWR | TRI_O_CLOEXEC,
int fd = TRI_CREATE(tmp.c_str(), O_CREAT | O_TRUNC | O_EXCL | O_RDWR | TRI_O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd < 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG(ERR) << "cannot create json file '" << tmp << "': " << TRI_LAST_ERROR_STR;
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
return false;
}
@ -399,8 +396,7 @@ bool VelocyPackHelper::velocyPackToFile(char const* filename,
TRI_CLOSE(fd);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG(ERR) << "cannot write to json file '" << tmp << "': " << TRI_LAST_ERROR_STR;
TRI_UnlinkFile(tmp);
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
TRI_UnlinkFile(tmp.c_str());
return false;
}
@ -411,8 +407,7 @@ bool VelocyPackHelper::velocyPackToFile(char const* filename,
TRI_CLOSE(fd);
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG(ERR) << "cannot sync saved json '" << tmp << "': " << TRI_LAST_ERROR_STR;
TRI_UnlinkFile(tmp);
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
TRI_UnlinkFile(tmp.c_str());
return false;
}
}
@ -422,29 +417,26 @@ bool VelocyPackHelper::velocyPackToFile(char const* filename,
if (res < 0) {
TRI_set_errno(TRI_ERROR_SYS_ERROR);
LOG(ERR) << "cannot close saved file '" << tmp << "': " << TRI_LAST_ERROR_STR;
TRI_UnlinkFile(tmp);
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
TRI_UnlinkFile(tmp.c_str());
return false;
}
res = TRI_RenameFile(tmp, filename);
res = TRI_RenameFile(tmp.c_str(), filename);
if (res != TRI_ERROR_NO_ERROR) {
TRI_set_errno(res);
LOG(ERR) << "cannot rename saved file '" << tmp << "' to '" << filename << "': " << TRI_LAST_ERROR_STR;
TRI_UnlinkFile(tmp);
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
TRI_UnlinkFile(tmp.c_str());
return false;
}
TRI_FreeString(TRI_CORE_MEM_ZONE, tmp);
return true;
}
int VelocyPackHelper::compare(VPackSlice const& lhs, VPackSlice const& rhs,
bool useUTF8) {
bool useUTF8, VPackOptions const* options,
VPackSlice const* lhsBase, VPackSlice const* rhsBase) {
{
int lWeight = TypeWeight(lhs);
int rWeight = TypeWeight(rhs);
@ -499,12 +491,41 @@ int VelocyPackHelper::compare(VPackSlice const& lhs, VPackSlice const& rhs,
}
return 1;
}
case VPackValueType::Custom:
case VPackValueType::String: {
int res;
std::string lhsString;
VPackValueLength nl;
char const* left;
if (lhs.isCustom()) {
if (lhsBase == nullptr || options == nullptr || options->customTypeHandler == nullptr) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"Could not extract custom attribute.");
}
lhsString.assign(options->customTypeHandler->toString(lhs, options, *lhsBase));
left = lhsString.c_str();
nl = lhsString.size();
} else {
left = lhs.getString(nl);
}
TRI_ASSERT(left != nullptr);
std::string rhsString;
VPackValueLength nr;
char const* left = lhs.getString(nl);
char const* right = rhs.getString(nr);
char const* right;
if (rhs.isCustom()) {
if (rhsBase == nullptr || options == nullptr || options->customTypeHandler == nullptr) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"Could not extract custom attribute.");
}
rhsString.assign(options->customTypeHandler->toString(rhs, options, *rhsBase));
right = rhsString.c_str();
nr = rhsString.size();
} else {
right = rhs.getString(nr);
}
TRI_ASSERT(right != nullptr);
int res;
if (useUTF8) {
res = TRI_compare_utf8(left, nl, right, nr);
} else {
@ -513,7 +534,8 @@ int VelocyPackHelper::compare(VPackSlice const& lhs, VPackSlice const& rhs,
}
if (res < 0) {
return -1;
} else if (res > 0) {
}
if (res > 0) {
return 1;
}
// res == 0
@ -537,7 +559,7 @@ int VelocyPackHelper::compare(VPackSlice const& lhs, VPackSlice const& rhs,
rhsValue = rhs.at(i);
}
int result = compare(lhsValue, rhsValue, useUTF8);
int result = compare(lhsValue, rhsValue, useUTF8, options, &lhs, &rhs);
if (result != 0) {
return result;
}
@ -558,7 +580,7 @@ int VelocyPackHelper::compare(VPackSlice const& lhs, VPackSlice const& rhs,
rhsValue = rhs.get(key);
}
int result = compare(lhsValue, rhsValue, useUTF8);
int result = compare(lhsValue, rhsValue, useUTF8, options, &lhs, &rhs);
if (result != 0) {
return result;
}
@ -595,9 +617,8 @@ double VelocyPackHelper::toDouble(VPackSlice const& slice, bool& failed) {
case VPackValueType::UInt:
case VPackValueType::SmallInt:
return slice.getNumericValue<double>();
case VPackValueType::String:
{
std::string tmp = slice.copyString();
case VPackValueType::String: {
std::string tmp(slice.copyString());
try {
// try converting string to number
return std::stod(tmp);
@ -609,8 +630,7 @@ double VelocyPackHelper::toDouble(VPackSlice const& slice, bool& failed) {
}
break;
}
case VPackValueType::Array:
{
case VPackValueType::Array: {
VPackValueLength const n = slice.length();
if (n == 0) {
@ -642,8 +662,7 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& logger,
bool longer = sliceStr.size() > cutoff;
if (longer) {
logger << sliceStr.substr(cutoff) << "...";
}
else {
} else {
logger << sliceStr;
}
return logger;

View File

@ -75,10 +75,19 @@ class VelocyPackHelper {
template <bool useUtf8>
struct VPackLess {
VPackLess(arangodb::velocypack::Options const* options = &arangodb::velocypack::Options::Defaults,
arangodb::velocypack::Slice const* lhsBase = nullptr,
arangodb::velocypack::Slice const* rhsBase = nullptr)
: options(options), lhsBase(lhsBase), rhsBase(rhsBase) {}
inline bool operator()(arangodb::velocypack::Slice const& lhs,
arangodb::velocypack::Slice const& rhs) const {
return VelocyPackHelper::compare(lhs, rhs, useUtf8) < 0;
return VelocyPackHelper::compare(lhs, rhs, useUtf8, options, lhsBase, rhsBase) < 0;
}
arangodb::velocypack::Options const* options;
arangodb::velocypack::Slice const* lhsBase;
arangodb::velocypack::Slice const* rhsBase;
};
struct AttributeSorter {
@ -193,7 +202,11 @@ class VelocyPackHelper {
/// @brief Compares two VelocyPack slices
//////////////////////////////////////////////////////////////////////////////
static int compare(VPackSlice const&, VPackSlice const&, bool);
static int compare(arangodb::velocypack::Slice const& lhs,
arangodb::velocypack::Slice const& rhs,
bool useUTF8, arangodb::velocypack::Options const* options = &arangodb::velocypack::Options::Defaults,
arangodb::velocypack::Slice const* lhsBase = nullptr,
arangodb::velocypack::Slice const* rhsBase = nullptr);
//////////////////////////////////////////////////////////////////////////////
/// @brief Merges two VelocyPack Slices