1
0
Fork 0

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

This commit is contained in:
jsteemann 2016-03-14 14:34:36 +01:00
commit d7bec3c343
11 changed files with 408 additions and 126 deletions

View File

@ -423,7 +423,7 @@ Slice Builder::getKey(std::string const& key) const {
}
uint8_t* Builder::set(Value const& item) {
auto const oldPos = _start + _pos;
auto const oldPos = _pos;
auto ctype = item.cType();
checkKeyIsString(item.valueType() == ValueType::String);
@ -668,7 +668,7 @@ uint8_t* Builder::set(Value const& item) {
"Cannot set a ValueType::Custom with this method");
}
}
return oldPos;
return _start + oldPos;
}
uint8_t* Builder::set(Slice const& item) {
@ -687,6 +687,8 @@ uint8_t* Builder::set(ValuePair const& pair) {
// ValueType::Binary, or ValueType::Custom, which can be built
// with two pieces of information
auto const oldPos = _pos;
checkKeyIsString(pair.valueType() == ValueType::String);
if (pair.valueType() == ValueType::Binary) {
@ -694,7 +696,7 @@ uint8_t* Builder::set(ValuePair const& pair) {
appendUInt(v, 0xbf);
memcpy(_start + _pos, pair.getStart(), checkOverflow(v));
_pos += v;
return nullptr; // unused here
return _start + oldPos;
} else if (pair.valueType() == ValueType::String) {
uint64_t size = pair.getSize();
if (size > 126) {
@ -802,12 +804,13 @@ uint8_t* Builder::add(ObjectIterator&& sub) {
if (_keyWritten) {
throw Exception(Exception::BuilderKeyAlreadyWritten);
}
auto const oldPos = _start + _pos;
auto const oldPos = _pos;
while (sub.valid()) {
add(sub.key().copyString(), sub.value());
add(sub.key());
add(sub.value());
sub.next();
}
return oldPos;
return _start + oldPos;
}
uint8_t* Builder::add(Value const& sub) { return addInternal<Value>(sub); }
@ -832,12 +835,12 @@ uint8_t* Builder::add(ArrayIterator&& sub) {
if (_start[tos] != 0x06 && _start[tos] != 0x13) {
throw Exception(Exception::BuilderNeedOpenArray);
}
auto const oldPos = _start + _pos;
auto const oldPos = _pos;
while (sub.valid()) {
add(sub.value());
sub.next();
}
return oldPos;
return _start + oldPos;
}
static_assert(sizeof(double) == 8, "double is not 8 bytes");

View File

@ -2232,7 +2232,7 @@ AqlValue Functions::Neighbors(arangodb::aql::Query* query,
size_t split;
char const* str = vertexId.c_str();
if (!TRI_ValidateDocumentIdKeyGenerator(str, &split)) {
if (!TRI_ValidateDocumentIdKeyGenerator(str, vertexId.size(), &split)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD);
}

View File

@ -422,6 +422,10 @@ void RestVocbaseBaseHandler::generateTransactionError(
generateError(HttpResponse::BAD, res, "invalid document key");
return;
case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE:
generateError(HttpResponse::BAD, res, "invalid edge attribute");
return;
case TRI_ERROR_ARANGO_OUT_OF_KEYS:
generateError(HttpResponse::SERVER_ERROR, res, "out of keys");
return;
@ -505,6 +509,10 @@ void RestVocbaseBaseHandler::generateTransactionError(
generateError(HttpResponse::BAD, result.code, "invalid document key");
return;
case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE:
generateError(HttpResponse::BAD, result.code, "invalid edge attribute");
return;
case TRI_ERROR_ARANGO_OUT_OF_KEYS:
generateError(HttpResponse::SERVER_ERROR, result.code, "out of keys");
return;

View File

@ -686,44 +686,6 @@ OperationResult Transaction::insert(std::string const& collectionName,
}
// Validate Edges
if (isEdgeCollection(collectionName)) {
// Check _from
auto checkFrom = [&](VPackSlice const value) -> void {
size_t split;
VPackSlice from = value.get(TRI_VOC_ATTRIBUTE_FROM);
if (!from.isString()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE);
}
std::string docId = from.copyString();
if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE);
}
};
// Check _to
auto checkTo = [&](VPackSlice const value) -> void {
size_t split;
VPackSlice to = value.get(TRI_VOC_ATTRIBUTE_TO);
if (!to.isString()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE);
}
std::string docId = to.copyString();
if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE);
}
};
if (value.isArray()) {
for (auto s : VPackArrayIterator(value)) {
checkFrom(s);
checkTo(s);
}
} else {
checkFrom(value);
checkTo(value);
}
}
OperationOptions optionsCopy = options;
if (ServerState::instance()->isCoordinator()) {
@ -811,47 +773,8 @@ OperationResult Transaction::insertLocal(std::string const& collectionName,
if (!value.isObject()) {
return TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
}
// add missing attributes for document (_id, _rev, _key)
VPackBuilder merge;
merge.openObject();
// generate a new tick value
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
std::string keyString;
auto key = value.get(TRI_VOC_ATTRIBUTE_KEY);
if (key.isNone()) {
// "_key" attribute not present in object
keyString = document->_keyGenerator->generate(revisionId);
merge.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(keyString));
} else if (!key.isString()) {
// "_key" present but wrong type
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
} else {
keyString = key.copyString();
int res = document->_keyGenerator->validate(keyString, false);
if (res != TRI_ERROR_NO_ERROR) {
// invalid key value
return res;
}
}
// add _rev attribute
merge.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId)));
// add _id attribute
uint8_t* p = merge.add(TRI_VOC_ATTRIBUTE_ID, VPackValuePair(9ULL, VPackValueType::Custom));
*p++ = 0xf3; // custom type for _id
DatafileHelper::StoreNumber<uint64_t>(p, cid, sizeof(uint64_t));
merge.close();
VPackBuilder toInsert = VPackCollection::merge(value, merge.slice(), false, false);
VPackSlice insertSlice = toInsert.slice();
TRI_doc_mptr_t mptr;
int res = document->insert(this, insertSlice, &mptr, options,
int res = document->insert(this, value, &mptr, options,
!isLocked(document, TRI_TRANSACTION_WRITE));
if (res != TRI_ERROR_NO_ERROR) {
@ -865,6 +788,9 @@ OperationResult Transaction::insertLocal(std::string const& collectionName,
TRI_ASSERT(mptr.getDataPtr() != nullptr);
std::string keyString
= VPackSlice(mptr.vpack()).get(TRI_VOC_ATTRIBUTE_KEY).copyString();
buildDocumentIdentity(resultBuilder, cid, keyString,
mptr.revisionIdAsSlice(), VPackSlice(),
nullptr, options.returnNew ? &mptr : nullptr);

View File

@ -85,7 +85,7 @@ static bool ParseDocumentHandle(v8::Handle<v8::Value> const arg,
// collection name / document key
size_t split;
if (TRI_ValidateDocumentIdKeyGenerator(*str, &split)) {
if (TRI_ValidateDocumentIdKeyGenerator(*str, str.length(), &split)) {
collectionName = std::string(*str, split);
auto const length = str.length() - split - 1;
auto buffer = new char[length + 1];
@ -96,7 +96,7 @@ static bool ParseDocumentHandle(v8::Handle<v8::Value> const arg,
}
// document key only
if (TraditionalKeyGenerator::validateKey(*str)) {
if (TraditionalKeyGenerator::validateKey(*str, str.length())) {
auto const length = str.length();
auto buffer = new char[length + 1];
memcpy(buffer, *str, length);

View File

@ -231,23 +231,21 @@ TraditionalKeyGenerator::~TraditionalKeyGenerator() {}
/// @brief validate a key
////////////////////////////////////////////////////////////////////////////////
bool TraditionalKeyGenerator::validateKey(char const* key) {
bool TraditionalKeyGenerator::validateKey(char const* key, size_t len) {
unsigned char const* p = reinterpret_cast<unsigned char const*>(key);
unsigned char const* s = p;
size_t pos = 0;
while (true) {
unsigned char c = *p;
if (c == '\0') {
return ((p - s) > 0) && ((p - s) <= TRI_VOC_KEY_MAX_LENGTH);
if (pos >= len || *p == '\0') {
return (pos > 0) && (pos <= TRI_VOC_KEY_MAX_LENGTH);
}
if (LookupTable[c]) {
++p;
continue;
if (!LookupTable[*p]) {
return false;
}
return false;
++p;
++pos;
}
}
@ -271,7 +269,7 @@ int TraditionalKeyGenerator::validate(std::string const& key, bool isRestore) {
}
// validate user-supplied key
if (!validateKey(key.c_str())) {
if (!validateKey(key.c_str(), key.size())) {
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
@ -320,22 +318,21 @@ AutoIncrementKeyGenerator::~AutoIncrementKeyGenerator() {}
/// @brief validate a numeric key
////////////////////////////////////////////////////////////////////////////////
bool AutoIncrementKeyGenerator::validateKey(char const* key) {
bool AutoIncrementKeyGenerator::validateKey(char const* key, size_t len) {
char const* p = key;
size_t pos = 0;
while (true) {
char c = *p;
if (c == '\0') {
return ((p - key) > 0) && ((p - key) <= TRI_VOC_KEY_MAX_LENGTH);
if (pos >= len || *p == '\0') {
return (pos > 0) && (pos <= TRI_VOC_KEY_MAX_LENGTH);
}
if (c >= '0' && c <= '9') {
++p;
continue;
if (*p < '0' || *p > '9') {
return false;
}
return false;
++p;
++pos;
}
}
@ -383,7 +380,7 @@ int AutoIncrementKeyGenerator::validate(std::string const& key,
}
// validate user-supplied key
if (!validateKey(key.c_str())) {
if (!validateKey(key.c_str(), key.size())) {
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
@ -428,9 +425,15 @@ void AutoIncrementKeyGenerator::toVelocyPack(VPackBuilder& builder) const {
/// @brief validate a document id (collection name + / + document key)
////////////////////////////////////////////////////////////////////////////////
bool TRI_ValidateDocumentIdKeyGenerator(char const* key, size_t* split) {
bool TRI_ValidateDocumentIdKeyGenerator(char const* key, size_t len,
size_t* split) {
if (len == 0) {
return false;
}
char const* p = key;
char c = *p;
size_t pos = 0;
// extract collection name
if (!(c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
@ -439,13 +442,18 @@ bool TRI_ValidateDocumentIdKeyGenerator(char const* key, size_t* split) {
}
++p;
++pos;
while (true) {
if (pos >= len) {
return false;
}
while (1) {
c = *p;
if (c == '_' || c == '-' || (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
++p;
++pos;
continue;
}
@ -456,14 +464,15 @@ bool TRI_ValidateDocumentIdKeyGenerator(char const* key, size_t* split) {
return false;
}
if (static_cast<size_t>(p - key) > TRI_COL_NAME_LENGTH) {
if (pos > TRI_COL_NAME_LENGTH) {
return false;
}
// store split position
*split = p - key;
*split = pos;
++p;
++pos;
// validate document key
return TraditionalKeyGenerator::validateKey(p);
return TraditionalKeyGenerator::validateKey(p, len - pos);
}

View File

@ -157,7 +157,7 @@ class TraditionalKeyGenerator : public KeyGenerator {
/// @brief validate a key
//////////////////////////////////////////////////////////////////////////////
static bool validateKey(char const* key);
static bool validateKey(char const* key, size_t len);
public:
//////////////////////////////////////////////////////////////////////////////
@ -210,7 +210,7 @@ class AutoIncrementKeyGenerator : public KeyGenerator {
/// @brief validate a key
//////////////////////////////////////////////////////////////////////////////
static bool validateKey(char const* key);
static bool validateKey(char const* key, size_t len);
public:
//////////////////////////////////////////////////////////////////////////////
@ -259,6 +259,6 @@ class AutoIncrementKeyGenerator : public KeyGenerator {
/// @brief validate a document id (collection name + / + document key)
////////////////////////////////////////////////////////////////////////////////
bool TRI_ValidateDocumentIdKeyGenerator(char const*, size_t*);
bool TRI_ValidateDocumentIdKeyGenerator(char const*, size_t, size_t*);
#endif

View File

@ -39,7 +39,7 @@ arangodb::traverser::VertexId arangodb::traverser::IdStringToVertexId(
size_t split;
char const* str = vertex.c_str();
if (!TRI_ValidateDocumentIdKeyGenerator(str, &split)) {
if (!TRI_ValidateDocumentIdKeyGenerator(str, vertex.size(), &split)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD);
}

View File

@ -3272,29 +3272,45 @@ int TRI_document_collection_t::insert(Transaction* trx, VPackSlice const slice,
bool lock) {
if (_info.type() == TRI_COL_TYPE_EDGE) {
// _from:
VPackSlice s = slice.get(TRI_VOC_ATTRIBUTE_FROM);
if (!s.isString()) {
return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE;
}
VPackValueLength len;
char const* docId = s.getString(len);
size_t split;
if (!TRI_ValidateDocumentIdKeyGenerator(docId, len, &split)) {
return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE;
}
// _to:
s = slice.get(TRI_VOC_ATTRIBUTE_TO);
if (!s.isString()) {
return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE;
}
docId = s.getString(len);
if (!TRI_ValidateDocumentIdKeyGenerator(docId, len, &split)) {
return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE;
}
}
uint64_t hash = 0;
VPackBuilder builder;
int res = newObjectForInsert(trx, slice, hash, builder);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
VPackSlice newSlice = builder.slice();
TRI_ASSERT(mptr != nullptr);
mptr->setDataPtr(nullptr);
VPackSlice const key(slice.get(TRI_VOC_ATTRIBUTE_KEY));
uint64_t const hash = key.hash();
std::unique_ptr<arangodb::wal::Marker> marker;
if (options.recoveryMarker == nullptr) {
marker.reset(createVPackInsertMarker(trx, slice));
marker.reset(createVPackInsertMarker(trx, newSlice));
}
TRI_voc_tick_t markerTick = 0;
int res;
// now insert into indexes
{
TRI_IF_FAILURE("InsertDocumentNoLock") {
@ -4034,6 +4050,47 @@ int TRI_document_collection_t::deleteSecondaryIndexes(
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief new object for insert, computes the hash of the key
////////////////////////////////////////////////////////////////////////////////
int TRI_document_collection_t::newObjectForInsert(
Transaction* trx,
VPackSlice const& value,
uint64_t& hash,
VPackBuilder& builder) {
// insert
{ VPackObjectBuilder guard(&builder);
TRI_SanitizeObject(value, builder);
uint8_t* p = builder.add(TRI_VOC_ATTRIBUTE_ID,
VPackValuePair(9ULL, VPackValueType::Custom));
*p++ = 0xf3; // custom type for _id
DatafileHelper::StoreNumber<uint64_t>(p, _info.id(), sizeof(uint64_t));
VPackSlice s = value.get(TRI_VOC_ATTRIBUTE_KEY);
TRI_voc_tick_t const newRev = TRI_NewTickServer();
if (s.isNone()) {
std::string keyString = _keyGenerator->generate(newRev);
uint8_t* where = builder.add(TRI_VOC_ATTRIBUTE_KEY,
VPackValue(keyString));
s = VPackSlice(where); // point to newly built value, the string
} else if (!s.isString()) {
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
} else {
std::string keyString = s.copyString();
int res = _keyGenerator->validate(keyString, false);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
builder.add(TRI_VOC_ATTRIBUTE_KEY, s);
}
hash = s.hash();
std::string rev = std::to_string(newRev);
builder.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(rev));
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief new object for replace, oldValue must have _key and _id correctly
/// set.
@ -4044,7 +4101,6 @@ VPackBuilder TRI_document_collection_t::newObjectForReplace(
VPackSlice const& oldValue,
VPackSlice const& newValue,
std::string const& rev) {
// replace
VPackBuilder builder;
{ VPackObjectBuilder guard(&builder);

View File

@ -253,6 +253,16 @@ struct TRI_document_collection_t : public TRI_collection_t {
int deleteSecondaryIndexes(arangodb::Transaction*, TRI_doc_mptr_t const*,
bool);
////////////////////////////////////////////////////////////////////////////////
/// @brief new object for insert, value must have _key set correctly.
////////////////////////////////////////////////////////////////////////////////
int newObjectForInsert(
arangodb::Transaction* trx,
arangodb::velocypack::Slice const& value,
uint64_t& hash,
arangodb::velocypack::Builder& builder);
////////////////////////////////////////////////////////////////////////////////
/// @brief new object for Replace
////////////////////////////////////////////////////////////////////////////////

View File

@ -2030,6 +2030,274 @@ function DatabaseDocumentSuite () {
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: returnNew and returnOld options
////////////////////////////////////////////////////////////////////////////////
function DatabaseDocumentSuiteReturnStuff () {
'use strict';
var cn = "UnitTestsCollectionBasics";
var ERRORS = require("internal").errors;
var collection = null;
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
db._drop(cn);
collection = db._create(cn, { waitForSync : false });
collection.load();
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
collection.drop();
wait(0.0);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with and without returnNew
////////////////////////////////////////////////////////////////////////////////
testCreateReturnNew : function () {
var res = collection.insert({"Hallo":12});
assertTypeOf("object", res);
assertEqual(3, Object.keys(res).length);
assertTypeOf("string", res._id);
assertTypeOf("string", res._key);
assertTypeOf("string", res._rev);
// Now with returnNew: true
res = collection.insert({"Hallo":12}, {returnNew: true});
assertTypeOf("object", res);
assertEqual(4, Object.keys(res).length);
assertTypeOf("string", res._id);
assertTypeOf("string", res._key);
assertTypeOf("string", res._rev);
assertTypeOf("object", res["new"]);
assertEqual(12, res["new"].Hallo);
assertEqual(4, Object.keys(res["new"]).length);
// Now with returnNew: false
res = collection.insert({"Hallo":12}, {returnNew: false});
assertTypeOf("object", res);
assertEqual(3, Object.keys(res).length);
assertTypeOf("string", res._id);
assertTypeOf("string", res._key);
assertTypeOf("string", res._rev);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief remove with and without returnOld
////////////////////////////////////////////////////////////////////////////////
testRemoveReturnOld : function () {
var res = collection.insert({"Hallo":12});
var res2 = collection.remove(res._key);
assertTypeOf("object", res2);
assertEqual(3, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnOld: true
res = collection.insert({"Hallo":12});
res2 = collection.remove(res._key, {returnOld: true});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2.old);
assertEqual(12, res2.old.Hallo);
assertEqual(4, Object.keys(res2.old).length);
// Now with returnOld: false
res = collection.insert({"Hallo":12});
res2 = collection.remove(res._key, {returnOld: false});
assertTypeOf("object", res2);
assertEqual(3, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief replace with and without returnOld and returnNew
////////////////////////////////////////////////////////////////////////////////
testReplaceReturnOldNew : function () {
var res = collection.insert({"Hallo":12});
var res2 = collection.replace(res._key,{"Hallo":13});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnOld: true
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":13}, {returnOld: true});
assertTypeOf("object", res2);
assertEqual(5, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2.old);
assertEqual(12, res2.old.Hallo);
assertEqual(4, Object.keys(res2.old).length);
// Now with returnOld: false
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":14}, {returnOld: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnNew: true
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":14}, {returnNew: true});
assertTypeOf("object", res2);
assertEqual(5, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2["new"]);
assertEqual(14, res2["new"].Hallo);
assertEqual(4, Object.keys(res2["new"]).length);
// Now with returnOld: false
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":15}, {returnNew: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnNew: true and returnOld:true
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":16},
{returnNew: true, returnOld: true});
assertTypeOf("object", res2);
assertEqual(6, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2.old);
assertTypeOf("object", res2["new"]);
assertEqual(16, res2["new"].Hallo);
assertEqual(12, res2.old.Hallo);
assertEqual(4, Object.keys(res2["new"]).length);
assertEqual(4, Object.keys(res2.old).length);
// Now with returnOld: false and returnNew: false
res = collection.insert({"Hallo":12});
res2 = collection.replace(res._key, {"Hallo":15},
{returnNew: false, returnOld: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief update with and without returnOld and returnNew
////////////////////////////////////////////////////////////////////////////////
testUpdateReturnOldNew : function () {
var res = collection.insert({"Hallo":12});
var res2 = collection.update(res._key,{"Hallo":13});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnOld: true
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":13}, {returnOld: true});
assertTypeOf("object", res2);
assertEqual(5, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2.old);
assertEqual(12, res2.old.Hallo);
assertEqual(4, Object.keys(res2.old).length);
// Now with returnOld: false
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":14}, {returnOld: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnNew: true
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":14}, {returnNew: true});
assertTypeOf("object", res2);
assertEqual(5, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2["new"]);
assertEqual(14, res2["new"].Hallo);
assertEqual(4, Object.keys(res2["new"]).length);
// Now with returnOld: false
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":15}, {returnNew: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
// Now with returnNew: true and returnOld:true
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":16},
{returnNew: true, returnOld: true});
assertTypeOf("object", res2);
assertEqual(6, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
assertTypeOf("object", res2.old);
assertTypeOf("object", res2["new"]);
assertEqual(16, res2["new"].Hallo);
assertEqual(12, res2.old.Hallo);
assertEqual(4, Object.keys(res2["new"]).length);
assertEqual(4, Object.keys(res2.old).length);
// Now with returnOld: false and returnNew: false
res = collection.insert({"Hallo":12});
res2 = collection.update(res._key, {"Hallo":15},
{returnNew: false, returnOld: false});
assertTypeOf("object", res2);
assertEqual(4, Object.keys(res2).length);
assertTypeOf("string", res2._id);
assertTypeOf("string", res2._key);
assertTypeOf("string", res2._rev);
}
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
@ -2041,5 +2309,7 @@ jsunity.run(CollectionDocumentSuite);
jsunity.run(DatabaseDocumentSuiteErrorHandling);
jsunity.run(DatabaseDocumentSuite);
jsunity.run(DatabaseDocumentSuiteReturnStuff);
return jsunity.done();