mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/arangodb/arangodb into devel
This commit is contained in:
commit
f51871c599
|
@ -914,20 +914,6 @@ if( EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" )
|
|||
)
|
||||
endif()
|
||||
|
||||
|
||||
################################################################################
|
||||
## SNAPCRAFT PACKAGE
|
||||
################################################################################
|
||||
|
||||
#set(USE_SNAPCRAFT "ON")
|
||||
if (USE_SNAPCRAFT)
|
||||
if(NOT DEFINED SNAP_PORT)
|
||||
set(SNAP_PORT 8529)
|
||||
endif()
|
||||
include(packages/snap)
|
||||
endif ()
|
||||
|
||||
|
||||
add_custom_target(packages
|
||||
DEPENDS ${PACKAGES_LIST}
|
||||
)
|
||||
|
|
|
@ -483,6 +483,9 @@ if test -n "${ENTERPRISE_GIT_URL}" ; then
|
|||
echo "I'm on tag: ${GITARGS}"
|
||||
else
|
||||
GITARGS=`git branch --no-color -q| grep '^\*' | sed "s;\* *;;"`
|
||||
if echo $GITARGS |grep -q ' '; then
|
||||
GITARGS=devel
|
||||
fi
|
||||
echo "I'm on Branch: ${GITARGS}"
|
||||
fi
|
||||
# clean up if we're commanded to:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: @CMAKE_PROJECT_NAME@
|
||||
name: @CPACK_PACKAGE_NAME@
|
||||
version: @CPACK_PACKAGE_VERSION@
|
||||
description: "ArangoDB is a native multi-model database with flexible data models for
|
||||
documents, graphs, and key-values. Build high performance applications using a convenient
|
||||
|
@ -12,7 +12,7 @@ grade: stable
|
|||
#grade: devel
|
||||
|
||||
parts:
|
||||
arangodb3:
|
||||
@CPACK_PACKAGE_NAME@:
|
||||
source: @CPACK_PACKAGE_TGZ@
|
||||
plugin: dump
|
||||
snap:
|
||||
|
|
|
@ -554,6 +554,11 @@ static void EnsureIndex(v8::FunctionCallbackInfo<v8::Value> const& args,
|
|||
|
||||
VPackBuilder builder;
|
||||
int res = EnhanceIndexJson(args, builder, create);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
VPackSlice slice = builder.slice();
|
||||
if (res == TRI_ERROR_NO_ERROR && ServerState::instance()->isCoordinator()) {
|
||||
TRI_ASSERT(slice.isObject());
|
||||
|
@ -568,27 +573,41 @@ static void EnsureIndex(v8::FunctionCallbackInfo<v8::Value> const& args,
|
|||
|
||||
VPackSlice v = slice.get("unique");
|
||||
|
||||
/* the following combinations of shardKeys and indexKeys are allowed/not allowed:
|
||||
|
||||
shardKeys indexKeys
|
||||
a a ok
|
||||
a b not ok
|
||||
a a b ok
|
||||
a b a not ok
|
||||
a b b not ok
|
||||
a b a b ok
|
||||
a b a b c ok
|
||||
a b c a b not ok
|
||||
a b c a b c ok
|
||||
*/
|
||||
|
||||
if (v.isBoolean() && v.getBoolean()) {
|
||||
// unique index, now check if fields and shard keys match
|
||||
VPackSlice flds = slice.get("fields");
|
||||
if (flds.isArray() && c->numberOfShards() > 1) {
|
||||
std::vector<std::string> const& shardKeys = c->shardKeys();
|
||||
std::unordered_set<std::string> indexKeys;
|
||||
size_t n = static_cast<size_t>(flds.length());
|
||||
|
||||
if (shardKeys.size() != n) {
|
||||
res = TRI_ERROR_CLUSTER_UNSUPPORTED;
|
||||
} else {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
VPackSlice f = flds.at(i);
|
||||
if (!f.isString()) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
continue;
|
||||
} else {
|
||||
std::string tmp = f.copyString();
|
||||
if (tmp != shardKeys[i]) {
|
||||
res = TRI_ERROR_CLUSTER_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
VPackSlice f = flds.at(i);
|
||||
if (!f.isString()) {
|
||||
// index attributes must be strings
|
||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
indexKeys.emplace(f.copyString());
|
||||
}
|
||||
|
||||
// all shard-keys must be covered by the index
|
||||
for (auto& it : shardKeys) {
|
||||
if (indexKeys.find(it) == indexKeys.end()) {
|
||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_CLUSTER_UNSUPPORTED, "shard key '" + it + "' must be present in unique index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -596,10 +615,6 @@ static void EnsureIndex(v8::FunctionCallbackInfo<v8::Value> const& args,
|
|||
}
|
||||
}
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_THROW_EXCEPTION(res);
|
||||
}
|
||||
|
||||
TRI_ASSERT(!slice.isNone());
|
||||
events::CreateIndex(collection->name(), slice);
|
||||
|
||||
|
|
|
@ -37,6 +37,17 @@ elseif (MSVC)
|
|||
include(packages/nsis)
|
||||
endif ()
|
||||
|
||||
################################################################################
|
||||
## SNAPCRAFT PACKAGE
|
||||
################################################################################
|
||||
|
||||
if (USE_SNAPCRAFT)
|
||||
if(NOT DEFINED SNAP_PORT)
|
||||
set(SNAP_PORT 8529)
|
||||
endif()
|
||||
include(packages/snap)
|
||||
endif ()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/Installation/cmake/CMakeCPackOptions.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/CMakeCPackOptions.cmake" @ONLY)
|
||||
|
|
|
@ -67,11 +67,17 @@ router.post((req, res) => {
|
|||
if (source instanceof Buffer) {
|
||||
source = writeUploadToTempFile(source);
|
||||
}
|
||||
const service = fm.install(
|
||||
source,
|
||||
req.queryParams.mount,
|
||||
_.omit(req.queryParams, ['mount'])
|
||||
);
|
||||
const dependencies = req.body.dependencies && JSON.parse(req.body.dependencies);
|
||||
const configuration = req.body.configuration && JSON.parse(req.body.configuration);
|
||||
const mount = req.queryParams.mount;
|
||||
fm.install(source, mount, _.omit(req.queryParams, ['mount']));
|
||||
if (configuration) {
|
||||
fm.setConfiguration(mount, {configuration, replace: true});
|
||||
}
|
||||
if (dependencies) {
|
||||
fm.setDependencies(mount, {dependencies, replace: true});
|
||||
}
|
||||
const service = fm.lookupService(mount);
|
||||
res.json(serviceToJson(service));
|
||||
})
|
||||
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
|
||||
|
@ -121,11 +127,17 @@ serviceRouter.patch((req, res) => {
|
|||
if (source instanceof Buffer) {
|
||||
source = writeUploadToTempFile(source);
|
||||
}
|
||||
const service = fm.upgrade(
|
||||
source,
|
||||
req.queryParams.mount,
|
||||
_.omit(req.queryParams, ['mount'])
|
||||
);
|
||||
const dependencies = req.body.dependencies && JSON.parse(req.body.dependencies);
|
||||
const configuration = req.body.configuration && JSON.parse(req.body.configuration);
|
||||
const mount = req.queryParams.mount;
|
||||
fm.upgrade(source, mount, _.omit(req.queryParams, ['mount']));
|
||||
if (configuration) {
|
||||
fm.setConfiguration(mount, {configuration, replace: false});
|
||||
}
|
||||
if (dependencies) {
|
||||
fm.setDependencies(mount, {dependencies, replace: false});
|
||||
}
|
||||
const service = fm.lookupService(mount);
|
||||
res.json(serviceToJson(service));
|
||||
})
|
||||
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
|
||||
|
@ -149,11 +161,17 @@ serviceRouter.put((req, res) => {
|
|||
if (source instanceof Buffer) {
|
||||
source = writeUploadToTempFile(source);
|
||||
}
|
||||
const service = fm.replace(
|
||||
source,
|
||||
req.queryParams.mount,
|
||||
_.omit(req.queryParams, ['mount'])
|
||||
);
|
||||
const dependencies = req.body.dependencies && JSON.parse(req.body.dependencies);
|
||||
const configuration = req.body.configuration && JSON.parse(req.body.configuration);
|
||||
const mount = req.queryParams.mount;
|
||||
fm.replace(source, mount, _.omit(req.queryParams, ['mount']));
|
||||
if (configuration) {
|
||||
fm.setConfiguration(mount, {configuration, replace: true});
|
||||
}
|
||||
if (dependencies) {
|
||||
fm.setDependencies(mount, {dependencies, replace: true});
|
||||
}
|
||||
const service = fm.lookupService(mount);
|
||||
res.json(serviceToJson(service));
|
||||
})
|
||||
.body(schemas.service, ['multipart/form-data', 'application/json'], `Service to be installed.`)
|
||||
|
|
|
@ -42,5 +42,7 @@ exports.service = joi.object({
|
|||
source: joi.alternatives(
|
||||
joi.string().description(`Local file path or URL of the service to be installed`),
|
||||
joi.object().type(Buffer).description(`Zip bundle of the service to be installed`)
|
||||
).required().description(`Local file path, URL or zip bundle of the service to be installed`)
|
||||
).required().description(`Local file path, URL or zip bundle of the service to be installed`),
|
||||
configuration: joi.string().optional().description(`Configuration to use for the service (JSON)`),
|
||||
dependencies: joi.string().optional().description(`Dependency options to use for the service (JSON)`)
|
||||
}).required();
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*jshint globalstrict:false, strict:false */
|
||||
/*global assertEqual, assertNotEqual, assertTrue, assertFalse, fail */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test the index
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var jsunity = require("jsunity");
|
||||
var internal = require("internal");
|
||||
var errors = internal.errors;
|
||||
var db = require("@arangodb").db;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite: check unique indexes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function uniqueIndexSuite() {
|
||||
'use strict';
|
||||
var cn = "UnitTestsCollectionIdx";
|
||||
|
||||
var runTest = function(indexType) {
|
||||
var tests = [
|
||||
{ shardKeys: ["a"], indexKeys: ["a"], result: true },
|
||||
{ shardKeys: ["b"], indexKeys: ["b"], result: true },
|
||||
{ shardKeys: ["a"], indexKeys: ["b"], result: false },
|
||||
{ shardKeys: ["b"], indexKeys: ["a"], result: false },
|
||||
{ shardKeys: ["a"], indexKeys: ["a", "b"], result: true },
|
||||
{ shardKeys: ["a"], indexKeys: ["b", "a"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["c"], result: false },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["b"], result: false },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["a"], result: false },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["a", "c"], result: false },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["c", "a"], result: false },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["a", "b"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["b", "a"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["a", "b", "c"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["b", "a", "c"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["c", "b", "a"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["b", "c", "a"], result: true },
|
||||
{ shardKeys: ["a", "b"], indexKeys: ["a", "b", "c", "d"], result: true },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["b"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["c"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "b"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "c"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["b", "c"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "b", "c"], result: true },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "b", "c", "d"], result: true },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "b", "d"], result: false },
|
||||
{ shardKeys: ["a", "b", "c"], indexKeys: ["a", "b", "d", "c"], result: true }
|
||||
];
|
||||
|
||||
tests.forEach(function(test) {
|
||||
// drop collection first in case it already exists
|
||||
internal.db._drop(cn);
|
||||
|
||||
var collection = internal.db._create(cn, { shardKeys: test.shardKeys, numberOfShards: 4 });
|
||||
if (test.result) {
|
||||
// index creation should work
|
||||
var idx = collection.ensureIndex({ type: indexType, fields: test.indexKeys, unique: true });
|
||||
assertTrue(idx.isNewlyCreated);
|
||||
assertTrue(idx.unique);
|
||||
assertEqual(test.indexKeys, idx.fields);
|
||||
|
||||
// now actually insert unique values
|
||||
var n = 1000;
|
||||
var threshold = 2;
|
||||
while (n > Math.pow(threshold, test.indexKeys.length)) {
|
||||
threshold += 2;
|
||||
}
|
||||
|
||||
var initState = function(indexKeys) {
|
||||
var state = { };
|
||||
for (var key in indexKeys) {
|
||||
state[indexKeys[key]] = 0;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
var permute = function(state, indexKeys) {
|
||||
for (var key in indexKeys) {
|
||||
if (++state[indexKeys[key]] >= threshold) {
|
||||
state[indexKeys[key]] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
var state = initState(test.indexKeys), i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
collection.insert(state);
|
||||
permute(state, test.indexKeys);
|
||||
}
|
||||
|
||||
var filter = function(state) {
|
||||
var parts = [];
|
||||
for (var key in state) {
|
||||
parts.push("doc." + key + " == " + state[key]);
|
||||
}
|
||||
return parts.join(" && ");
|
||||
};
|
||||
|
||||
state = initState(test.indexKeys);
|
||||
for (i = 0; i < n;) {
|
||||
var query = "FOR doc IN " + collection.name() + " FILTER " + filter(state) + " RETURN 1";
|
||||
if (i === 0) {
|
||||
// on first invocation check that the index is actually used
|
||||
assertNotEqual(-1, db._createStatement(query).explain().plan.nodes.map(function(node) { return node.type; }).indexOf("IndexNode"));
|
||||
}
|
||||
|
||||
// expect exactly one result
|
||||
assertEqual(1, db._query(query).toArray().length);
|
||||
|
||||
// query some random elements only to save time
|
||||
var skip = Math.floor(Math.random() * 15) + 1;
|
||||
while (skip > 0) {
|
||||
permute(state, test.indexKeys);
|
||||
--skip;
|
||||
++i;
|
||||
}
|
||||
if (i >= n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// index creation should fail
|
||||
try {
|
||||
collection.ensureIndex({ type: indexType, fields: test.indexKeys, unique: true });
|
||||
fail();
|
||||
} catch (err) {
|
||||
assertEqual(errors.ERROR_CLUSTER_UNSUPPORTED.code, err.errorNum);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set up
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
setUp : function () {
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief tear down
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tearDown : function () {
|
||||
internal.db._drop(cn);
|
||||
},
|
||||
|
||||
testUniqueHashIndexes : function () {
|
||||
runTest("hash");
|
||||
},
|
||||
|
||||
testUniqueSkiplistIndexes : function () {
|
||||
runTest("skiplist");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
jsunity.run(uniqueIndexSuite);
|
||||
|
||||
return jsunity.done();
|
Loading…
Reference in New Issue