1
0
Fork 0

added client side API for transactions

This commit is contained in:
Jan Steemann 2013-04-12 23:18:48 +02:00
parent 780c2a6caa
commit dee5adb9f5
15 changed files with 929 additions and 89 deletions

View File

@ -44,9 +44,10 @@ is started.
There are no individual `BEGIN`, `COMMIT` or `ROLLBACK` transaction commands
in ArangoDB. Instead, a transaction in ArangoDB is started by providing a
description of the transaction to the `TRANSACTION` Javascript function:
description of the transaction to the `db._executeTransaction` Javascript
function:
TRANSACTION(description);
db._executeTransaction(description);
This function will then automatically start a transaction, execute all required
data retrieval and/or modification operations, and at the end automatically
@ -72,10 +73,10 @@ read operations on a collection. Any attempt to write into a collection used
in read-only mode will make the transaction fail.
Collections for a transaction are declared by providing them in the `collections`
attribute of the object passed to the `TRANSACTION` function. The `collections`
attribute has the sub-attributes `read` and `write`:
attribute of the object passed to the `_executeTransaction` function. The
`collections` attribute has the sub-attributes `read` and `write`:
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "users", "logins" ],
read: [ "recommendations" ],
@ -89,7 +90,7 @@ the operations inside the transactions demand for it.
The contents of `read` or `write` can each be lists with collection names or a
single collection name (as a string):
TRANSACTION({
db._executeTransaction({
collections: {
write: "users",
read: "recommendations",
@ -111,7 +112,7 @@ All data modification and retrieval operations that are to be executed inside
the transaction need to be specified in a Javascript function, using the `action`
attribute:
TRANSACTION({
db._executeTransaction({
collections: {
write: "users",
},
@ -131,7 +132,7 @@ There is no explicit commit command.
To make a transaction abort and roll back all changes, an exception needs to
be thrown and not caught inside the transaction:
TRANSACTION({
db._executeTransaction({
collections: {
write: "users",
},
@ -149,7 +150,7 @@ As mentioned earlier, a transaction will commit automatically when the end of
the `action` function is reached and no exception has been thrown. In this
case, the user can return any legal Javascript value from the function:
TRANSACTION({
db._executeTransaction({
collections: {
write: "users",
},
@ -167,7 +168,7 @@ Examples
The first example will write 3 documents into a collection named `c1`.
The `c1` collection needs to be declared in the `write` attribute of the
`collections` attribute passed to the `TRANSACTION` function.
`collections` attribute passed to the `executeTransaction` function.
The `action` attribute contains the actual transaction code to be executed.
This code contains all data modification operations (3 in this example).
@ -176,7 +177,7 @@ This code contains all data modification operations (3 in this example).
var db = require("internal").db;
db._create("c1");
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1" ]
},
@ -197,7 +198,7 @@ will revert all changes, so as if the transaction never happened:
var db = require("internal").db;
db._create("c1");
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1" ]
},
@ -222,7 +223,7 @@ at some point during transaction execution:
var db = require("internal").db;
db._create("c1");
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1" ]
},
@ -260,7 +261,7 @@ start. The following example using a cap constraint should illustrate that:
db.c1.save({ _key: "key4" });
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1" ]
},
@ -288,7 +289,7 @@ attribute, e.g.:
db._create("c1");
db._create("c2");
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1", "c2" ]
},
@ -310,7 +311,7 @@ transaction abort and roll back all changes in all collections:
db._create("c1");
db._create("c2");
TRANSACTION({
db._executeTransaction({
collections: {
write: [ "c1", "c2" ]
},
@ -340,7 +341,7 @@ Some operations are not allowed inside ArangoDB transactions:
- creation and deletion of indexes (`db.ensure...Index()`, `db.dropIndex()`)
If an attempt is made to carry out any of these operations during a transaction,
ArangoDB will abort the transaction with error code `1655 (disallowed operation inside
ArangoDB will abort the transaction with error code `1653 (disallowed operation inside
transaction)`.
@ -378,7 +379,7 @@ collections are potentially non-repeatable.
Example:
TRANSACTION({
db._executeTransaction({
collections: {
read: "users"
},
@ -445,10 +446,10 @@ whether the delayed synchronisation had kicked in or not.
To ensure durability of transactions on a collection that have the `waitForSync`
property set to `false`, you can set the `waitForSync` attribute of the object
that is passed to `TRANSACTION`. This will force a synchronisation of the
that is passed to `executeTransaction`. This will force a synchronisation of the
transaction to disk even for collections that have `waitForSync´ set to `false`:
TRANSACTION({
db._executeTransaction({
collections: {
write: "users"
},
@ -473,8 +474,8 @@ committed transactions in the case of a crash.
In contrast, transactions that modify data in more than one collection are
automatically synchronised to disk. This comes at the cost of several disk sync
For a multi-collection transaction, the call to the `TRANSACTION` function will
only return only after the data of all modified collections has been synchronised
For a multi-collection transaction, the call to the `_executeTransaction` function
will only return only after the data of all modified collections has been synchronised
to disk and the transaction has been made fully durable. This not only reduces the
risk of losing data in case of a crash but also ensures consistency after a
restart.
@ -510,7 +511,7 @@ into one transaction.
Additionally, transactions in ArangoDB cannot be nested, i.e. a transaction
must not call any other transaction. If an attempt is made to call a transaction
from inside a running transaction, the server will throw error `1652 (nested
from inside a running transaction, the server will throw error `1651 (nested
transactions detected`).
It is also disallowed to execute user transaction on some of ArangoDB's own system
@ -520,9 +521,9 @@ transaction.
Finally, all collections that may be modified during a transaction must be
declared beforehand, i.e. using the `collections` attribute of the object passed
to the `TRANSACTION` function. If any attempt is made to carry out a data
to the `_executeTransaction` function. If any attempt is made to carry out a data
modification operation on a collection that was not declared in the `collections`
attribute, the transaction will be aborted and ArangoDB will throw error `1654
attribute, the transaction will be aborted and ArangoDB will throw error `1652
unregistered collection used in transaction`.
It is legal to not declare read-only collections, but this should be avoided if
possible to reduce the probability of deadlocks and non-repeatable reads.

View File

@ -0,0 +1,447 @@
# coding: utf-8
require 'rspec'
require './arangodb.rb'
describe ArangoDB do
api = "/_api/transaction"
prefix = "api-transaction"
context "running server-side transactions:" do
################################################################################
## error handling
################################################################################
context "error handling:" do
it "returns an error if a wrong method type is used" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \" for \" }"
doc = ArangoDB.log_put("#{prefix}-invalid-method", cmd, :body => body)
doc.code.should eq(405)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(405)
doc.parsed_response['errorNum'].should eq(405)
end
it "returns an error if no body is posted" do
cmd = api
doc = ArangoDB.log_post("#{prefix}-missing-body", cmd)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if no collections attribute is present" do
cmd = api
body = "{ \"foo\" : \"bar\" }"
doc = ArangoDB.log_post("#{prefix}-no-collection", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if the collections attribute has a wrong type" do
cmd = api
doc = ArangoDB.log_post("#{prefix}-collection-wrong-type", cmd)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if collections sub-attribute is wrong" do
cmd = api
body = "{ \"collections\" : { \"write\": false } }"
doc = ArangoDB.log_post("#{prefix}-invalid-collection-subattribute", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if no action is specified" do
cmd = api
body = "{ \"collections\" : { } }"
doc = ArangoDB.log_post("#{prefix}-missing-action", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if action attribute has a wrong type" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : false }"
doc = ArangoDB.log_post("#{prefix}-invalid-action", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if action attribute contains broken code" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \" for \" }"
doc = ArangoDB.log_post("#{prefix}-invalid-action", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(10)
end
it "returns an error if transactions are nested" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"return TRANSACTION({ collections: { }, action: function() { return 1; } });\" }"
doc = ArangoDB.log_post("#{prefix}-nested1", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1651)
end
it "returns an error if transactions are nested" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"return TRANSACTION({ collections: { }, action: \\\"return 1;\\\" });\" }"
doc = ArangoDB.log_post("#{prefix}-nested2", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1651)
end
end
################################################################################
## using "wrong" collections
################################################################################
context "using wrong collections:" do
before do
@cn1 = "UnitTestsTransactions1"
@cn2 = "UnitTestsTransactions2"
ArangoDB.create_collection(@cn1, false)
ArangoDB.create_collection(@cn2, false)
end
after do
ArangoDB.drop_collection(@cn1)
ArangoDB.drop_collection(@cn2)
end
it "returns an error if referring to a non-existing collection" do
cmd = api
body = "{ \"collections\" : { \"write\": \"_meow\" }, \"action\" : \"return 1;\" }"
doc = ArangoDB.log_post("#{prefix}-non-existing-collection", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1203)
end
it "returns an error when using a non-declared collection" do
cmd = api
body = "{ \"collections\" : { \"write\": \"#{@cn1}\" }, \"action\" : \"require(\\\"internal\\\").db.#{@cn2}.save({ });\" }"
doc = ArangoDB.log_post("#{prefix}-non-declared-collection", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1652)
end
it "returns an error when using a collection in invalid mode" do
cmd = api
body = "{ \"collections\" : { \"read\": \"#{@cn1}\" }, \"action\" : \"require(\\\"internal\\\").db.#{@cn1}.save({ });\" }"
doc = ArangoDB.log_post("#{prefix}-invalid-mode1", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1652)
end
it "returns an error when using a collection in invalid mode" do
cmd = api
body = "{ \"collections\" : { \"read\": [ \"#{@cn1}\", \"#{@cn2}\" ] }, \"action\" : \"require(\\\"internal\\\").db.#{@cn1}.save({ });\" }"
doc = ArangoDB.log_post("#{prefix}-invalid-mode2", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1652)
end
it "returns an error when using a disallowed operation" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"require(\\\"internal\\\").db._create(\\\"abc\\\");\" }"
doc = ArangoDB.log_post("#{prefix}-invalid-mode2", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1653)
end
end
################################################################################
## non-collection transactions
################################################################################
context "non-collection transactions:" do
it "returning a simple type" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"return 42;\" }"
doc = ArangoDB.log_post("#{prefix}-empty-transaction1", 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['result'].should eq(42)
end
it "returning a compound type" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"return [ true, { a: 42, b: [ null, true ], c: \\\"foo\\\" } ];\" }"
doc = ArangoDB.log_post("#{prefix}-empty-transaction2", 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['result'].should eq([ true, { "a" => 42, "b" => [ nil, true ], "c" => "foo" } ])
end
it "returning an exception" do
cmd = api
body = "{ \"collections\" : { }, \"action\" : \"throw \\\"doh!\\\";\" }"
doc = ArangoDB.log_post("#{prefix}-empty-exception", cmd, :body => body)
doc.code.should eq(500)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(500)
doc.parsed_response['errorNum'].should eq(500)
end
end
################################################################################
## single-collection transactions
################################################################################
context "single collection transactions:" do
before do
@cn = "UnitTestsTransactions"
@cid = ArangoDB.create_collection(@cn, false)
end
after do
ArangoDB.drop_collection(@cn)
end
it "read-only, using write" do
body = "{ }"
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
ArangoDB.size_collection(@cn).should eq(3);
cmd = api
body = "{ \"collections\" : { \"write\": \"#{@cn}\" }, \"action\" : \"var c = require(\\\"internal\\\").db.UnitTestsTransactions; return c.count();\" }"
doc = ArangoDB.log_post("#{prefix}-read-write", 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['result'].should eq(3)
ArangoDB.size_collection(@cn).should eq(3);
end
it "read-only, using read" do
body = "{ }"
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn}", :body => body)
ArangoDB.size_collection(@cn).should eq(3);
cmd = api
body = "{ \"collections\" : { \"read\": \"#{@cn}\" }, \"action\" : \"var c = require(\\\"internal\\\").db.UnitTestsTransactions; return c.count();\" }"
doc = ArangoDB.log_post("#{prefix}-read-only", 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['result'].should eq(3)
ArangoDB.size_collection(@cn).should eq(3);
end
it "committing" do
cmd = api
body = "{ \"collections\" : { \"write\": \"#{@cn}\" }, \"action\" : \"var c = require(\\\"internal\\\").db.UnitTestsTransactions; c.save({ }); c.save({ }); return c.count();\" }"
doc = ArangoDB.log_post("#{prefix}-single-commit", 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['result'].should eq(2)
ArangoDB.size_collection(@cn).should eq(2);
end
it "aborting" do
cmd = api
body = "{ \"collections\" : { \"write\": \"#{@cn}\" }, \"action\" : \"var c = require(\\\"internal\\\").db.UnitTestsTransactions; c.save({ }); c.save({ }); throw \\\"doh!\\\";\" }"
doc = ArangoDB.log_post("#{prefix}-single-abort", cmd, :body => body)
doc.code.should eq(500)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(500)
doc.parsed_response['errorNum'].should eq(500)
ArangoDB.size_collection(@cn).should eq(0);
end
end
################################################################################
## multi-collection transactions
################################################################################
context "multi collection transactions:" do
before do
@cn1 = "UnitTestsTransactions1"
@cn2 = "UnitTestsTransactions2"
ArangoDB.create_collection(@cn1, false)
ArangoDB.create_collection(@cn2, false)
end
after do
ArangoDB.drop_collection(@cn1)
ArangoDB.drop_collection(@cn2)
end
it "read-only, using write" do
body = "{ }"
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn2}", :body => body)
ArangoDB.size_collection(@cn1).should eq(3);
ArangoDB.size_collection(@cn2).should eq(1);
cmd = api
body = "{ \"collections\" : { \"write\": [ \"#{@cn1}\", \"#{@cn2}\" ] }, \"action\" : \"var c1 = require(\\\"internal\\\").db.#{@cn1}; var c2 = require(\\\"internal\\\").db.#{@cn2}; return [ c1.count(), c2.count() ];\" }"
doc = ArangoDB.log_post("#{prefix}-multi-read-write", 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['result'].should eq([ 3, 1 ])
ArangoDB.size_collection(@cn1).should eq(3);
ArangoDB.size_collection(@cn2).should eq(1);
end
it "read-only, using read" do
body = "{ }"
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn1}", :body => body)
doc = ArangoDB.post("/_api/document?collection=#{@cn2}", :body => body)
ArangoDB.size_collection(@cn1).should eq(3);
ArangoDB.size_collection(@cn2).should eq(1);
cmd = api
body = "{ \"collections\" : { \"read\": [ \"#{@cn1}\", \"#{@cn2}\" ] }, \"action\" : \"var c1 = require(\\\"internal\\\").db.#{@cn1}; var c2 = require(\\\"internal\\\").db.#{@cn2}; return [ c1.count(), c2.count() ];\" }"
doc = ArangoDB.log_post("#{prefix}-multi-read-only", 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['result'].should eq([ 3, 1 ])
ArangoDB.size_collection(@cn1).should eq(3);
ArangoDB.size_collection(@cn2).should eq(1);
end
it "committing" do
cmd = api
body = "{ \"collections\" : { \"write\": [ \"#{@cn1}\", \"#{@cn2}\" ] }, \"action\" : \"var c1 = require(\\\"internal\\\").db.#{@cn1}; var c2 = require(\\\"internal\\\").db.#{@cn2}; c1.save({ }); c1.save({ }); c2.save({ }); return [ c1.count(), c2.count() ];\" }"
doc = ArangoDB.log_post("#{prefix}-multi-commit", 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['result'].should eq([ 2, 1 ])
ArangoDB.size_collection(@cn1).should eq(2);
ArangoDB.size_collection(@cn2).should eq(1);
end
it "aborting" do
cmd = api
body = "{ \"collections\" : { \"write\": [ \"#{@cn1}\", \"#{@cn2}\" ] }, \"action\" : \"var c1 = require(\\\"internal\\\").db.#{@cn1}; var c2 = require(\\\"internal\\\").db.#{@cn2}; c1.save({ }); c1.save({ }); c2.save({ }); throw \\\"doh!\\\";\" }"
doc = ArangoDB.log_post("#{prefix}-multi-abort", cmd, :body => body)
doc.code.should eq(500)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(500)
doc.parsed_response['errorNum'].should eq(500)
ArangoDB.size_collection(@cn1).should eq(0);
ArangoDB.size_collection(@cn2).should eq(0);
end
end
end
end

View File

@ -23,4 +23,5 @@ rspec --color --format d \
api-cursor-spec.rb \
api-statistics-spec.rb \
api-simple-spec.rb \
api-transactions-spec.rb \
api-users-spec.rb

View File

@ -210,6 +210,7 @@ endif
################################################################################
SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \
@top_srcdir@/js/common/tests/shell-transactions.js \
@top_srcdir@/js/common/tests/shell-aqlfunctions.js \
@top_srcdir@/js/common/tests/shell-attributes.js \
@top_srcdir@/js/common/tests/shell-collection.js \

View File

@ -175,7 +175,7 @@ namespace triagens {
int begin () {
if (_trx == 0) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
return TRI_ERROR_TRANSACTION_INTERNAL;
}
if (_setupState != TRI_ERROR_NO_ERROR) {
@ -194,7 +194,7 @@ namespace triagens {
int commit () {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
// transaction not created or not running
return TRI_ERROR_TRANSACTION_INVALID_STATE;
return TRI_ERROR_TRANSACTION_INTERNAL;
}
int res = TRI_CommitTransaction(_trx, _nestingLevel);
@ -209,7 +209,7 @@ namespace triagens {
int abort () {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
// transaction not created or not running
return TRI_ERROR_TRANSACTION_INVALID_STATE;
return TRI_ERROR_TRANSACTION_INTERNAL;
}
int res = TRI_AbortTransaction(_trx, _nestingLevel);
@ -300,7 +300,7 @@ namespace triagens {
if (status == TRI_TRANSACTION_COMMITTED ||
status == TRI_TRANSACTION_ABORTED) {
// transaction already finished?
return registerError(TRI_ERROR_TRANSACTION_INVALID_STATE);
return registerError(TRI_ERROR_TRANSACTION_INTERNAL);
}
int res;
@ -364,7 +364,7 @@ namespace triagens {
const TRI_transaction_type_e type) {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
return TRI_ERROR_TRANSACTION_INTERNAL;
}
int res = TRI_LockCollectionTransaction(trxCollection, type, _nestingLevel);
@ -380,7 +380,7 @@ namespace triagens {
const TRI_transaction_type_e type) {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
return TRI_ERROR_TRANSACTION_INTERNAL;
}
int res = TRI_UnlockCollectionTransaction(trxCollection, type, _nestingLevel);
@ -878,7 +878,7 @@ namespace triagens {
if (getStatus() != TRI_TRANSACTION_CREATED) {
// transaction already started?
res = TRI_ERROR_TRANSACTION_INVALID_STATE;
res = TRI_ERROR_TRANSACTION_INTERNAL;
}
else {
res = TRI_AddCollectionTransaction(_trx, cid, type, _nestingLevel);

View File

@ -116,11 +116,10 @@
"ERROR_QUERY_FUNCTION_INVALID_CODE" : { "code" : 1581, "message" : "invalid user function code" },
"ERROR_QUERY_FUNCTION_NOT_FOUND" : { "code" : 1582, "message" : "user function not found" },
"ERROR_CURSOR_NOT_FOUND" : { "code" : 1600, "message" : "cursor not found" },
"ERROR_TRANSACTION_INVALID_STATE" : { "code" : 1650, "message" : "invalid transaction state" },
"ERROR_TRANSACTION_NESTED" : { "code" : 1652, "message" : "nested transactions detected" },
"ERROR_TRANSACTION_INTERNAL" : { "code" : 1653, "message" : "internal transaction error" },
"ERROR_TRANSACTION_UNREGISTERED_COLLECTION" : { "code" : 1654, "message" : "unregistered collection used in transaction" },
"ERROR_TRANSACTION_DISALLOWED_OPERATION" : { "code" : 1655, "message" : "disallowed operation inside transaction" },
"ERROR_TRANSACTION_INTERNAL" : { "code" : 1650, "message" : "internal transaction error" },
"ERROR_TRANSACTION_NESTED" : { "code" : 1651, "message" : "nested transactions detected" },
"ERROR_TRANSACTION_UNREGISTERED_COLLECTION" : { "code" : 1652, "message" : "unregistered collection used in transaction" },
"ERROR_TRANSACTION_DISALLOWED_OPERATION" : { "code" : 1653, "message" : "disallowed operation inside transaction" },
"ERROR_USER_INVALID_NAME" : { "code" : 1700, "message" : "invalid user name" },
"ERROR_USER_INVALID_PASSWORD" : { "code" : 1701, "message" : "invalid password" },
"ERROR_USER_DUPLICATE" : { "code" : 1702, "message" : "duplicate user" },

View File

@ -57,8 +57,6 @@ function ArangoDatabase (connection) {
this._collectionConstructor = ArangoCollection;
this._type = ArangoCollection.TYPE_DOCUMENT;
// private function to store a collection in both "db" and "edges" at the
// same time
this._registerCollection = function (name, obj) {
// store the collection in our own list
this[name] = obj;
@ -691,6 +689,68 @@ ArangoDatabase.prototype._query = function (data) {
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- transactions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoShell
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction
////////////////////////////////////////////////////////////////////////////////
ArangoDatabase.prototype._executeTransaction = function (data) {
if (! data || typeof(data) !== 'object') {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "usage: TRANSACTION(<object>)"
});
}
if (! data.collections || typeof(data.collections) !== 'object') {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "missing/invalid collections definition for transaction"
});
}
if (! data.action ||
(typeof(data.action) !== 'string' && typeof(data.action) !== 'function')) {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "missing/invalid action definition for transaction"
});
}
if (typeof(data.action) === 'function') {
data.action = 'return ' + String(data.action) + '();';
}
var requestResult = this._connection.POST("/_api/transaction",
JSON.stringify(data));
if (requestResult !== null && requestResult.error === true) {
throw new ArangoError(requestResult);
}
arangosh.checkRequestResult(requestResult);
return requestResult;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -56,8 +56,6 @@ function ArangoDatabase (connection) {
this._collectionConstructor = ArangoCollection;
this._type = ArangoCollection.TYPE_DOCUMENT;
// private function to store a collection in both "db" and "edges" at the
// same time
this._registerCollection = function (name, obj) {
// store the collection in our own list
this[name] = obj;
@ -690,6 +688,68 @@ ArangoDatabase.prototype._query = function (data) {
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- transactions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoShell
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction
////////////////////////////////////////////////////////////////////////////////
ArangoDatabase.prototype._executeTransaction = function (data) {
if (! data || typeof(data) !== 'object') {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "usage: TRANSACTION(<object>)"
});
}
if (! data.collections || typeof(data.collections) !== 'object') {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "missing/invalid collections definition for transaction"
});
}
if (! data.action ||
(typeof(data.action) !== 'string' && typeof(data.action) !== 'function')) {
throw new ArangoError({
error: true,
code: internal.errors.ERROR_HTTP_BAD_PARAMETER.code,
errorNum: internal.errors.ERROR_BAD_PARAMETER.code,
errorMessage: "missing/invalid action definition for transaction"
});
}
if (typeof(data.action) === 'function') {
data.action = 'return ' + String(data.action) + '();';
}
var requestResult = this._connection.POST("/_api/transaction",
JSON.stringify(data));
if (requestResult !== null && requestResult.error === true) {
throw new ArangoError(requestResult);
}
arangosh.checkRequestResult(requestResult);
return requestResult;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -116,11 +116,10 @@
"ERROR_QUERY_FUNCTION_INVALID_CODE" : { "code" : 1581, "message" : "invalid user function code" },
"ERROR_QUERY_FUNCTION_NOT_FOUND" : { "code" : 1582, "message" : "user function not found" },
"ERROR_CURSOR_NOT_FOUND" : { "code" : 1600, "message" : "cursor not found" },
"ERROR_TRANSACTION_INVALID_STATE" : { "code" : 1650, "message" : "invalid transaction state" },
"ERROR_TRANSACTION_NESTED" : { "code" : 1652, "message" : "nested transactions detected" },
"ERROR_TRANSACTION_INTERNAL" : { "code" : 1653, "message" : "internal transaction error" },
"ERROR_TRANSACTION_UNREGISTERED_COLLECTION" : { "code" : 1654, "message" : "unregistered collection used in transaction" },
"ERROR_TRANSACTION_DISALLOWED_OPERATION" : { "code" : 1655, "message" : "disallowed operation inside transaction" },
"ERROR_TRANSACTION_INTERNAL" : { "code" : 1650, "message" : "internal transaction error" },
"ERROR_TRANSACTION_NESTED" : { "code" : 1651, "message" : "nested transactions detected" },
"ERROR_TRANSACTION_UNREGISTERED_COLLECTION" : { "code" : 1652, "message" : "unregistered collection used in transaction" },
"ERROR_TRANSACTION_DISALLOWED_OPERATION" : { "code" : 1653, "message" : "disallowed operation inside transaction" },
"ERROR_USER_INVALID_NAME" : { "code" : 1700, "message" : "invalid user name" },
"ERROR_USER_INVALID_PASSWORD" : { "code" : 1701, "message" : "invalid password" },
"ERROR_USER_DUPLICATE" : { "code" : 1702, "message" : "duplicate user" },

View File

@ -0,0 +1,211 @@
/*jslint indent: 2, nomen: true, maxlen: 80 */
/*global require, assertEqual, assertTrue */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for client/server side transaction invocation
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var ERRORS = arangodb.errors;
var db = arangodb.db;
// -----------------------------------------------------------------------------
// --SECTION-- transactions tests
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function TransactionsInvocationsSuite () {
var unregister = function (name) {
try {
aqlfunctions.unregister(name);
}
catch (err) {
}
};
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with a string action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionString : function () {
var result = db._executeTransaction({
collections: { },
action: "return 23;"
});
assertEqual(23, result);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with a string function action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionStringFunction : function () {
var result = db._executeTransaction({
collections: { },
action: "return function () { return 11; }()"
});
assertEqual(11, result);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with a function action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionFunction : function () {
var result = db._executeTransaction({
collections: { },
action: function () { return 42; }
});
assertEqual(42, result);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with an invalid action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionInvalid1 : function () {
try {
db._executeTransaction({
collections: { },
action: null,
});
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with an invalid action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionInvalid2 : function () {
try {
db._executeTransaction({
collections: { },
action: [ ],
});
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction without an action
////////////////////////////////////////////////////////////////////////////////
testInvokeNoAction : function () {
try {
db._executeTransaction({
collections: { }
});
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with a non-working action declaration
////////////////////////////////////////////////////////////////////////////////
testInvokeActionBroken : function () {
try {
db._executeTransaction({
collections: { },
action: "function () { return 11; }"
});
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction with an invalid action
////////////////////////////////////////////////////////////////////////////////
testInvokeActionInvalid1 : function () {
try {
db._executeTransaction({
collections: { },
action: null,
});
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_BAD_PARAMETER.code, err.errorNum);
}
},
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(TransactionsInvocationsSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -1,5 +1,5 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
/*global require, exports */
/*global require, exports, TRANSACTION */
////////////////////////////////////////////////////////////////////////////////
/// @brief ArangoDatabase
@ -119,6 +119,27 @@ ArangoDatabase.prototype._query = function (data) {
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- transactions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoShell
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a transaction
////////////////////////////////////////////////////////////////////////////////
ArangoDatabase.prototype._executeTransaction = function (data) {
return TRANSACTION(data);
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- collection functions
// -----------------------------------------------------------------------------

View File

@ -148,6 +148,62 @@ function transactionInvocationSuite () {
tests.forEach(function (test) {
assertEqual(test.expected, TRANSACTION(test.trx));
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: action
////////////////////////////////////////////////////////////////////////////////
testActionFunction : function () {
var obj = {
collections : {
},
action : function () {
return 42;
}
};
assertEqual(42, TRANSACTION(obj));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: action
////////////////////////////////////////////////////////////////////////////////
testActionString : function () {
var obj = {
collections : {
},
action : "return 42;"
};
assertEqual(42, TRANSACTION(obj));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: nesting
////////////////////////////////////////////////////////////////////////////////
testNesting : function () {
var obj = {
collections : {
},
action : function () {
TRANSACTION({
collections: {
},
action: "return 1;"
});
}
};
try {
TRANSACTION(obj);
fail();
}
catch (err) {
assertEqual(arangodb.errors.ERROR_TRANSACTION_NESTED.code, err.errorNum);
}
}
};

View File

@ -161,11 +161,10 @@ ERROR_CURSOR_NOT_FOUND,1600,"cursor not found","Will be raised when a cursor is
## ArangoDB transaction errors
################################################################################
ERROR_TRANSACTION_INVALID_STATE,1650,"invalid transaction state","Will be raised when an operation is requested on a transaction that has an incompatible state."
ERROR_TRANSACTION_NESTED,1652,"nested transactions detected","Will be raised when transactions are nested."
ERROR_TRANSACTION_INTERNAL,1653,"internal transaction error","Will be raised when a wrong usage of transactions is detected. this is an internal error and indicates a bug in ArangoDB."
ERROR_TRANSACTION_UNREGISTERED_COLLECTION,1654,"unregistered collection used in transaction","Will be raised when a collection is used in the middle of a transaction but was not registered at transaction start."
ERROR_TRANSACTION_DISALLOWED_OPERATION,1655,"disallowed operation inside transaction","Will be raised when a disallowed operation is carried out in a transaction."
ERROR_TRANSACTION_INTERNAL,1650,"internal transaction error","Will be raised when a wrong usage of transactions is detected. this is an internal error and indicates a bug in ArangoDB."
ERROR_TRANSACTION_NESTED,1651,"nested transactions detected","Will be raised when transactions are nested."
ERROR_TRANSACTION_UNREGISTERED_COLLECTION,1652,"unregistered collection used in transaction","Will be raised when a collection is used in the middle of a transaction but was not registered at transaction start."
ERROR_TRANSACTION_DISALLOWED_OPERATION,1653,"disallowed operation inside transaction","Will be raised when a disallowed operation is carried out in a transaction."
################################################################################
## User management

View File

@ -112,9 +112,8 @@ void TRI_InitialiseErrorMessages (void) {
REG_ERROR(ERROR_QUERY_FUNCTION_INVALID_CODE, "invalid user function code");
REG_ERROR(ERROR_QUERY_FUNCTION_NOT_FOUND, "user function not found");
REG_ERROR(ERROR_CURSOR_NOT_FOUND, "cursor not found");
REG_ERROR(ERROR_TRANSACTION_INVALID_STATE, "invalid transaction state");
REG_ERROR(ERROR_TRANSACTION_NESTED, "nested transactions detected");
REG_ERROR(ERROR_TRANSACTION_INTERNAL, "internal transaction error");
REG_ERROR(ERROR_TRANSACTION_NESTED, "nested transactions detected");
REG_ERROR(ERROR_TRANSACTION_UNREGISTERED_COLLECTION, "unregistered collection used in transaction");
REG_ERROR(ERROR_TRANSACTION_DISALLOWED_OPERATION, "disallowed operation inside transaction");
REG_ERROR(ERROR_USER_INVALID_NAME, "invalid user name");

View File

@ -243,18 +243,15 @@ extern "C" {
/// - 1600: @LIT{cursor not found}
/// Will be raised when a cursor is requested via its id but a cursor with
/// that id cannot be found.
/// - 1650: @LIT{invalid transaction state}
/// Will be raised when an operation is requested on a transaction that has
/// an incompatible state.
/// - 1652: @LIT{nested transactions detected}
/// Will be raised when transactions are nested.
/// - 1653: @LIT{internal transaction error}
/// - 1650: @LIT{internal transaction error}
/// Will be raised when a wrong usage of transactions is detected. this is an
/// internal error and indicates a bug in ArangoDB.
/// - 1654: @LIT{unregistered collection used in transaction}
/// - 1651: @LIT{nested transactions detected}
/// Will be raised when transactions are nested.
/// - 1652: @LIT{unregistered collection used in transaction}
/// Will be raised when a collection is used in the middle of a transaction
/// but was not registered at transaction start.
/// - 1655: @LIT{disallowed operation inside transaction}
/// - 1653: @LIT{disallowed operation inside transaction}
/// Will be raised when a disallowed operation is carried out in a
/// transaction.
/// - 1700: @LIT{invalid user name}
@ -1448,28 +1445,7 @@ void TRI_InitialiseErrorMessages (void);
#define TRI_ERROR_CURSOR_NOT_FOUND (1600)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1650: ERROR_TRANSACTION_INVALID_STATE
///
/// invalid transaction state
///
/// Will be raised when an operation is requested on a transaction that has an
/// incompatible state.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_INVALID_STATE (1650)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1652: ERROR_TRANSACTION_NESTED
///
/// nested transactions detected
///
/// Will be raised when transactions are nested.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_NESTED (1652)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1653: ERROR_TRANSACTION_INTERNAL
/// @brief 1650: ERROR_TRANSACTION_INTERNAL
///
/// internal transaction error
///
@ -1477,10 +1453,20 @@ void TRI_InitialiseErrorMessages (void);
/// internal error and indicates a bug in ArangoDB.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_INTERNAL (1653)
#define TRI_ERROR_TRANSACTION_INTERNAL (1650)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1654: ERROR_TRANSACTION_UNREGISTERED_COLLECTION
/// @brief 1651: ERROR_TRANSACTION_NESTED
///
/// nested transactions detected
///
/// Will be raised when transactions are nested.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_NESTED (1651)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1652: ERROR_TRANSACTION_UNREGISTERED_COLLECTION
///
/// unregistered collection used in transaction
///
@ -1488,17 +1474,17 @@ void TRI_InitialiseErrorMessages (void);
/// was not registered at transaction start.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION (1654)
#define TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION (1652)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1655: ERROR_TRANSACTION_DISALLOWED_OPERATION
/// @brief 1653: ERROR_TRANSACTION_DISALLOWED_OPERATION
///
/// disallowed operation inside transaction
///
/// Will be raised when a disallowed operation is carried out in a transaction.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_TRANSACTION_DISALLOWED_OPERATION (1655)
#define TRI_ERROR_TRANSACTION_DISALLOWED_OPERATION (1653)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1700: ERROR_USER_INVALID_NAME