diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index fd308f1001..79a45ba276 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -2660,30 +2660,222 @@ static v8::Handle CreateVocBase (v8::Arguments const& argv, return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief index comparator, used by the coordinator to detect if two index +/// contents are the same +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER +static bool CompareGeoIndex1Coordinator (TRI_json_t const* lhs, + TRI_json_t const* rhs) { + if (! TRI_CheckSameValueJson(TRI_LookupArrayJson(lhs, "unique"), + TRI_LookupArrayJson(rhs, "unique"))) { + return false; + } + + if (! TRI_CheckSameValueJson(TRI_LookupArrayJson(lhs, "fields"), + TRI_LookupArrayJson(rhs, "fields"))) { + return false; + } + + if (! TRI_CheckSameValueJson(TRI_LookupArrayJson(lhs, "geoJson"), + TRI_LookupArrayJson(rhs, "geoJson"))) { + return false; + } + + return true; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ensure a geo index, coordinator case +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER +static v8::Handle EnsureGeoIndex1Coordinator (TRI_vocbase_col_t const* col, + bool create, + bool unique, + bool geoJson, + bool ignoreNull, + char const* location) { + v8::HandleScope scope; + + string const databaseName(col->_dbName); + string const cid = StringUtils::itoa(col->_cid); + + ClusterInfo* ci = ClusterInfo::instance(); + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + + if (json == 0) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "geo1")); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "unique", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, unique)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "geoJson", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, geoJson)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "constraint", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, unique)); + + if (unique) { + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "ignoreNull", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, ignoreNull)); + } + + TRI_json_t* flds = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, 1); + + if (flds == 0) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + TRI_V8_EXCEPTION_MEMORY(scope); + } + + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, flds, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, location)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "fields", flds); + + TRI_json_t* resultJson = 0; + string errorMsg; + int myerrno = ci->ensureIndexCoordinator(databaseName, + cid, + json, + create, + &CompareGeoIndex1Coordinator, + resultJson, + errorMsg, + 360.0); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + + if (myerrno != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION_MESSAGE(scope, myerrno, errorMsg); + } + + if (resultJson == 0) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + // TODO: protect against races on _name + v8::Handle index = IndexRep(col->_name, resultJson); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, resultJson); + + return scope.Close(index); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief index comparator, used by the coordinator to detect if two index +/// contents are the same +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER +static bool CompareGeoIndex2Coordinator (TRI_json_t const* lhs, + TRI_json_t const* rhs) { + if (! TRI_CheckSameValueJson(TRI_LookupArrayJson(lhs, "unique"), + TRI_LookupArrayJson(rhs, "unique"))) { + return false; + } + + if (! TRI_CheckSameValueJson(TRI_LookupArrayJson(lhs, "fields"), + TRI_LookupArrayJson(rhs, "fields"))) { + return false; + } + + return true; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ensure a geo index, coordinator case +//////////////////////////////////////////////////////////////////////////////// + +#ifdef TRI_ENABLE_CLUSTER +static v8::Handle EnsureGeoIndex2Coordinator (TRI_vocbase_col_t const* col, + bool create, + bool unique, + bool ignoreNull, + char const* latitude, + char const* longitude) { + v8::HandleScope scope; + + string const databaseName(col->_dbName); + string const cid = StringUtils::itoa(col->_cid); + + ClusterInfo* ci = ClusterInfo::instance(); + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + + if (json == 0) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, "geo2")); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "unique", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, unique)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "constraint", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, unique)); + + if (unique) { + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "ignoreNull", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, ignoreNull)); + } + + TRI_json_t* flds = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, 2); + + if (flds == 0) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + TRI_V8_EXCEPTION_MEMORY(scope); + } + + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, flds, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, latitude)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, flds, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, longitude)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "fields", flds); + + TRI_json_t* resultJson = 0; + string errorMsg; + int myerrno = ci->ensureIndexCoordinator(databaseName, + cid, + json, + create, + &CompareGeoIndex2Coordinator, + resultJson, + errorMsg, + 360.0); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + + if (myerrno != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION_MESSAGE(scope, myerrno, errorMsg); + } + + if (resultJson == 0) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + // TODO: protect against races on _name + v8::Handle index = IndexRep(col->_name, resultJson); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, resultJson); + + return scope.Close(index); +} +#endif + //////////////////////////////////////////////////////////////////////////////// /// @brief ensures that a geo index or constraint exists //////////////////////////////////////////////////////////////////////////////// static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv, bool unique) { v8::HandleScope scope; - + PREVENT_EMBEDDED_TRANSACTION(scope); + + TRI_vocbase_col_t const* col = TRI_UnwrapClass(argv.Holder(), WRP_VOCBASE_COL_TYPE); - v8::Handle err; - TRI_vocbase_col_t const* collection = UseCollection(argv.Holder(), &err); - - if (collection == 0) { - return scope.Close(v8::ThrowException(err)); + if (col == 0) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract collection"); } - TRI_primary_collection_t* primary = collection->_collection; - - TRI_document_collection_t* document = (TRI_document_collection_t*) primary; + TRI_vocbase_t* vocbase = col->_vocbase; + assert(vocbase != 0); + TRI_index_t* idx = 0; bool created; int off = unique ? 1 : 0; bool ignoreNull = false; - + // ............................................................................. // case: // ............................................................................. @@ -2692,15 +2884,30 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_Utf8ValueNFC loc(TRI_UNKNOWN_MEM_ZONE, argv[0]); if (*loc == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_PARAMETER(scope, " must be an attribute path"); } if (unique) { ignoreNull = TRI_ObjectToBoolean(argv[1]); } + +#ifdef TRI_ENABLE_CLUSTER + if (ServerState::instance()->isCoordinator()) { + v8::Handle ret = EnsureGeoIndex1Coordinator(col, true, unique, false, ignoreNull, *loc); + return scope.Close(ret); + } +#endif + + CollectionNameResolver resolver(vocbase); + ReadTransactionType trx(vocbase, resolver, col->_cid); - idx = TRI_EnsureGeoIndex1DocumentCollection(document, + int res = trx.begin(); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + + idx = TRI_EnsureGeoIndex1DocumentCollection((TRI_document_collection_t*) trx.primaryCollection(), *loc, false, unique, @@ -2717,15 +2924,30 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_Utf8ValueNFC loc(TRI_UNKNOWN_MEM_ZONE, argv[0]); if (*loc == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_PARAMETER(scope, " must be an attribute path"); } if (unique) { ignoreNull = TRI_ObjectToBoolean(argv[2]); } + +#ifdef TRI_ENABLE_CLUSTER + if (ServerState::instance()->isCoordinator()) { + v8::Handle ret = EnsureGeoIndex1Coordinator(col, true, unique, TRI_ObjectToBoolean(argv[1]), ignoreNull, *loc); + return scope.Close(ret); + } +#endif + + CollectionNameResolver resolver(vocbase); + ReadTransactionType trx(vocbase, resolver, col->_cid); - idx = TRI_EnsureGeoIndex1DocumentCollection(document, + int res = trx.begin(); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + + idx = TRI_EnsureGeoIndex1DocumentCollection((TRI_document_collection_t*) trx.primaryCollection(), *loc, TRI_ObjectToBoolean(argv[1]), unique, @@ -2743,20 +2965,34 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_Utf8ValueNFC lon(TRI_UNKNOWN_MEM_ZONE, argv[1]); if (*lat == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_PARAMETER(scope, " must be an attribute path"); } if (*lon == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_PARAMETER(scope, " must be an attribute path"); } if (unique) { ignoreNull = TRI_ObjectToBoolean(argv[2]); } + +#ifdef TRI_ENABLE_CLUSTER + if (ServerState::instance()->isCoordinator()) { + v8::Handle ret = EnsureGeoIndex2Coordinator(col, true, unique, ignoreNull, *lat, *lon); + return scope.Close(ret); + } +#endif + + CollectionNameResolver resolver(vocbase); + ReadTransactionType trx(vocbase, resolver, col->_cid); - idx = TRI_EnsureGeoIndex2DocumentCollection(document, + int res = trx.begin(); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + + idx = TRI_EnsureGeoIndex2DocumentCollection((TRI_document_collection_t*) trx.primaryCollection(), *lat, *lon, unique, @@ -2770,8 +3006,6 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv // ............................................................................. else { - ReleaseCollection(collection); - if (unique) { TRI_V8_EXCEPTION_USAGE( scope, @@ -2786,25 +3020,24 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv } if (idx == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), "index could not be created"); } TRI_json_t* json = idx->json(idx,false); if (json == 0) { - ReleaseCollection(collection); TRI_V8_EXCEPTION_MEMORY(scope); } - v8::Handle index = IndexRep(&primary->base, json); + // TODO: protect this from races on _name!! + v8::Handle index = IndexRep(col->_name, json); + TRI_FreeJson(TRI_CORE_MEM_ZONE, json); if (index->IsObject()) { index->ToObject()->Set(v8::String::New("isNewlyCreated"), v8::Boolean::New(created)); } - ReleaseCollection(collection); return scope.Close(index); } diff --git a/js/server/modules/org/arangodb/cluster.js b/js/server/modules/org/arangodb/cluster.js index 5e7cb9e92d..d2fc968a7f 100644 --- a/js/server/modules/org/arangodb/cluster.js +++ b/js/server/modules/org/arangodb/cluster.js @@ -380,6 +380,58 @@ function handleDatabaseChanges (plan, current) { cleanupCurrentDatabases(); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an index in a shard +//////////////////////////////////////////////////////////////////////////////// + +function createIndex (shard, index) { + var collection = arangodb.db._collection(shard); + + switch (index.type) { + case "hash": + if (index.unique) { + collection.ensureUniqueConstraint.apply(collection, index.fields); + } + else { + collection.ensureHashIndex.apply(collection, index.fields); + } + break; + case "skiplist": + if (index.unique) { + collection.ensureUniqueSkiplist.apply(collection, index.fields); + } + else { + collection.ensureSkiplist.apply(collection, index.fields); + } + break; + case "fulltext": + collection.ensureFulltextIndex(index.fields[0], index.minLength); + break; + case "geo1": + if (index.unique) { + collection.ensureGeoConstraint(index.fields[0], + index.geoJson, + index.ignoreNull); + } + else { + collection.ensureGeoIndex(index.fields[0], + index.geoJson, + index.ignoreNull); + } + break; + case "geo2": + if (index.unique) { + collection.ensureGeoConstraint(index.fields[0], + index.fields[1], + index.ignoreNull); + } + else { + collection.ensureGeoIndex(index.fields[0], index.fields[1]); + } + break; + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief create collections if they exist in the plan but not locally //////////////////////////////////////////////////////////////////////////////// @@ -526,35 +578,15 @@ function createLocalCollections (plannedCollections) { for (idx in payload.indexes) { if (payload.indexes.hasOwnProperty(idx)) { - if (! indexes.hasOwnProperty(payload.indexes[idx].id)) { - var c = db._collection(shard); - + var index = payload.indexes[idx]; + + if (! indexes.hasOwnProperty(index.id)) { console.info("creating index '%s/%s': %s", database, shard, - JSON.stringify(payload.indexes[idx])); + JSON.stringify(index)); - switch (payload.indexes[idx].type) { - case "hash": - if (payload.indexes[idx].unique) { - c.ensureUniqueConstraint.apply(c, payload.indexes[idx].fields); - } - else { - c.ensureHashIndex.apply(c, payload.indexes[idx].fields); - } - break; - case "skiplist": - if (payload.indexes[idx].unique) { - c.ensureUniqueSkiplist.apply(c, payload.indexes[idx].fields); - } - else { - c.ensureSkiplist.apply(c, payload.indexes[idx].fields); - } - break; - case "fulltext": - c.ensureFulltextIndex(payload.indexes[idx].fields[0], payload.indexes[idx].minLength); - break; - } + createIndex(shard, index); } } }