1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel

This commit is contained in:
a-brandt 2013-03-08 13:20:51 +01:00
commit 4b2cac5ba0
27 changed files with 1333 additions and 267 deletions

View File

@ -1,6 +1,55 @@
v1.3 (XXXX-XX-XX)
-----------------
* issue #212: auto-increment support
The feature can be used by creating a collection with the extra `keyOptions`
attribute as follows:
db._create("mycollection", { keyOptions: { type: "autoincrement", offset: 1, increment: 10, allowUserKeys: true } });
The `type` attribute will make sure the keys will be auto-generated if no
`_key` attribute is specified for a document.
The `allowUserKeys` attribute determines whether users might still supply own
`_key` values with documents or if this is considered an error.
The `increment` value determines the actual increment value, whereas the `offset`
value can be used to seed to value sequence with a specific starting value.
This will be useful later in a multi-master setup, when multiple servers can use
different auto-increment seed values and thus generate non-conflicting auto-increment values.
The default values currently are:
- `allowUserKeys`: `true`
- `offset`: `0`
- `increment`: `1`
The only other available key generator type currently is `traditional`.
The `traditional` key generator will auto-generate keys in a fashion as ArangoDB
always did (some increasing integer value, with a more or less unpredictable
increment value).
Note that for the `traditional` key generator there is no the option to disallow
user-supplied keys and give the server the sole responsibility for key generation.
This also introduces the following errors that API implementors may want to check
the return values for:
- 1222: `document key unexpected`: will be raised when a document is created with
a `_key` attribute, but the underlying collection was set up with the key generator
attribute `allowUserKeys: false`.
- 1225: `out of keys`: will be raised when the auto-increment key generator runs
out of keys. This may happen when the next key to be generated is 2^64 or higher.
In practice, this will only happen if the values for `increment` or `offset` are
not set appropriately, or if users are allowed to supply own keys, those keys
are near the 2^64 threshold, and later the auto-increment feature kicks in and
generates keys that cross that threshold.
It will not occur in practice with proper configuration and proper usage of the
collections.
* adjust startup log output to be more compact, less verbose
* set the required minimum number of file descriptors to 256.
@ -51,6 +100,8 @@ v1.3 (XXXX-XX-XX)
v1.2.1 (XXXX-XX-XX)
-------------------
* issue #442: pls update post install info on osx
* fixed conversion of special double values (NaN, -inf, +inf) when converting from
shapedjson to JSON

View File

@ -50,15 +50,15 @@ systemddir=/lib/systemd/system
########################################################
install_message="
ArangoDB (http://www.arangodb.org)
ArangoDB (https://www.arangodb.org)
A universal open-source database with a flexible data model for documents,
graphs, and key-values.
First Steps with ArangoDB:
http:/www.arangodb.org/quickstart
https:/www.arangodb.org/quickstart
Upgrading ArangoDB:
http://www.arangodb.org/manuals/1.1/Upgrading.html
https://www.arangodb.org/manuals/current/Upgrading.html
Configuration file:
/etc/arangodb/arangod.conf

View File

@ -947,6 +947,8 @@ describe ArangoDB do
doc.parsed_response['waitForSync'].should eq(true)
doc.parsed_response['isVolatile'].should eq(false)
doc.parsed_response['isSystem'].should eq(false)
doc.parsed_response['keyOptions']['type'].should eq("traditional")
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true)
cmd = api + "/" + cn + "/properties"
body = "{ \"waitForSync\" : false }"
@ -962,15 +964,17 @@ describe ArangoDB do
doc.parsed_response['waitForSync'].should eq(false)
doc.parsed_response['isVolatile'].should eq(false)
doc.parsed_response['isSystem'].should eq(false)
doc.parsed_response['keyOptions']['type'].should eq("traditional")
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true)
ArangoDB.drop_collection(cn)
end
it "create collection with createOptions property" do
it "create collection with explicit keyOptions property, traditional keygen" do
cn = "UnitTestsCollectionBasics"
cmd = "/_api/collection"
body = "{ \"name\" : \"#{cn}\", \"waitForSync\" : false, \"type\" : 2, \"createOptions\" : {\"opt1\" : 10, \"opt2\" : \"val2\" } }"
body = "{ \"name\" : \"#{cn}\", \"waitForSync\" : false, \"type\" : 2, \"keyOptions\" : {\"type\": \"traditional\", \"allowUserKeys\": true } }"
doc = ArangoDB.log_post("#{prefix}-with-create-options", cmd, :body => body)
doc.code.should eq(200)
@ -989,8 +993,39 @@ describe ArangoDB do
doc.parsed_response['status'].should eq(3)
doc.parsed_response['waitForSync'].should eq(true)
doc.parsed_response['isVolatile'].should eq(false)
doc.parsed_response['createOptions']['opt1'].should eq(10)
doc.parsed_response['createOptions']['opt2'].should eq("val2")
doc.parsed_response['keyOptions']['type'].should eq("traditional")
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true)
ArangoDB.drop_collection(cn)
end
it "create collection with explicit keyOptions property, autoinc keygen" do
cn = "UnitTestsCollectionBasics"
cmd = "/_api/collection"
body = "{ \"name\" : \"#{cn}\", \"waitForSync\" : false, \"type\" : 2, \"keyOptions\" : {\"type\": \"autoincrement\", \"offset\": 7, \"increment\": 99, \"allowUserKeys\": false } }"
doc = ArangoDB.log_post("#{prefix}-with-create-options", cmd, :body => body)
doc.code.should eq(200)
cid = doc.parsed_response['id']
cmd = api + "/" + cn + "/properties"
body = "{ \"waitForSync\" : true }"
doc = ArangoDB.log_put("#{prefix}-with-create-options", cmd, :body => body)
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)
doc.parsed_response['waitForSync'].should eq(true)
doc.parsed_response['isVolatile'].should eq(false)
doc.parsed_response['keyOptions']['type'].should eq("autoincrement")
doc.parsed_response['keyOptions']['increment'].should eq(99)
doc.parsed_response['keyOptions']['offset'].should eq(7)
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(false)
ArangoDB.drop_collection(cn)
end
@ -1015,11 +1050,13 @@ describe ArangoDB do
doc.parsed_response['code'].should eq(200)
doc.parsed_response['waitForSync'].should eq(false)
doc.parsed_response['isVolatile'].should eq(true)
doc.parsed_response['keyOptions']['type'].should eq("traditional")
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true)
ArangoDB.drop_collection(cn)
end
it "create collection with empty createOptions property" do
it "create collection with empty keyOptions property" do
cn = "UnitTestsCollectionBasics"
ArangoDB.drop_collection(cn)
@ -1043,7 +1080,8 @@ describe ArangoDB do
doc.parsed_response['status'].should eq(3)
doc.parsed_response['waitForSync'].should eq(true)
doc.parsed_response['isVolatile'].should eq(false)
doc.parsed_response['createOptions'].should be_nil
doc.parsed_response['keyOptions']['type'].should eq("traditional")
doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true)
ArangoDB.drop_collection(cn)
end

View File

@ -215,6 +215,7 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \
@top_srcdir@/js/common/tests/shell-collection.js \
@top_srcdir@/js/common/tests/shell-collection-volatile.js \
@top_srcdir@/js/common/tests/shell-compactor.js \
@top_srcdir@/js/common/tests/shell-keygen.js \
@top_srcdir@/js/common/tests/shell-simple-query.js \
@top_srcdir@/js/common/tests/shell-statement.js \
@top_srcdir@/js/common/tests/shell-crypto.js \

View File

@ -412,6 +412,10 @@ void RestVocbaseBaseHandler::generateTransactionError (const string& collectionN
generateError(HttpResponse::BAD, res, "invalid document key");
return;
case TRI_ERROR_ARANGO_OUT_OF_KEYS:
generateError(HttpResponse::SERVER_ERROR, res, "out of keys");
return;
case TRI_ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED:
generateError(HttpResponse::BAD, res, "collection does not allow using user-defined keys");
return;

View File

@ -958,12 +958,17 @@ static v8::Handle<v8::Value> SaveVocbaseCol (SingleCollectionWriteTransaction<Em
if (argv[0]->IsObject()) {
v8::Handle<v8::Object> obj = argv[0]->ToObject();
v8::Handle<v8::Value> v = obj->Get(v8g->KeyKey);
if (v->IsString()) {
// string key
TRI_Utf8ValueNFC str(TRI_CORE_MEM_ZONE, v);
key = TRI_DuplicateString2(*str, str.length());
holder.registerString(TRI_CORE_MEM_ZONE, key);
if (obj->Has(v8g->KeyKey)) {
v8::Handle<v8::Value> v = obj->Get(v8g->KeyKey);
if (v->IsString()) {
// string key
TRI_Utf8ValueNFC str(TRI_CORE_MEM_ZONE, v);
key = TRI_DuplicateString2(*str, str.length());
holder.registerString(TRI_CORE_MEM_ZONE, key);
}
else {
return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD)));
}
}
}
@ -1043,11 +1048,16 @@ static v8::Handle<v8::Value> SaveEdgeCol (SingleCollectionWriteTransaction<Embed
if (argv[2]->IsObject()) {
v8::Handle<v8::Object> obj = argv[2]->ToObject();
v8::Handle<v8::Value> v = obj->Get(v8g->KeyKey);
if (v->IsString()) {
TRI_Utf8ValueNFC str(TRI_CORE_MEM_ZONE, v);
key = TRI_DuplicateString2(*str, str.length());
holder.registerString(TRI_CORE_MEM_ZONE, key);
if (obj->Has(v8g->KeyKey)) {
v8::Handle<v8::Value> v = obj->Get(v8g->KeyKey);
if (v->IsString()) {
TRI_Utf8ValueNFC str(TRI_CORE_MEM_ZONE, v);
key = TRI_DuplicateString2(*str, str.length());
holder.registerString(TRI_CORE_MEM_ZONE, key);
}
else {
return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD)));
}
}
}
@ -1343,7 +1353,6 @@ static v8::Handle<v8::Value> CreateVocBase (v8::Arguments const& argv, TRI_col_t
}
v8::Handle<v8::Object> p = argv[1]->ToObject();
v8::Handle<v8::String> isSystemKey = v8::String::New("isSystem");
TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
@ -1360,20 +1369,20 @@ static v8::Handle<v8::Value> CreateVocBase (v8::Arguments const& argv, TRI_col_t
effectiveSize = (TRI_voc_size_t) s;
}
// get optional options
TRI_json_t* options = 0;
if (p->Has(v8g->CreateOptionsKey)) {
options = TRI_ObjectToJson(p->Get(v8g->CreateOptionsKey));
// get optional values
TRI_json_t* keyOptions = 0;
if (p->Has(v8g->KeyOptionsKey)) {
keyOptions = TRI_ObjectToJson(p->Get(v8g->KeyOptionsKey));
}
TRI_InitCollectionInfo(vocbase, &parameter, name.c_str(), collectionType, effectiveSize, options);
TRI_InitCollectionInfo(vocbase, &parameter, name.c_str(), collectionType, effectiveSize, keyOptions);
if (p->Has(v8g->WaitForSyncKey)) {
parameter._waitForSync = TRI_ObjectToBoolean(p->Get(v8g->WaitForSyncKey));
}
if (p->Has(isSystemKey)) {
parameter._isSystem = TRI_ObjectToBoolean(p->Get(isSystemKey));
if (p->Has(v8g->IsSystemKey)) {
parameter._isSystem = TRI_ObjectToBoolean(p->Get(v8g->IsSystemKey));
}
if (p->Has(v8g->IsVolatileKey)) {
@ -4586,6 +4595,20 @@ static v8::Handle<v8::Value> JS_NameVocbaseCol (v8::Arguments const& argv) {
/// kept in memory only and ArangoDB will not write or sync the data
/// to disk.
///
/// - @LIT{keyOptions} (optional) additional options for key generation. This is
/// a JSON array containing the following attributes (note: some of the
/// attributes are optional):
/// - @LIT{type}: the type of the key generator used for the collection.
/// - @LIT{allowUserKeys}: if set to @LIT{true}, then it is allowed to supply
/// own key values in the @LIT{_key} attribute of a document. If set to
/// @LIT{false}, then the key generator will solely be responsible for
/// generating keys and supplying own key values in the @LIT{_key} attribute
/// of documents is considered an error.
/// - @LIT{increment}: increment value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
/// - @LIT{offset}: initial offset value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
///
/// @FUN{@FA{collection}.properties(@FA{properties})}
///
/// Changes the collection properties. @FA{properties} must be a object with
@ -4601,8 +4624,8 @@ static v8::Handle<v8::Value> JS_NameVocbaseCol (v8::Arguments const& argv) {
/// created journals. Also note that you cannot lower the journal size to less
/// then size of the largest document already stored in the collection.
///
/// Note: some other collection properties, such as @LIT{type} or @LIT{isVolatile}
/// cannot be changed once the collection is created.
/// Note: some other collection properties, such as @LIT{type}, @LIT{isVolatile},
/// or @LIT{keyOptions} cannot be changed once the collection is created.
///
/// @EXAMPLES
///
@ -4711,18 +4734,22 @@ static v8::Handle<v8::Value> JS_PropertiesVocbaseCol (v8::Arguments const& argv)
v8::Handle<v8::Object> result = v8::Object::New();
if (TRI_IS_DOCUMENT_COLLECTION(base->_info._type)) {
TRI_voc_size_t maximalSize = base->_info._maximalSize;
bool waitForSync = base->_info._waitForSync;
TRI_json_t* keyOptions = primary->_keyGenerator->toJson(primary->_keyGenerator);
result->Set(v8g->WaitForSyncKey, waitForSync ? v8::True() : v8::False());
result->Set(v8g->JournalSizeKey, v8::Number::New(maximalSize));
result->Set(v8g->IsVolatileKey, base->_info._isVolatile ? v8::True() : v8::False());
result->Set(TRI_V8_SYMBOL("isSystem"), base->_info._isSystem ? v8::True() : v8::False());
if (base->_info._options) {
result->Set(v8g->CreateOptionsKey, TRI_ObjectJson(base->_info._options)->ToObject());
result->Set(v8g->IsSystemKey, base->_info._isSystem ? v8::True() : v8::False());
result->Set(v8g->JournalSizeKey, v8::Number::New(base->_info._maximalSize));
if (keyOptions != 0) {
result->Set(v8g->KeyOptionsKey, TRI_ObjectJson(keyOptions)->ToObject());
}
else {
result->Set(v8g->KeyOptionsKey, v8::Array::New());
}
result->Set(v8g->WaitForSyncKey, base->_info._waitForSync ? v8::True() : v8::False());
if (keyOptions != 0) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, keyOptions);
}
}
ReleaseCollection(collection);
@ -5588,6 +5615,21 @@ static v8::Handle<v8::Value> JS_CompletionsVocbase (v8::Arguments const& argv) {
/// enforce any synchronisation to disk and does not calculate any CRC
/// checksums for datafiles (as there are no datafiles).
///
/// - @LIT{keyOptions} (optional) additional options for key generation. If
/// specified, then @LIT{keyOptions} should be a JSON array containing the
/// following attributes (note: some of them are optional):
/// - @LIT{type}: specifies the type of the key generator. The currently
/// available generators are @LIT{traditional} and @LIT{autoincrement}.
/// - @LIT{allowUserKeys}: if set to @LIT{true}, then it is allowed to supply
/// own key values in the @LIT{_key} attribute of a document. If set to
/// @LIT{false}, then the key generator will solely be responsible for
/// generating keys and supplying own key values in the @LIT{_key} attribute
/// of documents is considered an error.
/// - @LIT{increment}: increment value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
/// - @LIT{offset}: initial offset value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
///
/// @EXAMPLES
///
/// With defaults:
@ -6441,10 +6483,11 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle<v8::Context> context,
// keys
// .............................................................................
v8g->IsSystemKey = v8::Persistent<v8::String>::New(v8::String::New("isSystem"));
v8g->IsVolatileKey = v8::Persistent<v8::String>::New(v8::String::New("isVolatile"));
v8g->JournalSizeKey = v8::Persistent<v8::String>::New(v8::String::New("journalSize"));
v8g->KeyOptionsKey = v8::Persistent<v8::String>::New(v8::String::New("keyOptions"));
v8g->WaitForSyncKey = v8::Persistent<v8::String>::New(v8::String::New("waitForSync"));
v8g->CreateOptionsKey = v8::Persistent<v8::String>::New(v8::String::New("createOptions"));
if (v8g->DidKey.IsEmpty()) {
v8g->DidKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("_id"));

View File

@ -480,32 +480,26 @@ void TRI_InitCollectionInfo (TRI_vocbase_t* vocbase,
char const* name,
TRI_col_type_e type,
TRI_voc_size_t maximalSize,
TRI_json_t* options) {
TRI_json_t* keyOptions) {
assert(parameter);
memset(parameter, 0, sizeof(TRI_col_info_t));
parameter->_version = TRI_COL_VERSION;
parameter->_type = type;
parameter->_cid = 0;
parameter->_rid = 0;
parameter->_version = TRI_COL_VERSION;
parameter->_type = type;
parameter->_cid = 0;
parameter->_rid = 0;
parameter->_deleted = false;
parameter->_isVolatile = false;
parameter->_isSystem = false;
parameter->_maximalSize = (maximalSize / PageSize) * PageSize;
parameter->_deleted = false;
parameter->_isVolatile = false;
parameter->_isSystem = false;
parameter->_maximalSize = (maximalSize / PageSize) * PageSize;
if (parameter->_maximalSize == 0 && maximalSize != 0) {
parameter->_maximalSize = PageSize;
}
parameter->_waitForSync = vocbase->_defaultWaitForSync;
parameter->_keyOptions = keyOptions;
TRI_CopyString(parameter->_name, name, sizeof(parameter->_name));
parameter->_waitForSync = vocbase->_defaultWaitForSync;
if (options) {
// parameter->_options = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, options);
parameter->_options = options;
}
else {
parameter->_options = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
@ -516,24 +510,25 @@ void TRI_CopyCollectionInfo (TRI_col_info_t* dst, const TRI_col_info_t* const sr
assert(dst);
memset(dst, 0, sizeof(TRI_col_info_t));
dst->_version = src->_version;
dst->_type = src->_type;
dst->_cid = src->_cid;
dst->_rid = src->_rid;
dst->_version = src->_version;
dst->_type = src->_type;
dst->_cid = src->_cid;
dst->_rid = src->_rid;
dst->_deleted = src->_deleted;
dst->_isSystem = src->_isSystem;
dst->_isVolatile = src->_isVolatile;
dst->_maximalSize = src->_maximalSize;
TRI_CopyString(dst->_name, src->_name, sizeof(dst->_name));
dst->_waitForSync = src->_waitForSync;
dst->_deleted = src->_deleted;
dst->_isSystem = src->_isSystem;
dst->_isVolatile = src->_isVolatile;
dst->_maximalSize = src->_maximalSize;
dst->_waitForSync = src->_waitForSync;
if (src->_options) {
dst->_options = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, src->_options);
if (src->_keyOptions) {
dst->_keyOptions = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, src->_keyOptions);
}
else {
dst->_options = NULL;
dst->_keyOptions = NULL;
}
TRI_CopyString(dst->_name, src->_name, sizeof(dst->_name));
}
////////////////////////////////////////////////////////////////////////////////
@ -541,9 +536,9 @@ void TRI_CopyCollectionInfo (TRI_col_info_t* dst, const TRI_col_info_t* const sr
////////////////////////////////////////////////////////////////////////////////
void TRI_FreeCollectionInfoOptions (TRI_col_info_t* parameter) {
if (parameter->_options) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameter->_options);
parameter->_options = NULL;
if (parameter->_keyOptions != NULL) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, parameter->_keyOptions);
parameter->_keyOptions = NULL;
}
}
@ -823,8 +818,8 @@ int TRI_LoadCollectionInfo (char const* path, TRI_col_info_t* parameter) {
}
}
else if (key->_type == TRI_JSON_STRING && value->_type == TRI_JSON_ARRAY) {
if (TRI_EqualString(key->_value._string.data, "options")) {
parameter->_options = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, value);
if (TRI_EqualString(key->_value._string.data, "keyOptions")) {
parameter->_keyOptions = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, value);
}
}
}
@ -858,17 +853,17 @@ int TRI_SaveCollectionInfo (char const* path, const TRI_col_info_t* const info)
return TRI_ERROR_OUT_OF_MEMORY;
}
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "version", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_version));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_type));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "cid", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_cid));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "deleted", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_deleted));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "maximalSize", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_maximalSize));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "name", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, info->_name));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "isVolatile", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_isVolatile));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "waitForSync", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_waitForSync));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "version", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_version));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_type));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "cid", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_cid));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "deleted", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_deleted));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "maximalSize", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, info->_maximalSize));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "name", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, info->_name));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "isVolatile", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_isVolatile));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "waitForSync", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, info->_waitForSync));
if (info->_options) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "options", TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, info->_options));
if (info->_keyOptions) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "keyOptions", TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, info->_keyOptions));
}
// save json info to file

View File

@ -222,7 +222,7 @@ typedef struct TRI_col_info_s {
TRI_voc_size_t _maximalSize; // maximal size of memory mapped file
char _name[TRI_COL_PATH_LENGTH]; // name of the collection
struct TRI_json_s* _options; // optional collection options
struct TRI_json_s* _keyOptions; // options for key creation
// flags
bool _deleted : 1; // if true, collection has been deleted

View File

@ -1320,9 +1320,11 @@ static bool OpenIterator (TRI_df_marker_t const* marker, void* data, TRI_datafil
TRI_primary_collection_t* primary;
TRI_doc_mptr_t const* found;
TRI_doc_datafile_info_t* dfi;
TRI_key_generator_t* keyGenerator;
TRI_voc_key_t key = NULL;
primary = &collection->base;
keyGenerator = primary->_keyGenerator;
CollectionRevisionUpdate(collection, marker);
@ -1366,6 +1368,10 @@ static bool OpenIterator (TRI_df_marker_t const* marker, void* data, TRI_datafil
primary->base._maximumMarkerSize = markerSize;
}
if (keyGenerator->track != NULL) {
keyGenerator->track(keyGenerator, key);
}
found = TRI_LookupByKeyAssociativePointer(&primary->_primaryIndex, key);
// it is a new entry
@ -1456,6 +1462,10 @@ static bool OpenIterator (TRI_df_marker_t const* marker, void* data, TRI_datafil
(char*) key,
(unsigned long long) d->_rid,
(unsigned long) marker->_tick);
if (keyGenerator->track != NULL) {
keyGenerator->track(keyGenerator, key);
}
found = TRI_LookupByKeyAssociativePointer(&primary->_primaryIndex, key);
@ -1727,6 +1737,7 @@ static bool InitDocumentCollection (TRI_document_collection_t* collection,
TRI_FreeIndex(idx);
}
TRI_DestroyVectorPointer(&collection->_allIndexes);
TRI_DestroyPrimaryCollection(&collection->base);
return false;
@ -1778,6 +1789,7 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
TRI_collection_t* collection;
TRI_shaper_t* shaper;
TRI_document_collection_t* document;
TRI_key_generator_t* keyGenerator;
int res;
bool waitForSync;
bool isVolatile;
@ -1789,11 +1801,24 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
cid = TRI_NewTickVocBase();
}
parameter->_cid = cid;
// check if we can generate the key generator
res = TRI_CreateKeyGenerator(parameter->_keyOptions, &keyGenerator);
if (res != TRI_ERROR_NO_ERROR) {
TRI_set_errno(res);
LOG_ERROR("cannot create document collection");
return NULL;
}
assert(keyGenerator != NULL);
// first create the document collection
document = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_document_collection_t), false);
if (document == NULL) {
TRI_FreeKeyGenerator(keyGenerator);
LOG_ERROR("cannot create document collection");
return NULL;
}
@ -1801,6 +1826,7 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
collection = TRI_CreateCollection(vocbase, &document->base.base, path, parameter);
if (collection == NULL) {
TRI_FreeKeyGenerator(keyGenerator);
LOG_ERROR("cannot create document collection");
TRI_Free(TRI_UNKNOWN_MEM_ZONE, document);
@ -1817,6 +1843,7 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
if (shaper == NULL) {
LOG_ERROR("cannot create shapes collection");
TRI_FreeKeyGenerator(keyGenerator);
TRI_CloseCollection(collection);
TRI_FreeCollection(collection); // will free document
@ -1825,15 +1852,18 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
// create document collection and shaper
if (false == InitDocumentCollection(document, shaper)) {
LOG_ERROR("cannot initialise shapes collection");
LOG_ERROR("cannot initialise document collection");
// TODO: shouldn't we destroy &document->_allIndexes, free document->_headers etc.?
TRI_FreeKeyGenerator(keyGenerator);
TRI_CloseCollection(collection);
TRI_FreeCollection(collection); // will free document
TRI_Free(TRI_UNKNOWN_MEM_ZONE, collection); // will free document
return NULL;
}
document->base._keyGenerator = keyGenerator;
// save the parameter block (within create, no need to lock)
res = TRI_SaveCollectionInfo(collection->_directory, parameter);
if (res != TRI_ERROR_NO_ERROR) {
@ -1925,7 +1955,9 @@ TRI_document_collection_t* TRI_OpenDocumentCollection (TRI_vocbase_t* vocbase, c
TRI_shaper_t* shaper;
TRI_document_collection_t* document;
TRI_shape_collection_t* shapeCollection;
TRI_key_generator_t* keyGenerator;
char* shapes;
int res;
// first open the document collection
document = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_document_collection_t), false);
@ -1972,6 +2004,22 @@ TRI_document_collection_t* TRI_OpenDocumentCollection (TRI_vocbase_t* vocbase, c
return NULL;
}
// check if we can generate the key generator
res = TRI_CreateKeyGenerator(collection->_info._keyOptions, &keyGenerator);
if (res != TRI_ERROR_NO_ERROR) {
TRI_set_errno(res);
LOG_ERROR("cannot initialise document collection");
TRI_CloseCollection(collection);
TRI_FreeCollection(collection);
return NULL;
}
assert(keyGenerator != NULL);
document->base._keyGenerator = keyGenerator;
assert(shaper);
shapeCollection = TRI_CollectionVocShaper(shaper);
if (shapeCollection != NULL) {
@ -4916,14 +4964,14 @@ int TRI_InitMarker (TRI_doc_document_key_marker_t* marker,
// create key using key generator
res = keyGenerator->generate(keyGenerator,
TRI_VOC_KEY_MAX_LENGTH,
marker,
marker->_rid,
key,
(char*) &keyBuffer,
&keySize);
if (res != TRI_ERROR_NO_ERROR) {
// key generation failed
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
return res;
}
keySize += 1;

View File

@ -35,7 +35,6 @@
#include "BasicsC/strings.h"
#include "BasicsC/voc-errors.h"
#include "VocBase/primary-collection.h"
#include "VocBase/vocbase.h"
// -----------------------------------------------------------------------------
@ -43,7 +42,26 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- REVISION KEY GENERATOR
// --SECTION-- private types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief available key generators
////////////////////////////////////////////////////////////////////////////////
typedef enum {
TYPE_UNKNOWN = 0,
TYPE_TRADITIONAL = 1,
TYPE_AUTOINCREMENT = 2
}
generator_type_e;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- TRADITIONAL KEY GENERATOR
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
@ -56,17 +74,20 @@
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief revision keygen private data
/// @brief traditional keygen private data
////////////////////////////////////////////////////////////////////////////////
typedef struct revision_keygen_s {
char* _prefix; // key prefix
size_t _prefixLength; // length of key prefix
size_t _padLength; // length of 0 bytes used for left padding
typedef struct traditional_keygen_s {
regex_t _regex; // regex of allowed keys
bool _allowUserKeys; // allow keys supplied by user?
}
revision_keygen_t;
traditional_keygen_t;
////////////////////////////////////////////////////////////////////////////////
/// @brief name of traditional key generator
////////////////////////////////////////////////////////////////////////////////
static const char* TraditionalName = "traditional";
////////////////////////////////////////////////////////////////////////////////
/// @}
@ -82,52 +103,32 @@ revision_keygen_t;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief initialise the revision key generator
/// @brief initialise the traditional key generator
////////////////////////////////////////////////////////////////////////////////
static int RevisionInit (TRI_key_generator_t* const generator,
const TRI_json_t* const options) {
revision_keygen_t* data;
static int TraditionalInit (TRI_key_generator_t* const generator,
const TRI_json_t* const options) {
traditional_keygen_t* data;
data = (revision_keygen_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(revision_keygen_t), false);
data = (traditional_keygen_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(traditional_keygen_t), false);
if (data == NULL) {
return TRI_ERROR_OUT_OF_MEMORY;
}
// defaults
data->_prefix = NULL;
data->_prefixLength = 0;
data->_padLength = 0;
data->_allowUserKeys = true;
// compile regex for key validation
if (regcomp(&data->_regex, "^(" TRI_VOC_KEY_REGEX ")$", REG_EXTENDED | REG_NOSUB) != 0) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
LOG_ERROR("cannot compile regular expression");
return TRI_ERROR_INTERNAL;
}
if (options != NULL) {
TRI_json_t* option;
option = TRI_LookupArrayJson(options, "prefix");
if (option != NULL && option->_type == TRI_JSON_STRING) {
data->_prefix = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, option->_value._string.data);
if (data->_prefix == NULL) {
// OOM
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
return TRI_ERROR_NO_ERROR;
}
data->_prefixLength = strlen(data->_prefix);
}
option = TRI_LookupArrayJson(options, "pad");
if (option != NULL && option->_type == TRI_JSON_NUMBER) {
data->_padLength = (size_t) option->_value._number;
}
option = TRI_LookupArrayJson(options, "allowUserKeys");
if (option != NULL && option->_type == TRI_JSON_BOOLEAN) {
data->_allowUserKeys = option->_value._boolean;
@ -135,27 +136,25 @@ static int RevisionInit (TRI_key_generator_t* const generator,
}
generator->_data = (void*) data;
LOG_TRACE("created traditional key-generator with options (allowUserKeys: %d)",
(int) data->_allowUserKeys);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free the revision key generator
/// @brief free the traditional key generator
////////////////////////////////////////////////////////////////////////////////
static void RevisionFree (TRI_key_generator_t* const generator) {
revision_keygen_t* data;
static void TraditionalFree (TRI_key_generator_t* const generator) {
traditional_keygen_t* data;
data = (revision_keygen_t*) generator->_data;
data = (traditional_keygen_t*) generator->_data;
if (data != NULL) {
// free regex
regfree(&data->_regex);
// free prefix string
if (data->_prefix != NULL) {
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, data->_prefix);
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
}
}
@ -166,16 +165,16 @@ static void RevisionFree (TRI_key_generator_t* const generator) {
/// maxLength + 1 bytes
////////////////////////////////////////////////////////////////////////////////
static int RevisionKey (TRI_key_generator_t* const generator,
const size_t maxLength,
const TRI_doc_document_key_marker_t* const marker,
const char* const userKey,
char* const outBuffer,
size_t* const outLength) {
revision_keygen_t* data;
static int TraditionalGenerate (TRI_key_generator_t* const generator,
const size_t maxLength,
const TRI_voc_rid_t rid,
const char* const userKey,
char* const outBuffer,
size_t* const outLength) {
traditional_keygen_t* data;
char* current;
data = (revision_keygen_t*) generator->_data;
data = (traditional_keygen_t*) generator->_data;
assert(data != NULL);
current = outBuffer;
@ -204,36 +203,8 @@ static int RevisionKey (TRI_key_generator_t* const generator,
current += userKeyLength;
}
else {
// user has not specified a key, generate one
TRI_voc_rid_t revision = marker->_rid;
if (data->_prefix != NULL && data->_prefixLength > 0) {
// copy the prefix
memcpy(current, data->_prefix, data->_prefixLength);
current += data->_prefixLength;
}
if (data->_padLength == 0) {
current += TRI_StringUInt64InPlace(revision, current);
}
else {
char numBuffer[22]; // a uint64 cannot be longer than this
size_t length;
length = TRI_StringUInt64InPlace(revision, (char*) &numBuffer);
if (length < data->_padLength) {
// pad with 0s
size_t padLength;
padLength = data->_padLength - length;
memset(current, '0', padLength);
current += padLength;
}
memcpy(current, (char*) &numBuffer, length);
current += length;
}
// user has not specified a key, generate one based on rid
current += TRI_StringUInt64InPlace(rid, outBuffer);
}
// add 0 byte
@ -248,16 +219,37 @@ static int RevisionKey (TRI_key_generator_t* const generator,
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a JSON representation of the key generator
////////////////////////////////////////////////////////////////////////////////
static TRI_json_t* TraditionalToJson (const TRI_key_generator_t* const generator) {
TRI_json_t* json;
traditional_keygen_t* data;
data = (traditional_keygen_t*) generator->_data;
assert(data != NULL);
json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
if (json != NULL) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TraditionalName));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "allowUserKeys", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, data->_allowUserKeys));
}
return json;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- KEY GENERATOR
// --SECTION-- AUTO-INCREMENT KEY GENERATOR
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private static variables
// --SECTION-- private types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
@ -266,10 +258,35 @@ static int RevisionKey (TRI_key_generator_t* const generator,
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief name of traditional key generator
/// @brief maximum allowed value for increment value
////////////////////////////////////////////////////////////////////////////////
static const char* Traditional = "traditional";
#define AUTOINCREMENT_MAX_INCREMENT (1ULL << 16)
////////////////////////////////////////////////////////////////////////////////
/// @brief maximum value for offset value
////////////////////////////////////////////////////////////////////////////////
#define AUTOINCREMENT_MAX_OFFSET (UINT64_MAX)
////////////////////////////////////////////////////////////////////////////////
/// @brief autoincrement keygen private data
////////////////////////////////////////////////////////////////////////////////
typedef struct autoincrement_keygen_s {
uint64_t _lastValue; // last value assigned
uint64_t _offset; // start value
uint64_t _increment; // increment value
regex_t _regex; // regex of allowed keys
bool _allowUserKeys; // allow keys supplied by user?
}
autoincrement_keygen_t;
////////////////////////////////////////////////////////////////////////////////
/// @brief name of auto-increment key generator
////////////////////////////////////////////////////////////////////////////////
static const char* AutoIncrementName = "autoincrement";
////////////////////////////////////////////////////////////////////////////////
/// @}
@ -285,62 +302,319 @@ static const char* Traditional = "traditional";
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief check whether the generaror type name is valid
/// @brief initialise the autoincrement key generator
////////////////////////////////////////////////////////////////////////////////
static bool ValidType (const char* const name) {
if (TRI_EqualString(name, Traditional)) {
return true;
static int AutoIncrementInit (TRI_key_generator_t* const generator,
const TRI_json_t* const options) {
autoincrement_keygen_t* data;
data = (autoincrement_keygen_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(autoincrement_keygen_t), false);
if (data == NULL) {
return TRI_ERROR_OUT_OF_MEMORY;
}
return false;
// defaults
data->_allowUserKeys = true;
data->_lastValue = 0;
data->_offset = 0;
data->_increment = 1;
// compile regex for key validation
if (regcomp(&data->_regex, "^(" TRI_VOC_ID_REGEX ")$", REG_EXTENDED | REG_NOSUB) != 0) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
LOG_ERROR("cannot compile regular expression");
return TRI_ERROR_INTERNAL;
}
if (options != NULL) {
TRI_json_t* option;
option = TRI_LookupArrayJson(options, "allowUserKeys");
if (option != NULL && option->_type == TRI_JSON_BOOLEAN) {
data->_allowUserKeys = option->_value._boolean;
}
option = TRI_LookupArrayJson(options, "increment");
if (option != NULL && option->_type == TRI_JSON_NUMBER) {
data->_increment = (uint64_t) option->_value._number;
if (data->_increment == 0 || data->_increment >= AUTOINCREMENT_MAX_INCREMENT) {
regfree(&data->_regex);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
return TRI_ERROR_BAD_PARAMETER;
}
}
option = TRI_LookupArrayJson(options, "offset");
if (option != NULL && option->_type == TRI_JSON_NUMBER) {
data->_offset = (uint64_t) option->_value._number;
if (data->_offset >= AUTOINCREMENT_MAX_OFFSET) {
regfree(&data->_regex);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
return TRI_ERROR_BAD_PARAMETER;
}
}
}
generator->_data = (void*) data;
LOG_TRACE("created autoincrement key-generator with options (allowUserKeys: %d, increment: %llu, offset: %llu)",
(int) data->_allowUserKeys,
(unsigned long long) data->_increment,
(unsigned long long) data->_offset);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free the autoincrement key generator
////////////////////////////////////////////////////////////////////////////////
static void AutoIncrementFree (TRI_key_generator_t* const generator) {
autoincrement_keygen_t* data;
data = (autoincrement_keygen_t*) generator->_data;
if (data != NULL) {
// free regex
regfree(&data->_regex);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, data);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate the next auto-increment value based on 3 parameters:
/// - last (highest) value assigned (can be 0)
/// - increment
/// - offset (start value)
////////////////////////////////////////////////////////////////////////////////
static uint64_t AutoIncrementNext (const uint64_t lastValue,
const uint64_t increment,
const uint64_t offset) {
uint64_t next;
if (lastValue < offset) {
return offset;
}
next = lastValue + increment - ((lastValue - offset) % increment);
// TODO: can re move the following?
if (next < offset) {
next = offset;
}
return next;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate a new key
/// the caller must make sure that the outBuffer is big enough to hold at least
/// maxLength + 1 bytes
////////////////////////////////////////////////////////////////////////////////
static int AutoIncrementGenerate (TRI_key_generator_t* const generator,
const size_t maxLength,
const TRI_voc_rid_t rid,
const char* const userKey,
char* const outBuffer,
size_t* const outLength) {
autoincrement_keygen_t* data;
char* current;
data = (autoincrement_keygen_t*) generator->_data;
assert(data != NULL);
current = outBuffer;
if (userKey != NULL) {
uint64_t userKeyValue;
size_t userKeyLength;
// user has specified a key
if (! data->_allowUserKeys) {
// we do not allow user-generated keys
return TRI_ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED;
}
userKeyLength = strlen(userKey);
if (userKeyLength > maxLength) {
// user key is too long
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
// validate user-supplied key
if (regexec(&data->_regex, userKey, 0, NULL, 0) != 0) {
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
memcpy(outBuffer, userKey, userKeyLength);
current += userKeyLength;
userKeyValue = TRI_UInt64String2(userKey, userKeyLength);
if (userKeyValue > data->_lastValue) {
// update our last value
data->_lastValue = userKeyValue;
}
}
else {
// user has not specified a key, generate one based on rid
uint64_t keyValue = AutoIncrementNext(data->_lastValue, data->_increment, data->_offset);
// bounds and sanity checks
if (keyValue == UINT64_MAX || keyValue < data->_lastValue) {
return TRI_ERROR_ARANGO_OUT_OF_KEYS;
}
assert(keyValue > data->_lastValue);
// update our last value
data->_lastValue = keyValue;
current += TRI_StringUInt64InPlace(keyValue, outBuffer);
}
// add 0 byte
*current = '\0';
if (current - outBuffer > (int) maxLength) {
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
*outLength = (current - outBuffer);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief track a key while a collection is opened
/// this function is used to update the _lastValue value
////////////////////////////////////////////////////////////////////////////////
static void AutoIncrementTrack (TRI_key_generator_t* const generator,
const TRI_voc_key_t key) {
autoincrement_keygen_t* data;
uint64_t value;
data = (autoincrement_keygen_t*) generator->_data;
assert(data != NULL);
// check the numeric key part
value = TRI_UInt64String(key);
if (value > data->_lastValue) {
// and update our last value
data->_lastValue = value;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a JSON representation of the key generator
////////////////////////////////////////////////////////////////////////////////
static TRI_json_t* AutoIncrementToJson (const TRI_key_generator_t* const generator) {
TRI_json_t* json;
autoincrement_keygen_t* data;
data = (autoincrement_keygen_t*) generator->_data;
assert(data != NULL);
json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
if (json != NULL) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "type", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, AutoIncrementName));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "allowUserKeys", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, data->_allowUserKeys));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "offset", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) data->_offset));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "increment", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) data->_increment));
}
return json;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- GENERAL GENERATOR FUNCTIONS
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief get the generator type from JSON
////////////////////////////////////////////////////////////////////////////////
static const char* GeneratorType (const TRI_json_t* const parameters) {
static generator_type_e GeneratorType (const TRI_json_t* const parameters) {
TRI_json_t* type;
const char* typeName;
if (parameters == NULL || parameters->_type != TRI_JSON_ARRAY) {
return Traditional;
return TYPE_TRADITIONAL;
}
type = TRI_LookupArrayJson(parameters, "type");
if (type == NULL || parameters->_type != TRI_JSON_STRING) {
return Traditional;
if (type == NULL || type->_type != TRI_JSON_STRING) {
return TYPE_TRADITIONAL;
}
return parameters->_value._string.data;
typeName = type->_value._string.data;
if (TRI_CaseEqualString(typeName, TraditionalName)) {
return TYPE_TRADITIONAL;
}
if (TRI_CaseEqualString(typeName, AutoIncrementName)) {
return TYPE_AUTOINCREMENT;
}
// error
return TYPE_UNKNOWN;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new generator
////////////////////////////////////////////////////////////////////////////////
static TRI_key_generator_t* CreateGenerator (const TRI_json_t* const parameters,
TRI_primary_collection_t* const collection) {
static TRI_key_generator_t* CreateGenerator (const TRI_json_t* const parameters) {
TRI_key_generator_t* generator;
const char* type;
generator_type_e type;
type = GeneratorType(parameters);
if (! ValidType(type)) {
if (type == TYPE_UNKNOWN) {
return NULL;
}
generator = (TRI_key_generator_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_key_generator_t), false);
if (generator == NULL) {
return NULL;
}
generator->_data = NULL;
generator->_collection = collection;
if (TRI_EqualString(type, Traditional)) {
generator->init = &RevisionInit;
generator->generate = &RevisionKey;
generator->free = &RevisionFree;
if (type == TYPE_TRADITIONAL) {
generator->init = &TraditionalInit;
generator->generate = &TraditionalGenerate;
generator->free = &TraditionalFree;
generator->track = NULL;
generator->toJson = &TraditionalToJson;
}
else if (type == TYPE_AUTOINCREMENT) {
generator->init = &AutoIncrementInit;
generator->generate = &AutoIncrementGenerate;
generator->free = &AutoIncrementFree;
generator->track = &AutoIncrementTrack;
generator->toJson = &AutoIncrementToJson;
}
return generator;
@ -360,37 +634,37 @@ static TRI_key_generator_t* CreateGenerator (const TRI_json_t* const parameters,
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief create a key generator and attach it to the collection
/// @brief create a key generator
////////////////////////////////////////////////////////////////////////////////
int TRI_CreateKeyGenerator (const TRI_json_t* const parameters,
TRI_primary_collection_t* const collection) {
int TRI_CreateKeyGenerator (const TRI_json_t* const parameters,
TRI_key_generator_t** dst) {
TRI_key_generator_t* generator;
TRI_json_t* options;
const TRI_json_t* options;
int res;
generator = CreateGenerator(parameters, collection);
*dst = NULL;
options = NULL;
if (parameters != NULL && parameters->_type == TRI_JSON_ARRAY) {
options = parameters;
}
generator = CreateGenerator(options);
if (generator == NULL) {
return TRI_ERROR_OUT_OF_MEMORY;
}
options = NULL;
if (parameters != NULL) {
options = TRI_LookupArrayJson(parameters, "options");
if (options != NULL && options->_type != TRI_JSON_ARRAY) {
options = NULL;
}
}
res = generator->init(generator, options);
if (res == TRI_ERROR_NO_ERROR) {
collection->_keyGenerator = generator;
}
else {
if (res != TRI_ERROR_NO_ERROR) {
TRI_FreeKeyGenerator(generator);
return res;
}
return res;
*dst = generator;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
@ -413,3 +687,4 @@ void TRI_FreeKeyGenerator (TRI_key_generator_t* generator) {
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End:

View File

@ -30,6 +30,8 @@
#include "BasicsC/common.h"
#include "VocBase/vocbase.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -42,9 +44,7 @@ extern "C" {
// --SECTION-- FORWARD DECLARATIONS
// -----------------------------------------------------------------------------
struct TRI_doc_document_key_marker_s;
struct TRI_json_s;
struct TRI_primary_collection_s;
// -----------------------------------------------------------------------------
// --SECTION-- public defines
@ -84,13 +84,13 @@ struct TRI_primary_collection_s;
////////////////////////////////////////////////////////////////////////////////
typedef struct TRI_key_generator_s {
struct TRI_json_s* _parameters;
struct TRI_primary_collection_s* _collection;
void* _data;
void* _data;
int (*init)(struct TRI_key_generator_s* const, const struct TRI_json_s* const);
int (*generate)(struct TRI_key_generator_s* const, const size_t, const struct TRI_doc_document_key_marker_s* const, const char* const, char* const, size_t* const);
int (*generate)(struct TRI_key_generator_s* const, const size_t, const TRI_voc_rid_t, const char* const, char* const, size_t* const);
void (*track)(struct TRI_key_generator_s* const, const TRI_voc_key_t);
void (*free)(struct TRI_key_generator_s* const);
struct TRI_json_s* (*toJson)(const struct TRI_key_generator_s* const);
}
TRI_key_generator_t;
@ -108,11 +108,11 @@ TRI_key_generator_t;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief create a key generator and attach it to the collection
/// @brief create a key generator
////////////////////////////////////////////////////////////////////////////////
int TRI_CreateKeyGenerator (const struct TRI_json_s* const,
struct TRI_primary_collection_s* const);
struct TRI_key_generator_s**);
////////////////////////////////////////////////////////////////////////////////
/// @brief free a key generator

View File

@ -442,8 +442,6 @@ static TRI_voc_size_t Count (TRI_primary_collection_t* primary) {
int TRI_InitPrimaryCollection (TRI_primary_collection_t* primary,
TRI_shaper_t* shaper) {
int res;
primary->_shaper = shaper;
primary->_capConstraint = NULL;
primary->_keyGenerator = NULL;
@ -469,10 +467,7 @@ int TRI_InitPrimaryCollection (TRI_primary_collection_t* primary,
TRI_InitReadWriteLock(&primary->_lock);
// init key generator
res = TRI_CreateKeyGenerator(primary->base._info._options, primary);
return res;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -197,7 +197,7 @@ extern size_t PageSize;
/// @brief maximal path length
////////////////////////////////////////////////////////////////////////////////
#define TRI_COL_PATH_LENGTH (4096)
#define TRI_COL_PATH_LENGTH (512)
////////////////////////////////////////////////////////////////////////////////
/// @brief default maximal collection journal size

View File

@ -24,7 +24,7 @@ AC_MSG_NOTICE([with LDFLAGS='$LDFLAGS'])
dnl ----------------------------------------------------------------------------
dnl grab the configure command and store it in m4 variables
dnl grab the configure command, options and flags and store them in m4 variables
dnl ----------------------------------------------------------------------------
if test `expr -- [$]0 : "'.*"` = 0; then
@ -33,19 +33,7 @@ else
TRI_CONFIGURE_COMMAND="$TRI_CONFIGURE_COMMAND [$]0"
fi
for arg in $ac_configure_args; do
if test `expr -- $arg : "'.*"` = 0; then
if test `expr -- $arg : "--.*"` = 0; then
break;
fi
TRI_CONFIGURE_OPTIONS="$TRI_CONFIGURE_OPTIONS '[$]arg'"
else
if test `expr -- $arg : "'--.*"` = 0; then
break;
fi
TRI_CONFIGURE_OPTIONS="$TRI_CONFIGURE_OPTIONS [$]arg"
fi
done
TRI_CONFIGURE_OPTIONS="$ac_configure_args"
for var in CFLAGS CXXFLAGS CPPFLAGS LDFLAGS EXTRA_LDFLAGS_PROGRAM LIBS CC CXX; do
eval val=\$$var

View File

@ -38,8 +38,6 @@ var output = arangodb.output;
var sprintf = arangodb.sprintf;
var db = arangodb.db;
var colors = require("internal").colors;
var simple = require("org/arangodb/simple-query");
var SimpleQueryAll = simple.SimpleQueryAll;
@ -146,6 +144,8 @@ ArangoCollection.prototype._PRINT = function () {
case ArangoCollection.TYPE_EDGE: type = "edge"; break;
}
var colors = require("internal").colors;
output("[ArangoCollection ",
colors.COLOR_STRING, this._id, colors.COLOR_RESET,
", \"",

View File

@ -359,8 +359,8 @@ ArangoCollection.prototype.properties = function (properties) {
isVolatile : requestResult.isVolatile
};
if (requestResult.createOptions !== undefined) {
result.createOptions = requestResult.createOptions;
if (requestResult.keyOptions !== undefined) {
result.keyOptions = requestResult.keyOptions;
}
return result;

View File

@ -348,8 +348,8 @@ ArangoDatabase.prototype._create = function (name, properties, type) {
body.isVolatile = properties.isVolatile;
}
if (properties.hasOwnProperty("createOptions")) {
body.createOptions = properties.createOptions;
if (properties.hasOwnProperty("keyOptions")) {
body.keyOptions = properties.keyOptions;
}
}

View File

@ -55,11 +55,11 @@ function collectionRepresentation (collection, showProperties, showCount, showFi
if (showProperties) {
var properties = collection.properties();
result.waitForSync = properties.waitForSync;
result.journalSize = properties.journalSize;
result.createOptions = properties.createOptions;
result.isVolatile = properties.isVolatile;
result.isSystem = properties.isSystem;
result.journalSize = properties.journalSize;
result.keyOptions = properties.keyOptions;
result.waitForSync = properties.waitForSync;
}
if (showCount) {
@ -132,7 +132,20 @@ function collectionRepresentation (collection, showProperties, showCount, showFi
/// This option should threrefore be used for cache-type collections only,
/// and not for data that cannot be re-created otherwise.
///
/// - @LIT{options} (optional) Additional collection options
/// - @LIT{keyOptions} (optional) additional options for key generation. If
/// specified, then @LIT{keyOptions} should be a JSON array containing the
/// following attributes (note: some of them are optional):
/// - @LIT{type}: specifies the type of the key generator. The currently
/// available generators are @LIT{traditional} and @LIT{autoincrement}.
/// - @LIT{allowUserKeys}: if set to @LIT{true}, then it is allowed to supply
/// own key values in the @LIT{_key} attribute of a document. If set to
/// @LIT{false}, then the key generator will solely be responsible for
/// generating keys and supplying own key values in the @LIT{_key} attribute
/// of documents is considered an error.
/// - @LIT{increment}: increment value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
/// - @LIT{offset}: initial offset value for @LIT{autoincrement} key generator.
/// Not used for other key generator types.
///
/// - @LIT{type} (optional, default is @LIT{2}): the type of the collection to
/// create. The following values for @FA{type} are valid:
@ -185,9 +198,9 @@ function post_api_collection (req, res) {
if (body.hasOwnProperty("type")) {
type = body.type;
}
if (body.hasOwnProperty("createOptions")) {
parameter.createOptions = body.createOptions;
if (body.hasOwnProperty("keyOptions")) {
parameter.keyOptions = body.keyOptions;
}
try {
@ -210,7 +223,7 @@ function post_api_collection (req, res) {
result.isSystem = parameter.isSystem || false;
result.status = collection.status();
result.type = collection.type();
result.createOptions = collection.createOptions;
result.keyOptions = collection.keyOptions;
headers.location = "/" + API + "/" + result.name;

View File

@ -358,8 +358,8 @@ ArangoCollection.prototype.properties = function (properties) {
isVolatile : requestResult.isVolatile
};
if (requestResult.createOptions !== undefined) {
result.createOptions = requestResult.createOptions;
if (requestResult.keyOptions !== undefined) {
result.keyOptions = requestResult.keyOptions;
}
return result;

View File

@ -347,8 +347,8 @@ ArangoDatabase.prototype._create = function (name, properties, type) {
body.isVolatile = properties.isVolatile;
}
if (properties.hasOwnProperty("createOptions")) {
body.createOptions = properties.createOptions;
if (properties.hasOwnProperty("keyOptions")) {
body.keyOptions = properties.keyOptions;
}
}

View File

@ -78,6 +78,7 @@
"ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED" : { "code" : 1222, "message" : "unexpected document key" },
"ERROR_ARANGO_INDEX_NEEDS_RESIZE" : { "code" : 1223, "message" : "index needs resizing" },
"ERROR_ARANGO_DATADIR_NOT_WRITABLE" : { "code" : 1224, "message" : "database directory not writable" },
"ERROR_ARANGO_OUT_OF_KEYS" : { "code" : 1225, "message" : "out of keys" },
"ERROR_ARANGO_DATAFILE_FULL" : { "code" : 1300, "message" : "datafile full" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },

View File

@ -0,0 +1,588 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief test the key generators
///
/// @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 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var wait = require("internal").wait;
var console = require("console");
var arangodb = require("org/arangodb");
var ArangoCollection = arangodb.ArangoCollection;
var db = arangodb.db;
var ERRORS = arangodb.errors;
// -----------------------------------------------------------------------------
// --SECTION-- auto-increment key generator
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: auto-increment
////////////////////////////////////////////////////////////////////////////////
function AutoIncrementSuite () {
var cn = "UnitTestsKeyGen";
return {
setUp : function () {
db._drop(cn);
},
tearDown : function () {
db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with invalid offset
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidOffset : function () {
try {
db._create(cn, { keyOptions: { type: "autoincrement", offset: -1 } });
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with invalid increment
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidIncrement1 : function () {
try {
db._create(cn, { keyOptions: { type: "autoincrement", increment: 0 } });
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with invalid increment
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidIncrement2 : function () {
try {
db._create(cn, { keyOptions: { type: "autoincrement", increment: -1 } });
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with invalid increment
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidIncrement2 : function () {
try {
db._create(cn, { keyOptions: { type: "autoincrement", increment: 9999999999999 } });
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidKey1 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: false } });
try {
c.save({ _key: "1234" }); // no user keys allowed
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidKey2 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: true } });
try {
c.save({ _key: "a1234" }); // invalid key
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_BAD.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk1 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 12345678901234567 } });
var options = c.properties().keyOptions;
assertEqual("autoincrement", options.type);
assertEqual(true, options.allowUserKeys);
assertEqual(1, options.increment);
assertEqual(12345678901234567, options.offset);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk2 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement" } });
var options = c.properties().keyOptions;
assertEqual("autoincrement", options.type);
assertEqual(true, options.allowUserKeys);
assertEqual(1, options.increment);
assertEqual(0, options.offset);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk3 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: false, offset: 83, increment: 156 } });
var options = c.properties().keyOptions;
assertEqual("autoincrement", options.type);
assertEqual(false, options.allowUserKeys);
assertEqual(156, options.increment);
assertEqual(83, options.offset);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with user key
////////////////////////////////////////////////////////////////////////////////
testCheckUserKey : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: true } });
var d1 = c.save({ _key: "1234" });
assertEqual("1234", d1._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValues1 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 2, increment: 3 } });
var d1 = c.save({ });
assertEqual("2", d1._key);
var d2 = c.save({ });
assertEqual("5", d2._key);
var d3 = c.save({ });
assertEqual("8", d3._key);
var d4 = c.save({ });
assertEqual("11", d4._key);
var d5 = c.save({ });
assertEqual("14", d5._key);
// create a gap
var d6 = c.save({ _key: "100" });
assertEqual("100", d6._key);
var d7 = c.save({ });
assertEqual("101", d7._key);
var d8 = c.save({ });
assertEqual("104", d8._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValues2 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 19, increment: 1 } });
var d1 = c.save({ });
assertEqual("19", d1._key);
var d2 = c.save({ });
assertEqual("20", d2._key);
var d3 = c.save({ });
assertEqual("21", d3._key);
var d4 = c.save({ });
assertEqual("22", d4._key);
var d5 = c.save({ });
assertEqual("23", d5._key);
// create a gap
var d6 = c.save({ _key: "99" });
assertEqual("99", d6._key);
var d7 = c.save({ });
assertEqual("100", d7._key);
var d8 = c.save({ });
assertEqual("101", d8._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values after unloading etc.
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValuesUnload1 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 1, increment: 4 } });
var d1 = c.save({ });
assertEqual("1", d1._key);
var d2 = c.save({ });
assertEqual("5", d2._key);
var d3 = c.save({ });
assertEqual("9", d3._key);
var d4 = c.save({ });
assertEqual("13", d4._key);
c.unload();
console.log("waiting for collection to unload");
wait(5);
assertEqual(ArangoCollection.STATUS_UNLOADED, c.status());
d1 = c.save({ });
assertEqual("17", d1._key);
d2 = c.save({ });
assertEqual("21", d2._key);
d3 = c.save({ });
assertEqual("25", d3._key);
d4 = c.save({ });
assertEqual("29", d4._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values after unloading etc.
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValuesUnload2 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 0, increment: 2 } });
var d1 = c.save({ });
assertEqual("2", d1._key);
var d2 = c.save({ });
assertEqual("4", d2._key);
var d3 = c.save({ });
assertEqual("6", d3._key);
var d4 = c.save({ });
assertEqual("8", d4._key);
c.unload();
console.log("waiting for collection to unload");
wait(5);
assertEqual(ArangoCollection.STATUS_UNLOADED, c.status());
d1 = c.save({ });
assertEqual("10", d1._key);
// create a gap
d2 = c.save({ _key: "39" });
assertEqual("39", d2._key);
d3 = c.save({ });
assertEqual("40", d3._key);
// create a gap
d4 = c.save({ _key: "19567" });
assertEqual("19567", d4._key);
c.unload();
console.log("waiting for collection to unload");
wait(5);
assertEqual(ArangoCollection.STATUS_UNLOADED, c.status());
d1 = c.save({ });
assertEqual("19568", d1._key);
d2 = c.save({ });
assertEqual("19570", d2._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValuesMixedWithUserKeys : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 0, increment: 1 } });
var d1 = c.save({ });
assertEqual("1", d1._key);
var d2 = c.save({ });
assertEqual("2", d2._key);
var d3 = c.save({ _key: "100" });
assertEqual("100", d3._key);
var d4 = c.save({ _key: "3" });
assertEqual("3", d4._key);
var d5 = c.save({ _key: "99" });
assertEqual("99", d5._key);
var d6 = c.save({ });
assertEqual("101", d6._key);
var d7 = c.save({ });
assertEqual("102", d7._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValuesDuplicates : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", offset: 0, increment: 1 } });
var d1 = c.save({ });
assertEqual("1", d1._key);
var d2 = c.save({ });
assertEqual("2", d2._key);
try {
c.save({ _key: "1" });
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test out of keys
////////////////////////////////////////////////////////////////////////////////
testOutOfKeys1 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: true, offset: 0, increment: 1 } });
var d1 = c.save({ _key: "18446744073709551615" }); // still valid
assertEqual("18446744073709551615", d1._key);
try {
c.save({ }); // should raise out of keys
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_OUT_OF_KEYS.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test out of keys
////////////////////////////////////////////////////////////////////////////////
testOutOfKeys2 : function () {
var c = db._create(cn, { keyOptions: { type: "autoincrement", allowUserKeys: true, offset: 0, increment: 10 } });
var d1 = c.save({ _key: "18446744073709551615" }); // still valid
assertEqual("18446744073709551615", d1._key);
try {
c.save({ }); // should raise out of keys
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_OUT_OF_KEYS.code, err.errorNum);
}
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- traditional key generator
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: traditional key gen
////////////////////////////////////////////////////////////////////////////////
function TraditionalSuite () {
var cn = "UnitTestsKeyGen";
return {
setUp : function () {
db._drop(cn);
},
tearDown : function () {
db._drop(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidKey1 : function () {
var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: false } });
try {
c.save({ _key: "1234" }); // no user keys allowed
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////
testCreateInvalidKey2 : function () {
var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });
try {
c.save({ _key: "öä .mds 3 -6" }); // invalid key
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_BAD.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk1 : function () {
var c = db._create(cn, { keyOptions: { type: "traditional" } });
var options = c.properties().keyOptions;
assertEqual("traditional", options.type);
assertEqual(true, options.allowUserKeys);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk2 : function () {
var c = db._create(cn, { keyOptions: { } });
var options = c.properties().keyOptions;
assertEqual("traditional", options.type);
assertEqual(true, options.allowUserKeys);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////
testCreateOk3 : function () {
var c = db._create(cn, { keyOptions: { allowUserKeys: false } });
var options = c.properties().keyOptions;
assertEqual("traditional", options.type);
assertEqual(false, options.allowUserKeys);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief create with user key
////////////////////////////////////////////////////////////////////////////////
testCheckUserKey : function () {
var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });
var options = c.properties().keyOptions;
assertEqual("traditional", options.type);
assertEqual(true, options.allowUserKeys);
var d1 = c.save({ _key: "1234" });
assertEqual("1234", d1._key);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////
testCheckAutoValues : function () {
var c = db._create(cn, { keyOptions: { type: "traditional" } });
var d1 = parseFloat(c.save({ })._key);
var d2 = parseFloat(c.save({ })._key);
var d3 = parseFloat(c.save({ })._key);
var d4 = parseFloat(c.save({ })._key);
var d5 = parseFloat(c.save({ })._key);
assertTrue(d1 < d2);
assertTrue(d2 < d3);
assertTrue(d3 < d4);
assertTrue(d4 < d5);
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
////////////////////////////////////////////////////////////////////////////////
jsunity.run(AutoIncrementSuite);
jsunity.run(TraditionalSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -96,6 +96,7 @@ ERROR_ARANGO_DOCUMENT_KEY_BAD,1221,"illegal document key","Will be raised when a
ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED,1222,"unexpected document key","Will be raised when a user-defined document key is supplied for collections with auto key generation."
ERROR_ARANGO_INDEX_NEEDS_RESIZE,1223,"index needs resizing","Will be raised when an index is full and should be resized to contain more data."
ERROR_ARANGO_DATADIR_NOT_WRITABLE,1224,"database directory not writable","Will be raised when the database directory is not writable for the current user."
ERROR_ARANGO_OUT_OF_KEYS,1225,"out of keys","Will be raised when a key generator runs out of keys."
################################################################################
## ArangoDB storage errors

View File

@ -74,6 +74,7 @@ void TRI_InitialiseErrorMessages (void) {
REG_ERROR(ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED, "unexpected document key");
REG_ERROR(ERROR_ARANGO_INDEX_NEEDS_RESIZE, "index needs resizing");
REG_ERROR(ERROR_ARANGO_DATADIR_NOT_WRITABLE, "database directory not writable");
REG_ERROR(ERROR_ARANGO_OUT_OF_KEYS, "out of keys");
REG_ERROR(ERROR_ARANGO_DATAFILE_FULL, "datafile full");
REG_ERROR(ERROR_QUERY_KILLED, "query killed");
REG_ERROR(ERROR_QUERY_PARSE, "%s");

View File

@ -152,6 +152,8 @@ extern "C" {
/// - 1224: @LIT{database directory not writable}
/// Will be raised when the database directory is not writable for the
/// current user.
/// - 1225: @LIT{out of keys}
/// Will be raised when a key generator runs out of keys.
/// - 1300: @LIT{datafile full}
/// Will be raised when the datafile reaches its limit.
/// - 1500: @LIT{query killed}
@ -1017,6 +1019,16 @@ void TRI_InitialiseErrorMessages (void);
#define TRI_ERROR_ARANGO_DATADIR_NOT_WRITABLE (1224)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1225: ERROR_ARANGO_OUT_OF_KEYS
///
/// out of keys
///
/// Will be raised when a key generator runs out of keys.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_ARANGO_OUT_OF_KEYS (1225)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1300: ERROR_ARANGO_DATAFILE_FULL
///

View File

@ -1318,6 +1318,10 @@ v8::Handle<v8::Array> TRI_ArrayAssociativePointer (const TRI_associative_pointer
v8::Handle<v8::Value> TRI_ObjectJson (TRI_json_t const* json) {
v8::HandleScope scope;
if (json == 0) {
return scope.Close(v8::Undefined());
}
switch (json->_type) {
case TRI_JSON_UNUSED:
return scope.Close(v8::Undefined());

View File

@ -90,8 +90,10 @@ typedef struct TRI_v8_global_s {
ToKey(),
BodyKey(),
ContentTypeKey(),
IsSystemKey(),
IsVolatileKey(),
JournalSizeKey(),
KeyOptionsKey(),
ParametersKey(),
PathKey(),
PrefixKey(),
@ -304,6 +306,12 @@ typedef struct TRI_v8_global_s {
v8::Persistent<v8::String> HeadersKey;
////////////////////////////////////////////////////////////////////////////////
/// @brief "isSystem" key name
////////////////////////////////////////////////////////////////////////////////
v8::Persistent<v8::String> IsSystemKey;
////////////////////////////////////////////////////////////////////////////////
/// @brief "isVolatile" key name
////////////////////////////////////////////////////////////////////////////////
@ -316,6 +324,12 @@ typedef struct TRI_v8_global_s {
v8::Persistent<v8::String> JournalSizeKey;
////////////////////////////////////////////////////////////////////////////////
/// @brief "keyOptions" key name
////////////////////////////////////////////////////////////////////////////////
v8::Persistent<v8::String> KeyOptionsKey;
////////////////////////////////////////////////////////////////////////////////
/// @brief "parameters" key name
////////////////////////////////////////////////////////////////////////////////
@ -382,12 +396,6 @@ typedef struct TRI_v8_global_s {
v8::Persistent<v8::String> WaitForSyncKey;
////////////////////////////////////////////////////////////////////////////////
/// @brief "waitForSync" key name
////////////////////////////////////////////////////////////////////////////////
v8::Persistent<v8::String> CreateOptionsKey;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////