mirror of https://gitee.com/bigwinds/arangodb
First working version of arangorestore for clusters.
This commit is contained in:
parent
d51c730138
commit
19289b6d37
|
@ -101,6 +101,14 @@ namespace triagens {
|
|||
return triagens::basics::JsonHelper::stringUInt64(_json, "id");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns the collection id as a string
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string id_as_string () const {
|
||||
return triagens::basics::JsonHelper::getStringValue(_json, "id", "");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns the collection name
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "VocBase/replication-logger.h"
|
||||
#include "VocBase/server.h"
|
||||
#include "VocBase/update-policy.h"
|
||||
#include "VocBase/index.h"
|
||||
|
||||
#ifdef TRI_ENABLE_CLUSTER
|
||||
#include "Cluster/ClusterMethods.h"
|
||||
|
@ -2158,7 +2159,7 @@ int RestReplicationHandler::processRestoreCollectionCoordinator (
|
|||
return TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
TRI_json_t const* parameters = JsonHelper::getArrayElement(collection, "parameters");
|
||||
TRI_json_t* parameters = JsonHelper::getArrayElement(collection, "parameters");
|
||||
|
||||
if (! JsonHelper::isArray(parameters)) {
|
||||
errorMsg = "collection parameters declaration is invalid";
|
||||
|
@ -2166,14 +2167,6 @@ int RestReplicationHandler::processRestoreCollectionCoordinator (
|
|||
return TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
TRI_json_t const* indexes = JsonHelper::getArrayElement(collection, "indexes");
|
||||
|
||||
if (! JsonHelper::isList(indexes)) {
|
||||
errorMsg = "collection indexes declaration is invalid";
|
||||
|
||||
return TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
const string name = JsonHelper::getStringValue(parameters, "name", "");
|
||||
|
||||
if (name.empty()) {
|
||||
|
@ -2187,56 +2180,24 @@ int RestReplicationHandler::processRestoreCollectionCoordinator (
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
TRI_vocbase_col_t* col = 0;
|
||||
string dbName = _vocbase->_name;
|
||||
|
||||
if (reuseId) {
|
||||
TRI_json_t const* idString = JsonHelper::getArrayElement(parameters, "cid");
|
||||
|
||||
if (! JsonHelper::isString(idString)) {
|
||||
errorMsg = "collection id is missing";
|
||||
|
||||
return TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
TRI_voc_cid_t cid = StringUtils::uint64(idString->_value._string.data, idString->_value._string.length - 1);
|
||||
|
||||
// first look up the collection by the cid
|
||||
col = TRI_LookupCollectionByIdVocBase(_vocbase, cid);
|
||||
}
|
||||
|
||||
|
||||
if (col == 0) {
|
||||
// not found, try name next
|
||||
col = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
|
||||
}
|
||||
// in a cluster, we only look up by name:
|
||||
ClusterInfo* ci = ClusterInfo::instance();
|
||||
TRI_shared_ptr<CollectionInfo> col = ci->getCollection(dbName, name);
|
||||
|
||||
// drop an existing collection if it exists
|
||||
if (col != 0) {
|
||||
if (! col->empty()) {
|
||||
if (dropExisting) {
|
||||
int res = TRI_DropCollectionVocBase(_vocbase, col, remoteServerId);
|
||||
|
||||
int res = ci->dropCollectionCoordinator(dbName, col->id_as_string(),
|
||||
errorMsg, 0.0);
|
||||
if (res == TRI_ERROR_FORBIDDEN) {
|
||||
// some collections must not be dropped
|
||||
|
||||
// instead, truncate them
|
||||
CollectionNameResolver resolver(_vocbase);
|
||||
SingleCollectionWriteTransaction<EmbeddableTransaction<RestTransactionContext>, UINT64_MAX> trx(_vocbase, resolver, col->_cid);
|
||||
|
||||
res = trx.begin();
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
TRI_barrier_t* barrier = TRI_CreateBarrierElement(&(trx.primaryCollection()->_barrierList));
|
||||
|
||||
if (barrier == 0) {
|
||||
return TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
res = trx.truncate(false);
|
||||
res = trx.finish(res);
|
||||
|
||||
TRI_FreeBarrier(barrier);
|
||||
|
||||
// ...
|
||||
// do a fanout to all shards and truncate them, use col and asyncreq.
|
||||
// return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -2256,7 +2217,78 @@ int RestReplicationHandler::processRestoreCollectionCoordinator (
|
|||
}
|
||||
|
||||
// now re-create the collection
|
||||
int res = createCollection(parameters, &col, reuseId, remoteServerId);
|
||||
// dig out number of shards:
|
||||
TRI_json_t const* shards = JsonHelper::getArrayElement(parameters, "shards");
|
||||
if (0 == shards) {
|
||||
errorMsg = "did not find \"shards\" attribute in parameters";
|
||||
return TRI_ERROR_INTERNAL;
|
||||
}
|
||||
if (! TRI_IsArrayJson(shards)) {
|
||||
errorMsg = "\"shards\" attribute in parameters is not an array";
|
||||
return TRI_ERROR_INTERNAL;
|
||||
}
|
||||
uint64_t numberOfShards = TRI_LengthVector(&shards->_value._objects)/2;
|
||||
|
||||
TRI_voc_tick_t new_id_tick = ci->uniqid(1);
|
||||
string new_id = StringUtils::itoa(new_id_tick);
|
||||
TRI_ReplaceArrayJson(TRI_UNKNOWN_MEM_ZONE, parameters, "id",
|
||||
TRI_CreateString2CopyJson(TRI_UNKNOWN_MEM_ZONE,
|
||||
new_id.c_str(), new_id.size()));
|
||||
|
||||
// Now put in the primary and an edge index if needed:
|
||||
TRI_json_t* indexes = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
if (indexes == 0) {
|
||||
errorMsg = "out of memory";
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// create a dummy primary index
|
||||
TRI_index_t* idx = TRI_CreatePrimaryIndex(0);
|
||||
|
||||
if (idx == 0) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, indexes);
|
||||
errorMsg = "out of memory";
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
TRI_json_t* idxJson = idx->json(idx);
|
||||
TRI_FreeIndex(idx);
|
||||
|
||||
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, indexes, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, idxJson));
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, idxJson);
|
||||
|
||||
TRI_json_t* type = TRI_LookupArrayJson(parameters, "type");
|
||||
TRI_col_type_e collectionType;
|
||||
if (TRI_IsNumberJson(type)) {
|
||||
collectionType = (TRI_col_type_e) type->_value._number;
|
||||
}
|
||||
else {
|
||||
errorMsg = "collection type not given or wrong";
|
||||
return TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (collectionType == TRI_COL_TYPE_EDGE) {
|
||||
// create a dummy edge index
|
||||
idx = TRI_CreateEdgeIndex(0, new_id_tick);
|
||||
|
||||
if (idx == 0) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, indexes);
|
||||
errorMsg = "cannot create edge index";
|
||||
return TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
idxJson = idx->json(idx);
|
||||
TRI_FreeIndex(idx);
|
||||
|
||||
TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, indexes, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, idxJson));
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, idxJson);
|
||||
}
|
||||
|
||||
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, parameters, "indexes", indexes);
|
||||
|
||||
int res = ci->createCollectionCoordinator(dbName, new_id, numberOfShards,
|
||||
parameters, errorMsg, 0.0);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
errorMsg = "unable to create collection: " + string(TRI_errno_string(res));
|
||||
|
@ -2422,57 +2454,30 @@ int RestReplicationHandler::processRestoreIndexesCoordinator (
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
// look up the collection
|
||||
TRI_vocbase_col_t* col = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
|
||||
string dbName = _vocbase->_name;
|
||||
|
||||
if (col == 0) {
|
||||
// in a cluster, we only look up by name:
|
||||
ClusterInfo* ci = ClusterInfo::instance();
|
||||
TRI_shared_ptr<CollectionInfo> col = ci->getCollection(dbName, name);
|
||||
|
||||
if (col->empty()) {
|
||||
errorMsg = "could not find collection '" + name + "'";
|
||||
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
|
||||
}
|
||||
|
||||
int res = TRI_UseCollectionVocBase(_vocbase, col);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
|
||||
TRI_primary_collection_t* primary = col->_collection;
|
||||
|
||||
TRI_ReadLockReadWriteLock(&_vocbase->_inventoryLock);
|
||||
|
||||
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(primary);
|
||||
|
||||
int res = TRI_ERROR_NO_ERROR;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
TRI_json_t const* idxDef = (TRI_json_t const*) TRI_AtVector(&indexes->_value._objects, i);
|
||||
TRI_index_t* idx = 0;
|
||||
|
||||
// {"id":"229907440927234","type":"hash","unique":false,"fields":["x","Y"]}
|
||||
|
||||
res = TRI_FromJsonIndexDocumentCollection((TRI_document_collection_t*) primary, idxDef, &idx);
|
||||
|
||||
TRI_json_t* res_json = 0;
|
||||
res = ci->ensureIndexCoordinator(dbName, col->id_as_string(), idxDef,
|
||||
true, IndexComparator, res_json, errorMsg, 3600.0);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
errorMsg = "could not create index: " + string(TRI_errno_string(res));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
assert(idx != 0);
|
||||
|
||||
res = TRI_SaveIndex(primary, idx, remoteServerId);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
errorMsg = "could not save index: " + string(TRI_errno_string(res));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(primary);
|
||||
|
||||
TRI_ReadUnlockReadWriteLock(&_vocbase->_inventoryLock);
|
||||
|
||||
TRI_ReleaseCollectionVocBase(_vocbase, col);
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2825,54 +2830,252 @@ void RestReplicationHandler::handleCommandRestoreData () {
|
|||
|
||||
#ifdef TRI_ENABLE_CLUSTER
|
||||
void RestReplicationHandler::handleCommandRestoreDataCoordinator () {
|
||||
char const* value = _request->value("collection");
|
||||
char const* name = _request->value("collection");
|
||||
|
||||
if (value == 0) {
|
||||
if (name == 0) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
||||
"invalid collection parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
CollectionNameResolver resolver(_vocbase);
|
||||
|
||||
TRI_voc_cid_t cid = resolver.getCollectionId(value);
|
||||
|
||||
if (cid == 0) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
||||
"invalid collection parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
bool recycleIds = false;
|
||||
value = _request->value("recycleIds");
|
||||
if (value != 0) {
|
||||
recycleIds = StringUtils::boolean(value);
|
||||
}
|
||||
|
||||
bool force = false;
|
||||
value = _request->value("force");
|
||||
if (value != 0) {
|
||||
force = StringUtils::boolean(value);
|
||||
}
|
||||
|
||||
TRI_server_id_t remoteServerId = 0; // TODO
|
||||
string dbName = _vocbase->_name;
|
||||
string errorMsg;
|
||||
|
||||
int res = processRestoreData(resolver, cid, remoteServerId, recycleIds, force, errorMsg);
|
||||
// in a cluster, we only look up by name:
|
||||
ClusterInfo* ci = ClusterInfo::instance();
|
||||
TRI_shared_ptr<CollectionInfo> col = ci->getCollection(dbName, name);
|
||||
|
||||
if (col->empty()) {
|
||||
generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to distribute the documents we get over the shards:
|
||||
map<ShardID, ServerID> shardIdsMap = col->shardIds();
|
||||
map<string, size_t> shardTab;
|
||||
vector<string> shardIds;
|
||||
map<ShardID, ServerID>::iterator it;
|
||||
map<string, size_t>::iterator it2;
|
||||
for (it = shardIdsMap.begin(); it != shardIdsMap.end(); it++) {
|
||||
shardTab.insert(make_pair(it->first,shardIds.size()));
|
||||
shardIds.push_back(it->first);
|
||||
}
|
||||
vector<StringBuffer*> bufs;
|
||||
size_t j;
|
||||
for (j = 0; j < shardIds.size(); j++) {
|
||||
bufs.push_back(new StringBuffer(TRI_UNKNOWN_MEM_ZONE));
|
||||
}
|
||||
|
||||
const string invalidMsg = string("received invalid JSON data for collection ")
|
||||
+ name;
|
||||
|
||||
char const* ptr = _request->body();
|
||||
char const* end = ptr + _request->bodySize();
|
||||
|
||||
int res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
while (ptr < end) {
|
||||
char const* pos = strchr(ptr, '\n');
|
||||
|
||||
if (pos == 0) {
|
||||
pos = end;
|
||||
}
|
||||
else {
|
||||
*((char*) pos) = '\0';
|
||||
}
|
||||
|
||||
if (pos - ptr > 1) {
|
||||
// found something
|
||||
TRI_json_t* json = TRI_JsonString(TRI_CORE_MEM_ZONE, ptr);
|
||||
|
||||
if (! JsonHelper::isArray(json)) {
|
||||
if (json != 0) {
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
}
|
||||
|
||||
errorMsg = invalidMsg;
|
||||
|
||||
res = TRI_ERROR_HTTP_CORRUPTED_JSON;
|
||||
break;
|
||||
}
|
||||
|
||||
const char* key = 0;
|
||||
TRI_json_t const* doc = 0;
|
||||
TRI_replication_operation_e type;
|
||||
|
||||
const size_t n = json->_value._objects._length;
|
||||
|
||||
for (size_t i = 0; i < n; i += 2) {
|
||||
TRI_json_t const* element
|
||||
= (TRI_json_t const*) TRI_AtVector(&json->_value._objects, i);
|
||||
|
||||
if (! JsonHelper::isString(element)) {
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
errorMsg = invalidMsg;
|
||||
|
||||
res = TRI_ERROR_HTTP_CORRUPTED_JSON;
|
||||
break;
|
||||
}
|
||||
|
||||
const char* attributeName = element->_value._string.data;
|
||||
TRI_json_t const* value = (TRI_json_t const*) TRI_AtVector(&json->_value._objects, i + 1);
|
||||
|
||||
if (TRI_EqualString(attributeName, "type")) {
|
||||
if (JsonHelper::isNumber(value)) {
|
||||
type = (TRI_replication_operation_e) (int) value->_value._number;
|
||||
}
|
||||
}
|
||||
|
||||
else if (TRI_EqualString(attributeName, "key")) {
|
||||
if (JsonHelper::isString(value)) {
|
||||
key = value->_value._string.data;
|
||||
}
|
||||
}
|
||||
|
||||
else if (TRI_EqualString(attributeName, "data")) {
|
||||
if (JsonHelper::isArray(value)) {
|
||||
doc = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
|
||||
// key must not be 0, but doc can be 0!
|
||||
if (key == 0) {
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
errorMsg = invalidMsg;
|
||||
|
||||
res = TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 != doc && type != MARKER_REMOVE) {
|
||||
ShardID responsibleShard;
|
||||
bool usesDefaultSharding;
|
||||
res = ci->getResponsibleShard(col->id_as_string(), doc, true,
|
||||
responsibleShard, usesDefaultSharding);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
errorMsg = "error during determining responsible shard";
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
it2 = shardTab.find(responsibleShard);
|
||||
if (it2 == shardTab.end()) {
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
errorMsg = "cannot find responsible shard";
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
bufs[it2->second]->appendText(ptr, pos-ptr);
|
||||
bufs[it2->second]->appendText("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == MARKER_REMOVE) {
|
||||
// A remove marker, this has to be appended to all!
|
||||
for (j = 0; j < bufs.size(); j++) {
|
||||
bufs[j]->appendText(ptr, pos-ptr);
|
||||
bufs[j]->appendText("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// How very strange!
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
errorMsg = invalidMsg;
|
||||
|
||||
res = TRI_ERROR_HTTP_BAD_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
}
|
||||
|
||||
ptr = pos + 1;
|
||||
}
|
||||
|
||||
if (res == TRI_ERROR_NO_ERROR) {
|
||||
// Set a few variables needed for our work:
|
||||
ClusterComm* cc = ClusterComm::instance();
|
||||
|
||||
// Send a synchronous request to that shard using ClusterComm:
|
||||
ClusterCommResult* result;
|
||||
CoordTransactionID coordTransactionID = TRI_NewTickServer();
|
||||
|
||||
for (it = shardIdsMap.begin(); it != shardIdsMap.end(); ++it) {
|
||||
map<string, string>* headers = new map<string, string>;
|
||||
it2 = shardTab.find(it->first);
|
||||
if (it2 == shardTab.end()) {
|
||||
errorMsg = "cannot find shard";
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
else {
|
||||
j = it2->second;
|
||||
result = cc->asyncRequest("", coordTransactionID, "shard:" + it->first,
|
||||
triagens::rest::HttpRequest::HTTP_REQUEST_PUT,
|
||||
"/_db/" + StringUtils::urlEncode(dbName) +
|
||||
"/_api/replication/restore-data?collection=" +
|
||||
it->first,
|
||||
new string(bufs[j]->c_str(), bufs[j]->length()),
|
||||
true, headers, NULL, 300.0);
|
||||
delete result;
|
||||
}
|
||||
}
|
||||
|
||||
// Now listen to the results:
|
||||
int count;
|
||||
int nrok = 0;
|
||||
for (count = (int) shardIdsMap.size(); count > 0; count--) {
|
||||
result = cc->wait( "", coordTransactionID, 0, "", 0.0);
|
||||
if (result->status == CL_COMM_RECEIVED) {
|
||||
if (result->answer_code == triagens::rest::HttpResponse::OK ||
|
||||
result->answer_code == triagens::rest::HttpResponse::CREATED) {
|
||||
TRI_json_t* json = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE,
|
||||
result->answer->body());
|
||||
|
||||
if (JsonHelper::isArray(json)) {
|
||||
TRI_json_t const* r = TRI_LookupArrayJson(json, "result");
|
||||
if (TRI_IsBooleanJson(r)) {
|
||||
if (r->_value._boolean) {
|
||||
nrok++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json != 0) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete result;
|
||||
}
|
||||
|
||||
if (nrok != shardIdsMap.size()) {
|
||||
errorMsg = "some shard produced an error";
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Free all the string buffers:
|
||||
for (j = 0; j < shardIds.size(); j++) {
|
||||
delete bufs[j];
|
||||
}
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
generateError(HttpResponse::SERVER_ERROR, res);
|
||||
generateError(HttpResponse::BAD, res, errorMsg);
|
||||
}
|
||||
else {
|
||||
TRI_json_t result;
|
||||
|
||||
TRI_InitArrayJson(TRI_CORE_MEM_ZONE, &result);
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, &result, "result", TRI_CreateBooleanJson(TRI_CORE_MEM_ZONE, true));
|
||||
TRI_json_t result;
|
||||
|
||||
generateResult(&result);
|
||||
}
|
||||
TRI_InitArrayJson(TRI_CORE_MEM_ZONE, &result);
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE, &result, "result", TRI_CreateBooleanJson(TRI_CORE_MEM_ZONE, true));
|
||||
|
||||
generateResult(&result);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include "VocBase/replication-logger.h"
|
||||
#include "VocBase/server.h"
|
||||
#include "VocBase/voc-shaper.h"
|
||||
#include "VocBase/index.h"
|
||||
#include "v8.h"
|
||||
#include "V8/JSLoader.h"
|
||||
|
||||
|
@ -876,156 +877,6 @@ static v8::Handle<v8::Value> IndexRep (string const& collectionName,
|
|||
return scope.Close(rep);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief index comparator, used by the coordinator to detect if two index
|
||||
/// contents are the same
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef TRI_ENABLE_CLUSTER
|
||||
static bool IndexComparator (TRI_json_t const* lhs,
|
||||
TRI_json_t const* rhs) {
|
||||
TRI_json_t* typeJson = TRI_LookupArrayJson(lhs, "type");
|
||||
assert(TRI_IsStringJson(typeJson));
|
||||
|
||||
// type must be identical
|
||||
if (! TRI_CheckSameValueJson(typeJson, TRI_LookupArrayJson(rhs, "type"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TRI_idx_type_e type = TRI_TypeIndex(typeJson->_value._string.data);
|
||||
|
||||
|
||||
// unique must be identical if present
|
||||
TRI_json_t* value = TRI_LookupArrayJson(lhs, "unique");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "unique"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (type == TRI_IDX_TYPE_GEO1_INDEX) {
|
||||
// geoJson must be identical if present
|
||||
value = TRI_LookupArrayJson(lhs, "geoJson");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "geoJson"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
value = TRI_LookupArrayJson(lhs, "ignoreNull");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "ignoreNull"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_GEO2_INDEX) {
|
||||
value = TRI_LookupArrayJson(lhs, "ignoreNull");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "ignoreNull"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_FULLTEXT_INDEX) {
|
||||
// minLength
|
||||
value = TRI_LookupArrayJson(lhs, "minLength");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "minLength"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_CAP_CONSTRAINT) {
|
||||
// size, byteSize
|
||||
value = TRI_LookupArrayJson(lhs, "size");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "size"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
value = TRI_LookupArrayJson(lhs, "byteSize");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "byteSize"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == TRI_IDX_TYPE_BITARRAY_INDEX) {
|
||||
// bitarray indexes are considered identical if they are based on the same attributes
|
||||
TRI_json_t const* r = TRI_LookupArrayJson(rhs, "fields");
|
||||
value = TRI_LookupArrayJson(lhs, "fields");
|
||||
|
||||
if (TRI_IsListJson(value) &&
|
||||
TRI_IsListJson(r) &&
|
||||
value->_value._objects._length == r->_value._objects._length) {
|
||||
|
||||
for (size_t i = 0; i < value->_value._objects._length; ++i) {
|
||||
TRI_json_t const* l1 = TRI_LookupListJson(value, i);
|
||||
TRI_json_t const* r1 = TRI_LookupListJson(r, i);
|
||||
|
||||
if (TRI_IsListJson(l1) &&
|
||||
TRI_IsListJson(r1) &&
|
||||
l1->_value._objects._length == 2 &&
|
||||
r1->_value._objects._length == 2) {
|
||||
|
||||
// element at position 0 is the attribute name
|
||||
if (! TRI_CheckSameValueJson(TRI_LookupListJson(l1, 0), TRI_LookupListJson(r1, 0))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// we must always exit here to avoid the "regular" fields comparison
|
||||
return true;
|
||||
}
|
||||
|
||||
// other index types: fields must be identical if present
|
||||
value = TRI_LookupArrayJson(lhs, "fields");
|
||||
|
||||
if (TRI_IsListJson(value)) {
|
||||
if (type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||
// compare fields in arbitrary order
|
||||
TRI_json_t const* r = TRI_LookupArrayJson(rhs, "fields");
|
||||
|
||||
if (! TRI_IsListJson(r) ||
|
||||
value->_value._objects._length != r->_value._objects._length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < value->_value._objects._length; ++i) {
|
||||
TRI_json_t const* v = TRI_LookupListJson(value, i);
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (size_t j = 0; j < r->_value._objects._length; ++j) {
|
||||
if (TRI_CheckSameValueJson(v, TRI_LookupListJson(r, j))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "fields"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract the unique flag from the data
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "BasicsC/conversions.h"
|
||||
#include "BasicsC/files.h"
|
||||
#include "BasicsC/json.h"
|
||||
#include "BasicsC/json-utilities.h"
|
||||
#include "BasicsC/linked-list.h"
|
||||
#include "BasicsC/logging.h"
|
||||
#include "BasicsC/string-buffer.h"
|
||||
|
@ -2680,6 +2681,156 @@ void TRI_FreeBitarrayIndex (TRI_index_t* idx) {
|
|||
TRI_Free(TRI_CORE_MEM_ZONE, idx);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief index comparator, used by the coordinator to detect if two index
|
||||
/// contents are the same
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef TRI_ENABLE_CLUSTER
|
||||
bool IndexComparator (TRI_json_t const* lhs,
|
||||
TRI_json_t const* rhs) {
|
||||
TRI_json_t* typeJson = TRI_LookupArrayJson(lhs, "type");
|
||||
assert(TRI_IsStringJson(typeJson));
|
||||
|
||||
// type must be identical
|
||||
if (! TRI_CheckSameValueJson(typeJson, TRI_LookupArrayJson(rhs, "type"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TRI_idx_type_e type = TRI_TypeIndex(typeJson->_value._string.data);
|
||||
|
||||
|
||||
// unique must be identical if present
|
||||
TRI_json_t* value = TRI_LookupArrayJson(lhs, "unique");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "unique"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (type == TRI_IDX_TYPE_GEO1_INDEX) {
|
||||
// geoJson must be identical if present
|
||||
value = TRI_LookupArrayJson(lhs, "geoJson");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "geoJson"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
value = TRI_LookupArrayJson(lhs, "ignoreNull");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "ignoreNull"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_GEO2_INDEX) {
|
||||
value = TRI_LookupArrayJson(lhs, "ignoreNull");
|
||||
if (TRI_IsBooleanJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "ignoreNull"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_FULLTEXT_INDEX) {
|
||||
// minLength
|
||||
value = TRI_LookupArrayJson(lhs, "minLength");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "minLength"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == TRI_IDX_TYPE_CAP_CONSTRAINT) {
|
||||
// size, byteSize
|
||||
value = TRI_LookupArrayJson(lhs, "size");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "size"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
value = TRI_LookupArrayJson(lhs, "byteSize");
|
||||
if (TRI_IsNumberJson(value)) {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "byteSize"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == TRI_IDX_TYPE_BITARRAY_INDEX) {
|
||||
// bitarray indexes are considered identical if they are based on the same attributes
|
||||
TRI_json_t const* r = TRI_LookupArrayJson(rhs, "fields");
|
||||
value = TRI_LookupArrayJson(lhs, "fields");
|
||||
|
||||
if (TRI_IsListJson(value) &&
|
||||
TRI_IsListJson(r) &&
|
||||
value->_value._objects._length == r->_value._objects._length) {
|
||||
|
||||
for (size_t i = 0; i < value->_value._objects._length; ++i) {
|
||||
TRI_json_t const* l1 = TRI_LookupListJson(value, i);
|
||||
TRI_json_t const* r1 = TRI_LookupListJson(r, i);
|
||||
|
||||
if (TRI_IsListJson(l1) &&
|
||||
TRI_IsListJson(r1) &&
|
||||
l1->_value._objects._length == 2 &&
|
||||
r1->_value._objects._length == 2) {
|
||||
|
||||
// element at position 0 is the attribute name
|
||||
if (! TRI_CheckSameValueJson(TRI_LookupListJson(l1, 0), TRI_LookupListJson(r1, 0))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// we must always exit here to avoid the "regular" fields comparison
|
||||
return true;
|
||||
}
|
||||
|
||||
// other index types: fields must be identical if present
|
||||
value = TRI_LookupArrayJson(lhs, "fields");
|
||||
|
||||
if (TRI_IsListJson(value)) {
|
||||
if (type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||
// compare fields in arbitrary order
|
||||
TRI_json_t const* r = TRI_LookupArrayJson(rhs, "fields");
|
||||
|
||||
if (! TRI_IsListJson(r) ||
|
||||
value->_value._objects._length != r->_value._objects._length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < value->_value._objects._length; ++i) {
|
||||
TRI_json_t const* v = TRI_LookupListJson(value, i);
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (size_t j = 0; j < r->_value._objects._length; ++j) {
|
||||
if (TRI_CheckSameValueJson(v, TRI_LookupListJson(r, j))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (! TRI_CheckSameValueJson(value, TRI_LookupArrayJson(rhs, "fields"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -600,6 +600,15 @@ void TRI_DestroyBitarrayIndex (TRI_index_t*);
|
|||
|
||||
void TRI_FreeBitarrayIndex (TRI_index_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief index comparator, used by the coordinator to detect if two index
|
||||
/// contents are the same
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef TRI_ENABLE_CLUSTER
|
||||
bool IndexComparator (TRI_json_t const* lhs, TRI_json_t const* rhs);
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue