mirror of https://gitee.com/bigwinds/arangodb
added collection.checksum()
This commit is contained in:
parent
25a8ab6c59
commit
3cc4405c18
|
@ -1,6 +1,13 @@
|
|||
v1.4
|
||||
----
|
||||
|
||||
* added collection.checksum(<withData>) method to calculate CRC checksums for
|
||||
collections
|
||||
|
||||
This can be used to
|
||||
- check if data in a collection has changed
|
||||
- compare the contents of two collections on different ArangoDB instances
|
||||
|
||||
* issue #565: add description line to aal.listAvailable()
|
||||
|
||||
* fixed several out-of-memory situations when double freeing or invalid memory
|
||||
|
|
|
@ -380,9 +380,9 @@ describe ArangoDB do
|
|||
r2 = doc.parsed_response['revision']
|
||||
r2.should_not eq("");
|
||||
r2.should_not eq(r1);
|
||||
|
||||
# truncate
|
||||
doc = ArangoDB.log_put("#{prefix}-get-collection-revision", "/_api/collection/#{@cn}/truncate", :body => "")
|
||||
|
||||
# create another document
|
||||
doc = ArangoDB.log_post("#{prefix}-get-collection-revision", "/_api/document/?collection=" + @cn, :body => body)
|
||||
|
||||
# fetch revision again
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd)
|
||||
|
@ -397,6 +397,124 @@ describe ArangoDB do
|
|||
r3.should_not eq("");
|
||||
r3.should_not eq(r1);
|
||||
r3.should_not eq(r2);
|
||||
|
||||
# truncate
|
||||
doc = ArangoDB.log_put("#{prefix}-get-collection-revision", "/_api/collection/#{@cn}/truncate", :body => "")
|
||||
|
||||
# fetch revision again
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['revision'].should be_kind_of(String)
|
||||
|
||||
r4 = doc.parsed_response['revision']
|
||||
r4.should_not eq("");
|
||||
r4.should_not eq(r1);
|
||||
r4.should_not eq(r2);
|
||||
r4.should_not eq(r3);
|
||||
end
|
||||
|
||||
# checksum
|
||||
it "calculating the checksum for a collection" do
|
||||
cmd = api + "/" + @cn + "/checksum"
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-checksum", cmd)
|
||||
|
||||
# empty collection
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['id'].should eq(@cid)
|
||||
doc.parsed_response['name'].should eq(@cn)
|
||||
doc.parsed_response['status'].should eq(3)
|
||||
r1 = doc.parsed_response['revision']
|
||||
r1.should be_kind_of(String)
|
||||
r1.should_not eq("");
|
||||
c1 = doc.parsed_response['checksum']
|
||||
c1.should be_kind_of(Integer)
|
||||
c1.should eq(0);
|
||||
|
||||
# create a new document
|
||||
body = "{ \"test\" : 1 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-get-collection-checksum", "/_api/document/?collection=" + @cn, :body => body)
|
||||
|
||||
# fetch checksum again
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-checksum", cmd)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
r2 = doc.parsed_response['revision']
|
||||
r2.should_not eq("");
|
||||
r2.should_not eq(r1);
|
||||
c2 = doc.parsed_response['checksum']
|
||||
c2.should be_kind_of(Integer)
|
||||
c2.should_not eq(0);
|
||||
c2.should_not eq(c1);
|
||||
|
||||
# create another document
|
||||
doc = ArangoDB.log_post("#{prefix}-get-collection-checksum", "/_api/document/?collection=" + @cn, :body => body)
|
||||
|
||||
# fetch checksum again
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-checksum", cmd)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
r3 = doc.parsed_response['revision']
|
||||
r3.should_not eq("");
|
||||
r3.should_not eq(r1);
|
||||
r3.should_not eq(r2);
|
||||
c3 = doc.parsed_response['checksum']
|
||||
c3.should be_kind_of(Integer)
|
||||
c3.should_not eq(0);
|
||||
c3.should_not eq(c1);
|
||||
c3.should_not eq(c2);
|
||||
|
||||
# fetch checksum <withData>
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-checksum", cmd + "?withData=true")
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
r4 = doc.parsed_response['revision']
|
||||
r4.should eq(r3);
|
||||
c4 = doc.parsed_response['checksum']
|
||||
c4.should be_kind_of(Integer)
|
||||
c4.should_not eq(0);
|
||||
c4.should_not eq(c1);
|
||||
c4.should_not eq(c2);
|
||||
c4.should_not eq(c3);
|
||||
|
||||
# truncate
|
||||
doc = ArangoDB.log_put("#{prefix}-get-collection-checksum", "/_api/collection/#{@cn}/truncate", :body => "")
|
||||
|
||||
# fetch checksum again
|
||||
doc = ArangoDB.log_get("#{prefix}-get-collection-checksum", cmd)
|
||||
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
r5 = doc.parsed_response['revision']
|
||||
r5.should_not eq("");
|
||||
r5.should_not eq(r1);
|
||||
r5.should_not eq(r2);
|
||||
r5.should_not eq(r3);
|
||||
r5.should_not eq(r4);
|
||||
c5 = doc.parsed_response['checksum']
|
||||
c5.should be_kind_of(Integer)
|
||||
c5.should eq(0);
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -73,9 +73,9 @@ namespace triagens {
|
|||
/// @brief create the barrier
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Barrier (TRI_primary_collection_t* const doc) :
|
||||
Barrier (TRI_primary_collection_t* const primary) :
|
||||
_barrier(0) {
|
||||
_barrier = TRI_CreateBarrierElement(&doc->_barrierList);
|
||||
_barrier = TRI_CreateBarrierElement(&primary->_barrierList);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "BasicsC/logging.h"
|
||||
#include "BasicsC/random.h"
|
||||
#include "BasicsC/string-buffer.h"
|
||||
#include "GeoIndex/geo-index.h"
|
||||
#include "HashIndex/hash-index.h"
|
||||
#include "FulltextIndex/fulltext-index.h"
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include "FulltextIndex/fulltext-query.h"
|
||||
#include "SkipLists/skiplistIndex.h"
|
||||
#include "Utilities/ResourceHolder.h"
|
||||
#include "Utils/Barrier.h"
|
||||
#include "Utils/CollectionNameResolver.h"
|
||||
#include "Utils/EmbeddableTransaction.h"
|
||||
#include "Utils/SingleCollectionReadOnlyTransaction.h"
|
||||
|
@ -1019,7 +1021,7 @@ static v8::Handle<v8::Value> ExecuteSkiplistQuery (v8::Arguments const& argv,
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot execute skiplist query");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -1210,7 +1212,7 @@ static v8::Handle<v8::Value> ExecuteBitarrayQuery (v8::Arguments const& argv,
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch documents");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
TRI_primary_collection_t* primary = trx.primaryCollection();
|
||||
|
@ -1504,7 +1506,7 @@ static v8::Handle<v8::Value> EdgesQuery (TRI_edge_direction_e direction, v8::Arg
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch edges");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
TRI_primary_collection_t* primary = trx.primaryCollection();
|
||||
|
@ -1713,14 +1715,14 @@ static v8::Handle<v8::Value> JS_AllQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch documents");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
res = trx.read(docs, &barrier, skip, limit, &total);
|
||||
res = trx.finish(res);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch documents");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
const size_t n = docs.size();
|
||||
|
@ -1791,14 +1793,14 @@ static v8::Handle<v8::Value> JS_OffsetQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch documents");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
res = trx.readOffset(docs, &barrier, internalSkip, batchSize, skip, &total);
|
||||
res = trx.finish(res);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch documents");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
const size_t n = docs.size();
|
||||
|
@ -1869,7 +1871,7 @@ static v8::Handle<v8::Value> JS_AnyQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch document");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
res = trx.readRandom(&document, &barrier);
|
||||
|
@ -1879,7 +1881,8 @@ static v8::Handle<v8::Value> JS_AnyQuery (v8::Arguments const& argv) {
|
|||
if (barrier != 0) {
|
||||
TRI_FreeBarrier(barrier);
|
||||
}
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot fetch document");
|
||||
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
if (document._data == 0 || document._key == 0) {
|
||||
|
@ -1929,7 +1932,7 @@ static v8::Handle<v8::Value> JS_ByExampleQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot query by example");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
TRI_primary_collection_t* primary = trx.primaryCollection();
|
||||
|
@ -2156,7 +2159,7 @@ static v8::Handle<v8::Value> JS_ByExampleHashIndex (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot query by example");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -2218,6 +2221,135 @@ static v8::Handle<v8::Value> JS_ByConditionBitarray (v8::Arguments const& argv)
|
|||
return ExecuteBitarrayQuery(argv, signature, QUERY_CONDITION);
|
||||
}
|
||||
|
||||
typedef struct collection_checksum_s {
|
||||
uint32_t _checksum;
|
||||
TRI_string_buffer_t _buffer;
|
||||
}
|
||||
collection_checksum_t;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief callback for checksum calculation, WD = with data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<bool WD> static bool ChecksumCalculator (TRI_doc_mptr_t const* mptr,
|
||||
TRI_primary_collection_t* primary,
|
||||
void* data) {
|
||||
TRI_df_marker_t const* marker = static_cast<TRI_df_marker_t const*>(mptr->_data);
|
||||
collection_checksum_t* helper = static_cast<collection_checksum_t*>(data);
|
||||
uint32_t localCrc;
|
||||
|
||||
if (marker->_type == TRI_DOC_MARKER_KEY_DOCUMENT) {
|
||||
// must convert _rid into string for portability (little vs. big endian etc.)
|
||||
const string key = string(mptr->_key) + StringUtils::itoa(mptr->_rid);
|
||||
localCrc = TRI_Crc32HashPointer(key.c_str(), key.size());
|
||||
}
|
||||
else if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) {
|
||||
TRI_doc_edge_key_marker_t const* e = (TRI_doc_edge_key_marker_t const*) marker;
|
||||
|
||||
// must convert _rid, _fromCid, _toCid into strings for portability
|
||||
const string key = string(mptr->_key) + StringUtils::itoa(mptr->_rid) +
|
||||
StringUtils::itoa(e->_toCid) + string(((char*) marker) + e->_offsetToKey) +
|
||||
StringUtils::itoa(e->_fromCid) + string(((char*) marker) + e->_offsetFromKey);
|
||||
|
||||
localCrc = TRI_Crc32HashPointer(key.c_str(), key.size());
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WD) {
|
||||
// with data
|
||||
TRI_doc_document_key_marker_t const* d = (TRI_doc_document_key_marker_t const*) marker;
|
||||
|
||||
TRI_shaped_json_t shaped;
|
||||
TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, d);
|
||||
|
||||
TRI_StringifyArrayShapedJson(primary->_shaper, &helper->_buffer, &shaped, false);
|
||||
localCrc += TRI_Crc32HashPointer(TRI_BeginStringBuffer(&helper->_buffer), TRI_LengthStringBuffer(&helper->_buffer));
|
||||
TRI_ResetStringBuffer(&helper->_buffer);
|
||||
}
|
||||
|
||||
helper->_checksum += localCrc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief calculates a checksum for the data in a collection
|
||||
///
|
||||
/// @FUN{@FA{collection}.checksum(@FA{withData})}
|
||||
///
|
||||
/// The @FN{checksum} operation calculates a CRC32 checksum of the meta-data
|
||||
/// (keys and revision ids) contained in collection @FA{collection}.
|
||||
///
|
||||
/// If the optional argument @FA{withData} is set to @LIT{true}, then the
|
||||
/// actual document data is also checksummed. Including the document data in
|
||||
/// checksumming will make the calculation slower, but is more accurate.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_ChecksumCollection (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
TRI_vocbase_col_t const* col;
|
||||
col = TRI_UnwrapClass<TRI_vocbase_col_t>(argv.Holder(), TRI_GetVocBaseColType());
|
||||
|
||||
if (col == 0) {
|
||||
TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection");
|
||||
}
|
||||
|
||||
bool withData = false;
|
||||
if (argv.Length() > 0) {
|
||||
withData = TRI_ObjectToBoolean(argv[0]);
|
||||
}
|
||||
|
||||
CollectionNameResolver resolver(col->_vocbase);
|
||||
ReadTransactionType trx(col->_vocbase, resolver, col->_cid);
|
||||
|
||||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
TRI_primary_collection_t* primary = trx.primaryCollection();
|
||||
|
||||
Barrier barrier(primary);
|
||||
|
||||
collection_checksum_t helper;
|
||||
helper._checksum = 0;
|
||||
|
||||
// .............................................................................
|
||||
// inside a read transaction
|
||||
// .............................................................................
|
||||
|
||||
trx.lockRead();
|
||||
// get last tick
|
||||
const string tick = StringUtils::itoa(primary->base._info._tick);
|
||||
|
||||
if (withData) {
|
||||
TRI_InitStringBuffer(&helper._buffer, TRI_CORE_MEM_ZONE);
|
||||
|
||||
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<true>);
|
||||
|
||||
TRI_DestroyStringBuffer(&helper._buffer);
|
||||
}
|
||||
else {
|
||||
TRI_DocumentIteratorPrimaryCollection(primary, &helper, &ChecksumCalculator<false>);
|
||||
}
|
||||
|
||||
trx.finish(res);
|
||||
|
||||
// .............................................................................
|
||||
// outside a write transaction
|
||||
// .............................................................................
|
||||
|
||||
v8::Handle<v8::Object> result = v8::Object::New();
|
||||
result->Set(v8::String::New("checksum"), v8::Number::New(helper._checksum));
|
||||
result->Set(v8::String::New("revision"), v8::String::New(tick.c_str(), tick.size()));
|
||||
|
||||
return scope.Close(result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief selects all edges for a set of vertices
|
||||
///
|
||||
|
@ -2304,7 +2436,7 @@ static v8::Handle<v8::Value> FulltextQuery (ReadTransactionType& trx,
|
|||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_FreeQueryFulltextIndex(query);
|
||||
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "invalid value for <query>");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
TRI_fulltext_index_t* fulltextIndex = (TRI_fulltext_index_t*) idx;
|
||||
|
@ -2312,7 +2444,7 @@ static v8::Handle<v8::Value> FulltextQuery (ReadTransactionType& trx,
|
|||
if (isSubstringQuery && ! fulltextIndex->_indexSubstrings) {
|
||||
TRI_FreeQueryFulltextIndex(query);
|
||||
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "index does not support substring matching");
|
||||
TRI_V8_EXCEPTION(scope, TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
TRI_fulltext_result_t* queryResult = TRI_QueryFulltextIndex(fulltextIndex->_fulltextIndex, query);
|
||||
|
@ -2393,7 +2525,7 @@ static v8::Handle<v8::Value> JS_FulltextQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot execute fulltext query");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -2465,7 +2597,7 @@ static v8::Handle<v8::Value> NearQuery (ReadTransactionType& trx,
|
|||
int res = StoreGeoResult(trx, collection, cors, documents, distances);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot add document to geo-index");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2492,7 +2624,7 @@ static v8::Handle<v8::Value> JS_NearQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot execute near query");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -2560,7 +2692,7 @@ static v8::Handle<v8::Value> JS_TopQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot execute pqueue query");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -2655,7 +2787,7 @@ static v8::Handle<v8::Value> WithinQuery (ReadTransactionType& trx,
|
|||
int res = StoreGeoResult(trx, collection, cors, documents, distances);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot add document to geo-index");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2682,7 +2814,7 @@ static v8::Handle<v8::Value> JS_WithinQuery (v8::Arguments const& argv) {
|
|||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot execute within query");
|
||||
TRI_V8_EXCEPTION(scope, res);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> err;
|
||||
|
@ -2749,6 +2881,7 @@ void TRI_InitV8Queries (v8::Handle<v8::Context> context) {
|
|||
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_BITARRAY", JS_ByExampleBitarray);
|
||||
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_HASH", JS_ByExampleHashIndex);
|
||||
TRI_AddMethodVocbase(rt, "BY_EXAMPLE_SKIPLIST", JS_ByExampleSkiplist);
|
||||
TRI_AddMethodVocbase(rt, "checksum", JS_ChecksumCollection);
|
||||
TRI_AddMethodVocbase(rt, "edges", JS_EdgesQuery);
|
||||
TRI_AddMethodVocbase(rt, "FULLTEXT", JS_FulltextQuery);
|
||||
TRI_AddMethodVocbase(rt, "inEdges", JS_InEdgesQuery);
|
||||
|
|
|
@ -196,33 +196,27 @@ static int32_t const WRP_SHAPED_JSON_TYPE = 4;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a v8 string value from an internal uint64_t id value
|
||||
/// @brief create a v8 tick id value from the internal tick id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline v8::Handle<v8::Value> V8StringId (const uint64_t id) {
|
||||
static inline v8::Handle<v8::Value> V8TickId (const TRI_voc_tick_t tick) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
const string idStr = StringUtils::itoa(id);
|
||||
const string id = StringUtils::itoa(tick);
|
||||
|
||||
v8::Handle<v8::Value> result = v8::String::New(idStr.c_str(), idStr.size());
|
||||
|
||||
return scope.Close(result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a v8 collection id value from the internal collection id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline v8::Handle<v8::Value> V8CollectionId (const uint64_t cid) {
|
||||
return V8StringId(cid);
|
||||
return scope.Close(v8::String::New(id.c_str(), id.size()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a v8 revision id value from the internal revision id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline v8::Handle<v8::Value> V8RevisionId (const uint64_t rid) {
|
||||
return V8StringId(rid);
|
||||
static inline v8::Handle<v8::Value> V8RevisionId (const TRI_voc_rid_t rid) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
const string id = StringUtils::itoa(rid);
|
||||
|
||||
return scope.Close(v8::String::New(id.c_str(), id.size()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -235,9 +229,7 @@ static inline v8::Handle<v8::Value> V8DocumentId (const string& collectionName,
|
|||
|
||||
const string id = DocumentHelper::assembleDocumentId(collectionName, key);
|
||||
|
||||
v8::Handle<v8::Value> result = v8::String::New(id.c_str(), id.size());
|
||||
|
||||
return scope.Close(result);
|
||||
return scope.Close(v8::String::New(id.c_str(), id.size()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2595,7 +2587,7 @@ static v8::Handle<v8::Value> JS_IdGeneralCursor (v8::Arguments const& argv) {
|
|||
TRI_shadow_id id = TRI_GetIdDataShadowData(vocbase->_cursors, UnwrapGeneralCursor(argv.Holder()));
|
||||
|
||||
if (id != 0) {
|
||||
return scope.Close(V8StringId(id));
|
||||
return scope.Close(V8TickId(id));
|
||||
}
|
||||
|
||||
TRI_V8_EXCEPTION(scope, TRI_ERROR_CURSOR_NOT_FOUND);
|
||||
|
@ -7724,7 +7716,9 @@ v8::Handle<v8::Object> TRI_WrapCollection (TRI_vocbase_col_t const* collection)
|
|||
const_cast<TRI_vocbase_col_t*>(collection));
|
||||
|
||||
if (! result.IsEmpty()) {
|
||||
result->Set(v8g->_IdKey, V8CollectionId(collection->_cid), v8::ReadOnly);
|
||||
const string cidString = StringUtils::itoa(collection->_cid);
|
||||
|
||||
result->Set(v8g->_IdKey, v8::String::New(cidString.c_str(), cidString.size()), v8::ReadOnly);
|
||||
}
|
||||
|
||||
return scope.Close(result);
|
||||
|
|
|
@ -761,8 +761,7 @@ void TRI_DebugDatafileInfoPrimaryCollection (TRI_primary_collection_t* primary)
|
|||
|
||||
size_t TRI_DocumentIteratorPrimaryCollection (TRI_primary_collection_t* primary,
|
||||
void* data,
|
||||
bool (*callback)(TRI_doc_mptr_t const*,
|
||||
TRI_primary_collection_t*, void*)) {
|
||||
bool (*callback)(TRI_doc_mptr_t const*, TRI_primary_collection_t*, void*)) {
|
||||
if (primary->_primaryIndex._nrUsed > 0) {
|
||||
void** ptr = primary->_primaryIndex._table;
|
||||
void** end = ptr + primary->_primaryIndex._nrAlloc;
|
||||
|
|
|
@ -379,6 +379,22 @@ ArangoCollection.prototype.figures = function () {
|
|||
return requestResult.figures;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets the checksum of a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.checksum = function (withData) {
|
||||
var append = withData ? "?withData=true" : "";
|
||||
var requestResult = this._database._connection.GET(this._baseurl("checksum") + append);
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return {
|
||||
checksum: requestResult.checksum,
|
||||
revision: requestResult.revision
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets the revision id of a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -312,7 +312,7 @@ function get_api_collections (req, res) {
|
|||
|
||||
excludeSystem = false;
|
||||
if (req.parameters.hasOwnProperty('excludeSystem')) {
|
||||
var value = req.parameters.excludeSystem;
|
||||
var value = req.parameters.excludeSystem.toLowerCase();
|
||||
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
||||
excludeSystem = true;
|
||||
}
|
||||
|
@ -586,6 +586,50 @@ function get_api_collections (req, res) {
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// @RESTHEADER{GET /_api/collection/{collection-name}/checksum, returns a checksum for the collection
|
||||
///
|
||||
/// @RESTURLPARAMETERS
|
||||
///
|
||||
/// @RESTURLPARAM{collection-name,string,required}
|
||||
///
|
||||
/// @RESTURLPARAM{withData,boolean,optional}
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
/// Will calculate checksum of the meta-data (keys and revision ids) and
|
||||
/// optionally document data in the collection.
|
||||
///
|
||||
/// The checksum can be used to compare if two collections on different ArangoDB
|
||||
/// instances contain the same contents. The current revision of the collection is
|
||||
/// returned too so one can make sure the checksums are calculated for the same
|
||||
/// state of data.
|
||||
///
|
||||
/// By default, the checksum will only be calculated on the `_key` and `_rev`
|
||||
/// system attributes of the documents contained in the collection.
|
||||
/// For edge collections, the system attributes `_from` and `_to` will also be
|
||||
/// included in the calculation.
|
||||
///
|
||||
/// By providing the optional URL parameter `withData` with a value of `true`,
|
||||
/// the user-defined document attributes will be included in the calculation too.
|
||||
/// Note that including user-defined attributes will make the checksumming slower.
|
||||
///
|
||||
/// - `checksum`: The calculated checksum as a number.
|
||||
///
|
||||
/// - `revision`: The collection revision id as a string.
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{400}
|
||||
/// If the `collection-name` is missing, then a `HTTP 400` is
|
||||
/// returned.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// If the `collection-name` is unknown, then a `HTTP 404`
|
||||
/// is returned.
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function get_api_collection (req, res) {
|
||||
var name;
|
||||
var result;
|
||||
|
@ -629,11 +673,31 @@ function get_api_collection (req, res) {
|
|||
if (req.suffix.length === 2) {
|
||||
sub = decodeURIComponent(req.suffix[1]);
|
||||
|
||||
// .............................................................................
|
||||
// /_api/collection/<identifier>/checksum
|
||||
// .............................................................................
|
||||
|
||||
if (sub === "checksum") {
|
||||
var withData = false;
|
||||
if (req.parameters.hasOwnProperty('withData')) {
|
||||
var value = req.parameters.withData.toLowerCase();
|
||||
if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
|
||||
withData = true;
|
||||
}
|
||||
}
|
||||
|
||||
result = collectionRepresentation(collection, false, false, false);
|
||||
var checksum = collection.checksum(withData);
|
||||
result.checksum = checksum.checksum;
|
||||
result.revision = checksum.revision;
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result);
|
||||
}
|
||||
|
||||
// .............................................................................
|
||||
// /_api/collection/<identifier>/figures
|
||||
// .............................................................................
|
||||
|
||||
if (sub === "figures") {
|
||||
else if (sub === "figures") {
|
||||
result = collectionRepresentation(collection, true, true, true);
|
||||
headers = { location : "/" + API + "/" + collection.name() + "/figures" };
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result, headers);
|
||||
|
|
|
@ -378,6 +378,22 @@ ArangoCollection.prototype.figures = function () {
|
|||
return requestResult.figures;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets the checksum of a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.checksum = function (withData) {
|
||||
var append = withData ? "?withData=true" : "";
|
||||
var requestResult = this._database._connection.GET(this._baseurl("checksum") + append);
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return {
|
||||
checksum: requestResult.checksum,
|
||||
revision: requestResult.revision
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets the revision id of a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -830,6 +830,143 @@ function CollectionSuite () {
|
|||
db._drop(cn2);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test checksum
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testChecksum : function () {
|
||||
var cn = "example";
|
||||
|
||||
db._drop(cn);
|
||||
var c1 = db._create(cn);
|
||||
|
||||
// empty collection, checksum should be 0
|
||||
var r1 = c1.checksum();
|
||||
assertTypeOf("string", r1.revision);
|
||||
assertTrue(r1.revision !== "");
|
||||
assertTrue(r1.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r1.checksum);
|
||||
assertEqual(0, r1.checksum);
|
||||
|
||||
// inserting a doc, checksum should change
|
||||
c1.save({ a : 1 });
|
||||
var r2 = c1.checksum();
|
||||
assertNotEqual(r1.revision, r2.revision);
|
||||
assertTypeOf("string", r2.revision);
|
||||
assertTrue(r2.revision !== "");
|
||||
assertTrue(r2.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r2.checksum);
|
||||
assertNotEqual(0, r2.checksum);
|
||||
|
||||
// inserting another doc, checksum should change
|
||||
c1.save({ a : 2 });
|
||||
var r3 = c1.checksum();
|
||||
assertNotEqual(r1.revision, r3.revision);
|
||||
assertNotEqual(r2.revision, r3.revision);
|
||||
assertTypeOf("string", r3.revision);
|
||||
assertTrue(r3.revision !== "");
|
||||
assertTrue(r3.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r3.checksum);
|
||||
assertNotEqual(0, r3.checksum);
|
||||
assertNotEqual(r2.checksum, r3.checksum);
|
||||
|
||||
// test after unloading
|
||||
c1.unload();
|
||||
var r4 = c1.checksum();
|
||||
assertTypeOf("string", r4.revision);
|
||||
assertEqual(r3.revision, r4.revision);
|
||||
assertTypeOf("number", r4.checksum);
|
||||
assertNotEqual(0, r4.checksum);
|
||||
assertEqual(r3.checksum, r4.checksum);
|
||||
|
||||
// test withData
|
||||
var r5 = c1.checksum(true);
|
||||
assertTypeOf("string", r5.revision);
|
||||
assertEqual(r4.revision, r5.revision);
|
||||
assertTypeOf("number", r5.checksum);
|
||||
assertNotEqual(0, r5.checksum);
|
||||
assertNotEqual(r4.checksum, r5.checksum);
|
||||
|
||||
// test after truncation
|
||||
c1.truncate();
|
||||
var r6 = c1.checksum();
|
||||
assertTypeOf("string", r6.revision);
|
||||
assertNotEqual(r4.revision, r6.revision);
|
||||
assertNotEqual(r5.revision, r6.revision);
|
||||
assertTypeOf("number", r6.checksum);
|
||||
assertEqual(0, r6.checksum);
|
||||
|
||||
db._drop(cn);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test checksum
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testChecksumEdge : function () {
|
||||
var cn = "example";
|
||||
var vn = "example2";
|
||||
|
||||
db._drop(cn);
|
||||
db._drop(vn);
|
||||
db._create(vn);
|
||||
var c1 = db._createEdgeCollection(cn);
|
||||
|
||||
var r1 = c1.checksum();
|
||||
assertTypeOf("string", r1.revision);
|
||||
assertTrue(r1.revision !== "");
|
||||
assertTrue(r1.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r1.checksum);
|
||||
assertEqual(0, r1.checksum);
|
||||
|
||||
c1.save(vn + "/1", vn + "/2", { a : 1 });
|
||||
var r2 = c1.checksum();
|
||||
assertNotEqual(r1.revision, r2.revision);
|
||||
assertTypeOf("string", r2.revision);
|
||||
assertTrue(r2.revision !== "");
|
||||
assertTrue(r2.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r2.checksum);
|
||||
assertNotEqual(0, r2.checksum);
|
||||
|
||||
c1.save(vn + "/1", vn + "/2", { a : 2 });
|
||||
var r3 = c1.checksum();
|
||||
assertNotEqual(r1.revision, r3.revision);
|
||||
assertNotEqual(r2.revision, r3.revision);
|
||||
assertTypeOf("string", r3.revision);
|
||||
assertTrue(r3.revision !== "");
|
||||
assertTrue(r3.revision.match(/^[0-9]+$/));
|
||||
assertTypeOf("number", r3.checksum);
|
||||
assertNotEqual(0, r3.checksum);
|
||||
assertNotEqual(r2.checksum, r3.checksum);
|
||||
|
||||
c1.unload();
|
||||
var r4 = c1.checksum();
|
||||
assertTypeOf("string", r4.revision);
|
||||
assertEqual(r3.revision, r4.revision);
|
||||
assertTypeOf("number", r4.checksum);
|
||||
assertEqual(r3.checksum, r4.checksum);
|
||||
|
||||
// test withData
|
||||
var r5 = c1.checksum(true);
|
||||
assertTypeOf("string", r5.revision);
|
||||
assertEqual(r4.revision, r5.revision);
|
||||
assertTypeOf("number", r5.checksum);
|
||||
assertNotEqual(0, r5.checksum);
|
||||
assertNotEqual(r4.checksum, r5.checksum);
|
||||
|
||||
// test after truncation
|
||||
c1.truncate();
|
||||
var r6 = c1.checksum();
|
||||
assertTypeOf("string", r6.revision);
|
||||
assertNotEqual(r4.revision, r6.revision);
|
||||
assertNotEqual(r5.revision, r6.revision);
|
||||
assertTypeOf("number", r6.checksum);
|
||||
assertEqual(0, r6.checksum);
|
||||
|
||||
db._drop(cn);
|
||||
db._drop(vn);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test revision id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -864,6 +1001,8 @@ function CollectionSuite () {
|
|||
var r4 = c1.revision();
|
||||
assertTypeOf("string", r4);
|
||||
assertEqual(r3, r4);
|
||||
|
||||
db._drop(cn);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -300,7 +300,7 @@ void TRI_IncreaseLengthStringBuffer (TRI_string_buffer_t * self, size_t n) {
|
|||
/// @brief returns true if buffer is empty
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_EmptyStringBuffer (TRI_string_buffer_t const * self) {
|
||||
bool TRI_EmptyStringBuffer (TRI_string_buffer_t const* self) {
|
||||
return self->_buffer == self->_current;
|
||||
}
|
||||
|
||||
|
@ -308,13 +308,27 @@ bool TRI_EmptyStringBuffer (TRI_string_buffer_t const * self) {
|
|||
/// @brief clears the buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_ClearStringBuffer (TRI_string_buffer_t * self) {
|
||||
void TRI_ClearStringBuffer (TRI_string_buffer_t* self) {
|
||||
if (self->_buffer != NULL) {
|
||||
self->_current = self->_buffer;
|
||||
memset(self->_buffer, 0, self->_len + 1);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief resets the buffer (without clearing)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_ResetStringBuffer (TRI_string_buffer_t* self) {
|
||||
if (self->_buffer != NULL) {
|
||||
self->_current = self->_buffer;
|
||||
|
||||
if (self->_len > 0) {
|
||||
*self->_current = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the character at the end of the string buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -180,7 +180,13 @@ bool TRI_EmptyStringBuffer (TRI_string_buffer_t const * self);
|
|||
/// @brief clears the buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_ClearStringBuffer (TRI_string_buffer_t * self);
|
||||
void TRI_ClearStringBuffer (TRI_string_buffer_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief resets the buffer (without clearing)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_ResetStringBuffer (TRI_string_buffer_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the character at the end of the string buffer
|
||||
|
|
Loading…
Reference in New Issue