mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
4b2cac5ba0
51
CHANGELOG
51
CHANGELOG
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ¶meter, name.c_str(), collectionType, effectiveSize, options);
|
||||
TRI_InitCollectionInfo(vocbase, ¶meter, 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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
16
configure.ac
16
configure.ac
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
", \"",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue