mirror of https://gitee.com/bigwinds/arangodb
added client side API for transactions
This commit is contained in:
parent
780c2a6caa
commit
dee5adb9f5
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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:
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue