mirror of https://gitee.com/bigwinds/arangodb
make graphs transaction-aware (#9855)
* make graphs transaction-aware * simranification * fix tests with mmfiles
This commit is contained in:
parent
10d99b20cb
commit
f8c156a44f
|
@ -1,6 +1,10 @@
|
||||||
v3.5.1 (XXXX-XX-XX)
|
v3.5.1 (XXXX-XX-XX)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
* Make graph operations in general-graph transaction-aware.
|
||||||
|
|
||||||
|
* Fixed adding an orphan collections as the first collection in a SmartGraph.
|
||||||
|
|
||||||
* Fixed non-deterministic occurrences of "document not found" errors in sharded
|
* Fixed non-deterministic occurrences of "document not found" errors in sharded
|
||||||
collections with custom shard keys (i.e. non-`_key`) and multi-document lookups.
|
collections with custom shard keys (i.e. non-`_key`) and multi-document lookups.
|
||||||
|
|
||||||
|
@ -10,8 +14,6 @@ v3.5.1 (XXXX-XX-XX)
|
||||||
internal iterator after being rearmed with new lookup values (which only happens
|
internal iterator after being rearmed with new lookup values (which only happens
|
||||||
if the IN iterator is called from an inner FOR loop).
|
if the IN iterator is called from an inner FOR loop).
|
||||||
|
|
||||||
* Fixed adding an orphan collection as the first collection in a SmartGraph.
|
|
||||||
|
|
||||||
* Geo functions will now have better error reporting on invalid input.
|
* Geo functions will now have better error reporting on invalid input.
|
||||||
|
|
||||||
* The graph viewer of the web interface now tries to find a vertex document of
|
* The graph viewer of the web interface now tries to find a vertex document of
|
||||||
|
|
|
@ -235,8 +235,8 @@ void RestAqlHandler::setupClusterQuery() {
|
||||||
}
|
}
|
||||||
collectionBuilder.close();
|
collectionBuilder.close();
|
||||||
|
|
||||||
// creates a StandaloneContext or a leasing context
|
// creates a StandaloneContext or a leased context
|
||||||
auto ctx = createAQLTransactionContext();
|
auto ctx = createTransactionContext();
|
||||||
|
|
||||||
VPackBuilder answerBuilder;
|
VPackBuilder answerBuilder;
|
||||||
answerBuilder.openObject();
|
answerBuilder.openObject();
|
||||||
|
|
|
@ -51,9 +51,12 @@
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
using namespace arangodb::graph;
|
using namespace arangodb::graph;
|
||||||
|
|
||||||
std::shared_ptr<transaction::Context> GraphOperations::ctx() const {
|
std::shared_ptr<transaction::Context> GraphOperations::ctx() {
|
||||||
return transaction::StandaloneContext::Create(_vocbase);
|
if (!_ctx) {
|
||||||
};
|
_ctx = std::make_shared<transaction::StandaloneSmartContext>(_vocbase);
|
||||||
|
}
|
||||||
|
return _ctx;
|
||||||
|
}
|
||||||
|
|
||||||
void GraphOperations::checkForUsedEdgeCollections(const Graph& graph,
|
void GraphOperations::checkForUsedEdgeCollections(const Graph& graph,
|
||||||
const std::string& collectionName,
|
const std::string& collectionName,
|
||||||
|
@ -456,7 +459,7 @@ OperationResult GraphOperations::addEdgeDefinition(VPackSlice edgeDefinitionSlic
|
||||||
|
|
||||||
// finally save the graph
|
// finally save the graph
|
||||||
return gmngr.storeGraph(_graph, waitForSync, true);
|
return gmngr.storeGraph(_graph, waitForSync, true);
|
||||||
};
|
}
|
||||||
|
|
||||||
// vertices
|
// vertices
|
||||||
|
|
||||||
|
@ -466,7 +469,7 @@ OperationResult GraphOperations::getVertex(std::string const& collectionName,
|
||||||
std::string const& key,
|
std::string const& key,
|
||||||
boost::optional<TRI_voc_rid_t> rev) {
|
boost::optional<TRI_voc_rid_t> rev) {
|
||||||
return getDocument(collectionName, key, std::move(rev));
|
return getDocument(collectionName, key, std::move(rev));
|
||||||
};
|
}
|
||||||
|
|
||||||
// TODO check if definitionName is an edge collection in _graph?
|
// TODO check if definitionName is an edge collection in _graph?
|
||||||
OperationResult GraphOperations::getEdge(const std::string& definitionName,
|
OperationResult GraphOperations::getEdge(const std::string& definitionName,
|
||||||
|
@ -867,10 +870,9 @@ OperationResult GraphOperations::removeEdgeOrVertex(const std::string& collectio
|
||||||
edgeCollections.emplace(it); // but also to edgeCollections for later iteration
|
edgeCollections.emplace(it); // but also to edgeCollections for later iteration
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneSmartContext>(_vocbase);
|
|
||||||
transaction::Options trxOptions;
|
transaction::Options trxOptions;
|
||||||
trxOptions.waitForSync = waitForSync;
|
trxOptions.waitForSync = waitForSync;
|
||||||
transaction::Methods trx{ctx, {}, trxCollections, {}, trxOptions};
|
transaction::Methods trx{ctx(), {}, trxCollections, {}, trxOptions};
|
||||||
|
|
||||||
res = trx.begin();
|
res = trx.begin();
|
||||||
|
|
||||||
|
@ -898,7 +900,7 @@ OperationResult GraphOperations::removeEdgeOrVertex(const std::string& collectio
|
||||||
|
|
||||||
arangodb::aql::Query query(false, _vocbase, queryString, bindVars,
|
arangodb::aql::Query query(false, _vocbase, queryString, bindVars,
|
||||||
nullptr, arangodb::aql::PART_DEPENDENT);
|
nullptr, arangodb::aql::PART_DEPENDENT);
|
||||||
query.setTransactionContext(ctx); // hack to share the same transaction
|
query.setTransactionContext(ctx()); // hack to share the same transaction
|
||||||
|
|
||||||
auto queryResult = query.executeSync(QueryRegistryFeature::registry());
|
auto queryResult = query.executeSync(QueryRegistryFeature::registry());
|
||||||
|
|
||||||
|
|
|
@ -48,14 +48,16 @@ class GraphOperations {
|
||||||
private:
|
private:
|
||||||
Graph& _graph;
|
Graph& _graph;
|
||||||
TRI_vocbase_t& _vocbase;
|
TRI_vocbase_t& _vocbase;
|
||||||
|
std::shared_ptr<transaction::Context> _ctx;
|
||||||
|
|
||||||
Graph const& graph() const { return _graph; };
|
Graph const& graph() const { return _graph; };
|
||||||
std::shared_ptr<transaction::Context> ctx() const;
|
std::shared_ptr<transaction::Context> ctx();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GraphOperations() = delete;
|
GraphOperations() = delete;
|
||||||
GraphOperations(Graph& graph_, TRI_vocbase_t& vocbase)
|
GraphOperations(Graph& graph_, TRI_vocbase_t& vocbase,
|
||||||
: _graph(graph_), _vocbase(vocbase) {}
|
std::shared_ptr<transaction::Context> const& ctx = nullptr)
|
||||||
|
: _graph(graph_), _vocbase(vocbase), _ctx(ctx) {}
|
||||||
|
|
||||||
// TODO I added the complex result type for the get* methods to exactly
|
// TODO I added the complex result type for the get* methods to exactly
|
||||||
// reproduce (in the RestGraphHandler) the behavior of the similar methods
|
// reproduce (in the RestGraphHandler) the behavior of the similar methods
|
||||||
|
|
|
@ -215,7 +215,7 @@ RestStatus RestCursorHandler::registerQueryOrCursor(VPackSlice const& slice) {
|
||||||
Cursor* cursor = cursors->createQueryStream(querySlice.copyString(), bindVarsBuilder,
|
Cursor* cursor = cursors->createQueryStream(querySlice.copyString(), bindVarsBuilder,
|
||||||
_options, batchSize, ttl,
|
_options, batchSize, ttl,
|
||||||
/*contextOwnedByExt*/ false,
|
/*contextOwnedByExt*/ false,
|
||||||
createAQLTransactionContext());
|
createTransactionContext());
|
||||||
|
|
||||||
return generateCursorResult(rest::ResponseCode::CREATED, cursor);
|
return generateCursorResult(rest::ResponseCode::CREATED, cursor);
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ RestStatus RestCursorHandler::registerQueryOrCursor(VPackSlice const& slice) {
|
||||||
auto query = std::make_unique<aql::Query>(
|
auto query = std::make_unique<aql::Query>(
|
||||||
false, _vocbase, arangodb::aql::QueryString(queryStr, static_cast<size_t>(l)),
|
false, _vocbase, arangodb::aql::QueryString(queryStr, static_cast<size_t>(l)),
|
||||||
bindVarsBuilder, _options, arangodb::aql::PART_MAIN);
|
bindVarsBuilder, _options, arangodb::aql::PART_MAIN);
|
||||||
query->setTransactionContext(createAQLTransactionContext());
|
query->setTransactionContext(createTransactionContext());
|
||||||
|
|
||||||
std::shared_ptr<aql::SharedQueryState> ss = query->sharedState();
|
std::shared_ptr<aql::SharedQueryState> ss = query->sharedState();
|
||||||
ss->setContinueHandler([self = shared_from_this(), ss] { self->continueHandlerExecution(); });
|
ss->setContinueHandler([self = shared_from_this(), ss] { self->continueHandlerExecution(); });
|
||||||
|
|
|
@ -251,7 +251,6 @@ Result RestGraphHandler::edgeAction(Graph& graph, const std::string& edgeDefinit
|
||||||
return edgeActionRemove(graph, edgeDefinitionName, edgeKey);
|
return edgeActionRemove(graph, edgeDefinitionName, edgeKey);
|
||||||
case RequestType::PATCH:
|
case RequestType::PATCH:
|
||||||
return edgeActionUpdate(graph, edgeDefinitionName, edgeKey);
|
return edgeActionUpdate(graph, edgeDefinitionName, edgeKey);
|
||||||
break;
|
|
||||||
case RequestType::PUT:
|
case RequestType::PUT:
|
||||||
return edgeActionReplace(graph, edgeDefinitionName, edgeKey);
|
return edgeActionReplace(graph, edgeDefinitionName, edgeKey);
|
||||||
default:;
|
default:;
|
||||||
|
@ -270,7 +269,8 @@ void RestGraphHandler::vertexActionRead(Graph& graph, std::string const& collect
|
||||||
|
|
||||||
auto maybeRev = handleRevision();
|
auto maybeRev = handleRevision();
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
OperationResult result = gops.getVertex(collectionName, key, maybeRev);
|
OperationResult result = gops.getVertex(collectionName, key, maybeRev);
|
||||||
|
|
||||||
if (!result.ok()) {
|
if (!result.ok()) {
|
||||||
|
@ -292,7 +292,6 @@ void RestGraphHandler::vertexActionRead(Graph& graph, std::string const& collect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
// use default options
|
// use default options
|
||||||
generateVertexRead(result.slice(), *ctx->getVPackOptionsForDump());
|
generateVertexRead(result.slice(), *ctx->getVPackOptionsForDump());
|
||||||
}
|
}
|
||||||
|
@ -533,7 +532,8 @@ void RestGraphHandler::edgeActionRead(Graph& graph, const std::string& definitio
|
||||||
|
|
||||||
auto maybeRev = handleRevision();
|
auto maybeRev = handleRevision();
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
OperationResult result = gops.getEdge(definitionName, key, maybeRev);
|
OperationResult result = gops.getEdge(definitionName, key, maybeRev);
|
||||||
|
|
||||||
if (result.fail()) {
|
if (result.fail()) {
|
||||||
|
@ -549,7 +549,6 @@ void RestGraphHandler::edgeActionRead(Graph& graph, const std::string& definitio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
generateEdgeRead(result.slice(), *ctx->getVPackOptionsForDump());
|
generateEdgeRead(result.slice(), *ctx->getVPackOptionsForDump());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +574,8 @@ Result RestGraphHandler::edgeActionRemove(Graph& graph, const std::string& defin
|
||||||
|
|
||||||
auto maybeRev = handleRevision();
|
auto maybeRev = handleRevision();
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
|
|
||||||
OperationResult result =
|
OperationResult result =
|
||||||
gops.removeEdge(definitionName, key, maybeRev, waitForSync, returnOld);
|
gops.removeEdge(definitionName, key, maybeRev, waitForSync, returnOld);
|
||||||
|
@ -585,7 +585,6 @@ Result RestGraphHandler::edgeActionRemove(Graph& graph, const std::string& defin
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
generateRemoved(true, result._options.waitForSync, result.slice().get("old"),
|
generateRemoved(true, result._options.waitForSync, result.slice().get("old"),
|
||||||
*ctx->getVPackOptionsForDump());
|
*ctx->getVPackOptionsForDump());
|
||||||
|
|
||||||
|
@ -673,7 +672,8 @@ Result RestGraphHandler::modifyEdgeDefinition(graph::Graph& graph, EdgeDefinitio
|
||||||
bool waitForSync = _request->parsedValue(StaticStrings::WaitForSyncString, false);
|
bool waitForSync = _request->parsedValue(StaticStrings::WaitForSyncString, false);
|
||||||
bool dropCollections = _request->parsedValue(StaticStrings::GraphDropCollections, false);
|
bool dropCollections = _request->parsedValue(StaticStrings::GraphDropCollections, false);
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
OperationResult result;
|
OperationResult result;
|
||||||
|
|
||||||
if (action == EdgeDefinitionAction::CREATE) {
|
if (action == EdgeDefinitionAction::CREATE) {
|
||||||
|
@ -694,8 +694,6 @@ Result RestGraphHandler::modifyEdgeDefinition(graph::Graph& graph, EdgeDefinitio
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
|
|
||||||
auto newGraph = getGraph(graph.name());
|
auto newGraph = getGraph(graph.name());
|
||||||
TRI_ASSERT(newGraph != nullptr);
|
TRI_ASSERT(newGraph != nullptr);
|
||||||
VPackBuilder builder;
|
VPackBuilder builder;
|
||||||
|
@ -724,7 +722,8 @@ Result RestGraphHandler::modifyVertexDefinition(graph::Graph& graph,
|
||||||
bool createCollection =
|
bool createCollection =
|
||||||
_request->parsedValue(StaticStrings::GraphCreateCollection, true);
|
_request->parsedValue(StaticStrings::GraphCreateCollection, true);
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
OperationResult result;
|
OperationResult result;
|
||||||
|
|
||||||
if (action == VertexDefinitionAction::CREATE) {
|
if (action == VertexDefinitionAction::CREATE) {
|
||||||
|
@ -740,8 +739,6 @@ Result RestGraphHandler::modifyVertexDefinition(graph::Graph& graph,
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
|
|
||||||
auto newGraph = getGraph(graph.name());
|
auto newGraph = getGraph(graph.name());
|
||||||
TRI_ASSERT(newGraph != nullptr);
|
TRI_ASSERT(newGraph != nullptr);
|
||||||
VPackBuilder builder;
|
VPackBuilder builder;
|
||||||
|
@ -785,7 +782,8 @@ Result RestGraphHandler::documentModify(graph::Graph& graph, const std::string&
|
||||||
std::unique_ptr<VPackBuilder> builder;
|
std::unique_ptr<VPackBuilder> builder;
|
||||||
auto maybeRev = handleRevision();
|
auto maybeRev = handleRevision();
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
|
|
||||||
OperationResult result;
|
OperationResult result;
|
||||||
// TODO get rid of this branching, rather use several functions and reuse the
|
// TODO get rid of this branching, rather use several functions and reuse the
|
||||||
|
@ -811,7 +809,6 @@ Result RestGraphHandler::documentModify(graph::Graph& graph, const std::string&
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
switch (colType) {
|
switch (colType) {
|
||||||
case TRI_COL_TYPE_DOCUMENT:
|
case TRI_COL_TYPE_DOCUMENT:
|
||||||
generateVertexModified(result._options.waitForSync, result.slice(),
|
generateVertexModified(result._options.waitForSync, result.slice(),
|
||||||
|
@ -828,7 +825,7 @@ Result RestGraphHandler::documentModify(graph::Graph& graph, const std::string&
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result RestGraphHandler::documentCreate(graph::Graph& graph, const std::string& collectionName,
|
Result RestGraphHandler::documentCreate(graph::Graph& graph, std::string const& collectionName,
|
||||||
TRI_col_type_e colType) {
|
TRI_col_type_e colType) {
|
||||||
bool parseSuccess = false;
|
bool parseSuccess = false;
|
||||||
VPackSlice body = this->parseVPackBody(parseSuccess);
|
VPackSlice body = this->parseVPackBody(parseSuccess);
|
||||||
|
@ -839,7 +836,8 @@ Result RestGraphHandler::documentCreate(graph::Graph& graph, const std::string&
|
||||||
bool waitForSync = _request->parsedValue(StaticStrings::WaitForSyncString, false);
|
bool waitForSync = _request->parsedValue(StaticStrings::WaitForSyncString, false);
|
||||||
bool returnNew = _request->parsedValue(StaticStrings::ReturnNewString, false);
|
bool returnNew = _request->parsedValue(StaticStrings::ReturnNewString, false);
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
|
|
||||||
OperationResult result;
|
OperationResult result;
|
||||||
if (colType == TRI_COL_TYPE_DOCUMENT) {
|
if (colType == TRI_COL_TYPE_DOCUMENT) {
|
||||||
|
@ -853,10 +851,7 @@ Result RestGraphHandler::documentCreate(graph::Graph& graph, const std::string&
|
||||||
if (result.fail()) {
|
if (result.fail()) {
|
||||||
// need to call more detailed constructor here
|
// need to call more detailed constructor here
|
||||||
generateTransactionError(collectionName, result, "", 0);
|
generateTransactionError(collectionName, result, "", 0);
|
||||||
return result.result;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
switch (colType) {
|
switch (colType) {
|
||||||
case TRI_COL_TYPE_DOCUMENT:
|
case TRI_COL_TYPE_DOCUMENT:
|
||||||
generateVertexCreated(result._options.waitForSync, result.slice(),
|
generateVertexCreated(result._options.waitForSync, result.slice(),
|
||||||
|
@ -869,8 +864,9 @@ Result RestGraphHandler::documentCreate(graph::Graph& graph, const std::string&
|
||||||
default:
|
default:
|
||||||
TRI_ASSERT(false);
|
TRI_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TRI_ERROR_NO_ERROR;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result RestGraphHandler::vertexActionRemove(graph::Graph& graph,
|
Result RestGraphHandler::vertexActionRemove(graph::Graph& graph,
|
||||||
|
@ -882,7 +878,8 @@ Result RestGraphHandler::vertexActionRemove(graph::Graph& graph,
|
||||||
|
|
||||||
auto maybeRev = handleRevision();
|
auto maybeRev = handleRevision();
|
||||||
|
|
||||||
GraphOperations gops{graph, _vocbase};
|
auto ctx = createTransactionContext();
|
||||||
|
GraphOperations gops{graph, _vocbase, ctx};
|
||||||
|
|
||||||
OperationResult result =
|
OperationResult result =
|
||||||
gops.removeVertex(collectionName, key, maybeRev, waitForSync, returnOld);
|
gops.removeVertex(collectionName, key, maybeRev, waitForSync, returnOld);
|
||||||
|
@ -892,7 +889,6 @@ Result RestGraphHandler::vertexActionRemove(graph::Graph& graph,
|
||||||
return result.result;
|
return result.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = std::make_shared<transaction::StandaloneContext>(_vocbase);
|
|
||||||
generateRemoved(true, result._options.waitForSync, result.slice().get("old"),
|
generateRemoved(true, result._options.waitForSync, result.slice().get("old"),
|
||||||
*ctx->getVPackOptionsForDump());
|
*ctx->getVPackOptionsForDump());
|
||||||
|
|
||||||
|
|
|
@ -544,7 +544,7 @@ void RestVocbaseBaseHandler::extractStringParameter(std::string const& name,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SingleCollectionTransaction> RestVocbaseBaseHandler::createTransaction(
|
std::unique_ptr<SingleCollectionTransaction> RestVocbaseBaseHandler::createTransaction(
|
||||||
std::string const& name, AccessMode::Type type) const {
|
std::string const& collectionName, AccessMode::Type type) const {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
std::string value = _request->header(StaticStrings::TransactionId, found);
|
std::string value = _request->header(StaticStrings::TransactionId, found);
|
||||||
if (found) {
|
if (found) {
|
||||||
|
@ -581,15 +581,15 @@ std::unique_ptr<SingleCollectionTransaction> RestVocbaseBaseHandler::createTrans
|
||||||
LOG_TOPIC("e94ea", DEBUG, Logger::TRANSACTIONS) << "Transaction with id '" << tid << "' not found";
|
LOG_TOPIC("e94ea", DEBUG, Logger::TRANSACTIONS) << "Transaction with id '" << tid << "' not found";
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_TRANSACTION_NOT_FOUND);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_TRANSACTION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
return std::make_unique<SingleCollectionTransaction>(ctx, name, type);
|
return std::make_unique<SingleCollectionTransaction>(ctx, collectionName, type);
|
||||||
} else {
|
} else {
|
||||||
auto ctx = transaction::StandaloneContext::Create(_vocbase);
|
auto ctx = transaction::StandaloneContext::Create(_vocbase);
|
||||||
return std::make_unique<SingleCollectionTransaction>(ctx, name, type);
|
return std::make_unique<SingleCollectionTransaction>(ctx, collectionName, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create proper transaction context, inclusing the proper IDs
|
/// @brief create proper transaction context, inclusing the proper IDs
|
||||||
std::shared_ptr<transaction::Context> RestVocbaseBaseHandler::createAQLTransactionContext() const {
|
std::shared_ptr<transaction::Context> RestVocbaseBaseHandler::createTransactionContext() const {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
std::string value = _request->header(StaticStrings::TransactionId, found);
|
std::string value = _request->header(StaticStrings::TransactionId, found);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
|
|
@ -39,347 +39,207 @@ namespace arangodb {
|
||||||
class SingleCollectionTransaction;
|
class SingleCollectionTransaction;
|
||||||
class VocbaseContext;
|
class VocbaseContext;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief abstract base request handler
|
/// @brief abstract base request handler
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class RestVocbaseBaseHandler : public RestBaseHandler {
|
class RestVocbaseBaseHandler : public RestBaseHandler {
|
||||||
RestVocbaseBaseHandler(RestVocbaseBaseHandler const&) = delete;
|
RestVocbaseBaseHandler(RestVocbaseBaseHandler const&) = delete;
|
||||||
RestVocbaseBaseHandler& operator=(RestVocbaseBaseHandler const&) = delete;
|
RestVocbaseBaseHandler& operator=(RestVocbaseBaseHandler const&) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief agency public path
|
/// @brief agency public path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const AGENCY_PATH;
|
static std::string const AGENCY_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief agency private path
|
/// @brief agency private path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const AGENCY_PRIV_PATH;
|
static std::string const AGENCY_PRIV_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief analyzer path
|
/// @brief analyzer path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
static std::string const ANALYZER_PATH;
|
static std::string const ANALYZER_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief batch path
|
/// @brief batch path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const BATCH_PATH;
|
static std::string const BATCH_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief collection path
|
/// @brief collection path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const COLLECTION_PATH;
|
static std::string const COLLECTION_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief control pregel path
|
/// @brief control pregel path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const CONTROL_PREGEL_PATH;
|
static std::string const CONTROL_PREGEL_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief cursor path
|
/// @brief cursor path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const CURSOR_PATH;
|
static std::string const CURSOR_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief database path
|
/// @brief database path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const DATABASE_PATH;
|
static std::string const DATABASE_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief document path
|
/// @brief document path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const DOCUMENT_PATH;
|
static std::string const DOCUMENT_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief edges path
|
/// @brief edges path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const EDGES_PATH;
|
static std::string const EDGES_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief gharial graph api path
|
/// @brief gharial graph api path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const GHARIAL_PATH;
|
static std::string const GHARIAL_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief endpoint path
|
/// @brief endpoint path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const ENDPOINT_PATH;
|
static std::string const ENDPOINT_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief document import path
|
/// @brief document import path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const IMPORT_PATH;
|
static std::string const IMPORT_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief index path
|
/// @brief index path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const INDEX_PATH;
|
static std::string const INDEX_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief replication path
|
/// @brief replication path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const REPLICATION_PATH;
|
static std::string const REPLICATION_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query all path
|
/// @brief simple query all path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_QUERY_ALL_PATH;
|
static std::string const SIMPLE_QUERY_ALL_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query all keys path
|
/// @brief simple query all keys path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_QUERY_ALL_KEYS_PATH;
|
static std::string const SIMPLE_QUERY_ALL_KEYS_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query by example path
|
/// @brief simple query by example path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_QUERY_BY_EXAMPLE;
|
static std::string const SIMPLE_QUERY_BY_EXAMPLE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query first example path
|
/// @brief simple query first example path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_FIRST_EXAMPLE;
|
static std::string const SIMPLE_FIRST_EXAMPLE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query remove by example path
|
/// @brief simple query remove by example path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_REMOVE_BY_EXAMPLE;
|
static std::string const SIMPLE_REMOVE_BY_EXAMPLE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query replace by example path
|
/// @brief simple query replace by example path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_REPLACE_BY_EXAMPLE;
|
static std::string const SIMPLE_REPLACE_BY_EXAMPLE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple query replace by example path
|
/// @brief simple query replace by example path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_UPDATE_BY_EXAMPLE;
|
static std::string const SIMPLE_UPDATE_BY_EXAMPLE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple batch document lookup path
|
/// @brief simple batch document lookup path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_LOOKUP_PATH;
|
static std::string const SIMPLE_LOOKUP_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief simple batch document removal path
|
/// @brief simple batch document removal path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const SIMPLE_REMOVE_PATH;
|
static std::string const SIMPLE_REMOVE_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief tasks path
|
/// @brief tasks path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const TASKS_PATH;
|
static std::string const TASKS_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief upload path
|
/// @brief upload path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const UPLOAD_PATH;
|
static std::string const UPLOAD_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief users path
|
/// @brief users path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const USERS_PATH;
|
static std::string const USERS_PATH;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief view path
|
/// @brief view path
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static std::string const VIEW_PATH;
|
static std::string const VIEW_PATH;
|
||||||
|
|
||||||
/// @brief Internal Traverser path
|
/// @brief Internal Traverser path
|
||||||
|
|
||||||
static std::string const INTERNAL_TRAVERSER_PATH;
|
static std::string const INTERNAL_TRAVERSER_PATH;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RestVocbaseBaseHandler(GeneralRequest*, GeneralResponse*);
|
RestVocbaseBaseHandler(GeneralRequest*, GeneralResponse*);
|
||||||
~RestVocbaseBaseHandler();
|
~RestVocbaseBaseHandler();
|
||||||
|
|
||||||
protected:
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief assemble a document id from a string and a string
|
|
||||||
/// optionally url-encodes
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
std::string assembleDocumentId(std::string const&, std::string const&, bool);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates a HTTP 201 or 202 response
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generate20x(arangodb::OperationResult const&, std::string const&,
|
|
||||||
TRI_col_type_e, arangodb::velocypack::Options const*,
|
|
||||||
bool isMultiple, rest::ResponseCode waitForSyncResponseCode);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates message for a saved document
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateSaved(arangodb::OperationResult const& result,
|
|
||||||
std::string const& collectionName, TRI_col_type_e type,
|
|
||||||
arangodb::velocypack::Options const*, bool isMultiple);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates deleted message
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateDeleted(arangodb::OperationResult const& result,
|
|
||||||
std::string const& collectionName, TRI_col_type_e type,
|
|
||||||
arangodb::velocypack::Options const*, bool isMultiple);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates document not found error message, no transaction info
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateDocumentNotFound(std::string const& /* collection name */,
|
|
||||||
std::string const& /* document key */) {
|
|
||||||
generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates not implemented
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateNotImplemented(std::string const&);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates forbidden
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateForbidden();
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates precondition failed, without transaction info
|
|
||||||
/// DEPRECATED
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generatePreconditionFailed(std::string const&, std::string const& key,
|
|
||||||
TRI_voc_rid_t rev);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates precondition failed, without transaction info
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generatePreconditionFailed(arangodb::velocypack::Slice const& slice);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates not modified
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateNotModified(TRI_voc_rid_t);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generates first entry from a result set
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateDocument(arangodb::velocypack::Slice const&, bool,
|
|
||||||
arangodb::velocypack::Options const* options = nullptr);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generate an error message for a transaction error, this method
|
|
||||||
/// is used by the others.
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateTransactionError(std::string const&, OperationResult const&,
|
|
||||||
std::string const& key, TRI_voc_rid_t = 0);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generate an error message for a transaction error
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateTransactionError(std::string const& str, Result const& res,
|
|
||||||
std::string const& key, TRI_voc_rid_t rid = 0) {
|
|
||||||
generateTransactionError(str, OperationResult(res), key, rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generate an error message for a transaction error
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void generateTransactionError(OperationResult const& result) {
|
|
||||||
generateTransactionError("", result, "", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief extracts the revision
|
|
||||||
///
|
|
||||||
/// @note @FA{header} must be lowercase.
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TRI_voc_rid_t extractRevision(char const*, bool&) const;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief extracts a string parameter value
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void extractStringParameter(std::string const& name, std::string& ret) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Helper to create a new Transaction for a single collection. The
|
|
||||||
* helper method will consider no-lock headers send via http and will lock the
|
|
||||||
* collection accordingly.
|
|
||||||
*
|
|
||||||
* @param collectionName Name of the collection to be locked
|
|
||||||
* @param mode The access mode (READ / WRITE / EXCLUSIVE)
|
|
||||||
*
|
|
||||||
* @return A freshly created transaction for the given collection with proper
|
|
||||||
* locking.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<SingleCollectionTransaction> createTransaction(std::string const& cname,
|
|
||||||
AccessMode::Type mode) const;
|
|
||||||
|
|
||||||
/// @brief create proper transaction context, inclusing the proper IDs
|
|
||||||
std::shared_ptr<transaction::Context> createAQLTransactionContext() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief request context
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
VocbaseContext& _context;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief the vocbase, managed by VocbaseContext do not release
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TRI_vocbase_t& _vocbase;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual bool cancel() override {
|
virtual bool cancel() override {
|
||||||
_context.cancel();
|
_context.cancel();
|
||||||
|
|
||||||
return RestBaseHandler::cancel();
|
return RestBaseHandler::cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @brief assemble a document id from a string and a string
|
||||||
|
/// optionally url-encodes
|
||||||
|
std::string assembleDocumentId(std::string const& collectionName,
|
||||||
|
std::string const& key, bool urlEncode);
|
||||||
|
|
||||||
|
/// @brief generates a HTTP 201 or 202 response
|
||||||
|
void generate20x(arangodb::OperationResult const&, std::string const&,
|
||||||
|
TRI_col_type_e, arangodb::velocypack::Options const*,
|
||||||
|
bool isMultiple, rest::ResponseCode waitForSyncResponseCode);
|
||||||
|
|
||||||
|
/// @brief generates message for a saved document
|
||||||
|
void generateSaved(arangodb::OperationResult const& result,
|
||||||
|
std::string const& collectionName, TRI_col_type_e type,
|
||||||
|
arangodb::velocypack::Options const*, bool isMultiple);
|
||||||
|
|
||||||
|
/// @brief generates deleted message
|
||||||
|
void generateDeleted(arangodb::OperationResult const& result,
|
||||||
|
std::string const& collectionName, TRI_col_type_e type,
|
||||||
|
arangodb::velocypack::Options const*, bool isMultiple);
|
||||||
|
|
||||||
|
/// @brief generates document not found error message, no transaction info
|
||||||
|
void generateDocumentNotFound(std::string const& /* collection name */,
|
||||||
|
std::string const& /* document key */) {
|
||||||
|
generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief generates not implemented
|
||||||
|
void generateNotImplemented(std::string const& path);
|
||||||
|
|
||||||
|
/// @brief generates forbidden
|
||||||
|
void generateForbidden();
|
||||||
|
|
||||||
|
/// @brief generates precondition failed, without transaction info
|
||||||
|
/// DEPRECATED
|
||||||
|
void generatePreconditionFailed(std::string const&, std::string const& key,
|
||||||
|
TRI_voc_rid_t rev);
|
||||||
|
|
||||||
|
/// @brief generates precondition failed, without transaction info
|
||||||
|
void generatePreconditionFailed(arangodb::velocypack::Slice const& slice);
|
||||||
|
|
||||||
|
/// @brief generates not modified
|
||||||
|
void generateNotModified(TRI_voc_rid_t);
|
||||||
|
|
||||||
|
/// @brief generates first entry from a result set
|
||||||
|
void generateDocument(arangodb::velocypack::Slice const& input,
|
||||||
|
bool generateBody,
|
||||||
|
arangodb::velocypack::Options const* options = nullptr);
|
||||||
|
|
||||||
|
/// @brief generate an error message for a transaction error, this method
|
||||||
|
/// is used by the others.
|
||||||
|
void generateTransactionError(std::string const& collectionName,
|
||||||
|
OperationResult const& result,
|
||||||
|
std::string const& key, TRI_voc_rid_t = 0);
|
||||||
|
|
||||||
|
/// @brief generate an error message for a transaction error
|
||||||
|
void generateTransactionError(std::string const& collectionName,
|
||||||
|
Result const& res,
|
||||||
|
std::string const& key, TRI_voc_rid_t rid = 0) {
|
||||||
|
generateTransactionError(collectionName, OperationResult(res), key, rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief generate an error message for a transaction error
|
||||||
|
void generateTransactionError(OperationResult const& result) {
|
||||||
|
generateTransactionError("", result, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief extracts the revision. "header" must be lowercase.
|
||||||
|
TRI_voc_rid_t extractRevision(char const* header, bool& isValid) const;
|
||||||
|
|
||||||
|
/// @brief extracts a string parameter value
|
||||||
|
void extractStringParameter(std::string const& name, std::string& ret) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper to create a new Transaction for a single collection. The
|
||||||
|
* helper method will will lock the collection accordingly. It will additionally
|
||||||
|
* check if there is a transaction-id header and will make use of an existing
|
||||||
|
* transaction if a transaction id is specified. it can also start a new
|
||||||
|
* transaction lazily if requested.
|
||||||
|
*
|
||||||
|
* @param collectionName Name of the collection to be locked
|
||||||
|
* @param mode The access mode (READ / WRITE / EXCLUSIVE)
|
||||||
|
*
|
||||||
|
* @return A freshly created transaction for the given collection with proper
|
||||||
|
* locking or a leased transaction.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<SingleCollectionTransaction> createTransaction(std::string const& cname,
|
||||||
|
AccessMode::Type mode) const;
|
||||||
|
|
||||||
|
/// @brief create proper transaction context, including the proper IDs
|
||||||
|
std::shared_ptr<transaction::Context> createTransactionContext() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @brief request context
|
||||||
|
VocbaseContext& _context;
|
||||||
|
|
||||||
|
/// @brief the vocbase, managed by VocbaseContext
|
||||||
|
TRI_vocbase_t& _vocbase;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace arangodb
|
} // namespace arangodb
|
||||||
|
|
|
@ -189,7 +189,8 @@ static void JS_GetGraphs(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice().get("graphs")));
|
auto ctx = std::make_shared<transaction::StandaloneContext>(vocbase);
|
||||||
|
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice().get("graphs"), ctx->getVPackOptionsForDump()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_V8_RETURN_UNDEFINED();
|
TRI_V8_RETURN_UNDEFINED();
|
||||||
|
@ -306,7 +307,8 @@ static void JS_AddEdgeDefinitions(v8::FunctionCallbackInfo<v8::Value> const& arg
|
||||||
}
|
}
|
||||||
TRI_ASSERT(graph.get() != nullptr);
|
TRI_ASSERT(graph.get() != nullptr);
|
||||||
|
|
||||||
GraphOperations gops{*graph.get(), vocbase};
|
auto ctx = transaction::V8Context::Create(vocbase, true);
|
||||||
|
GraphOperations gops{*graph.get(), vocbase, ctx};
|
||||||
OperationResult r = gops.addEdgeDefinition(edgeDefinition.slice(), false);
|
OperationResult r = gops.addEdgeDefinition(edgeDefinition.slice(), false);
|
||||||
|
|
||||||
if (r.fail()) {
|
if (r.fail()) {
|
||||||
|
@ -354,7 +356,8 @@ static void JS_EditEdgeDefinitions(v8::FunctionCallbackInfo<v8::Value> const& ar
|
||||||
}
|
}
|
||||||
TRI_ASSERT(graph.get() != nullptr);
|
TRI_ASSERT(graph.get() != nullptr);
|
||||||
|
|
||||||
GraphOperations gops{*(graph.get()), vocbase};
|
auto ctx = transaction::V8Context::Create(vocbase, true);
|
||||||
|
GraphOperations gops{*graph.get(), vocbase, ctx};
|
||||||
OperationResult r =
|
OperationResult r =
|
||||||
gops.editEdgeDefinition(edgeDefinition.slice(), false,
|
gops.editEdgeDefinition(edgeDefinition.slice(), false,
|
||||||
edgeDefinition.slice().get("collection").copyString());
|
edgeDefinition.slice().get("collection").copyString());
|
||||||
|
@ -420,7 +423,8 @@ static void JS_RemoveVertexCollection(v8::FunctionCallbackInfo<v8::Value> const&
|
||||||
builder.add("collection", VPackValue(vertexName));
|
builder.add("collection", VPackValue(vertexName));
|
||||||
builder.close();
|
builder.close();
|
||||||
|
|
||||||
GraphOperations gops{*(graph.get()), vocbase};
|
auto ctx = transaction::V8Context::Create(vocbase, true);
|
||||||
|
GraphOperations gops{*graph.get(), vocbase, ctx};
|
||||||
OperationResult r = gops.eraseOrphanCollection(false, vertexName, dropCollection);
|
OperationResult r = gops.eraseOrphanCollection(false, vertexName, dropCollection);
|
||||||
|
|
||||||
if (r.fail()) {
|
if (r.fail()) {
|
||||||
|
@ -472,7 +476,7 @@ static void JS_AddVertexCollection(v8::FunctionCallbackInfo<v8::Value> const& ar
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& vocbase = GetContextVocBase(isolate);
|
auto& vocbase = GetContextVocBase(isolate);
|
||||||
auto ctx = transaction::V8Context::Create(vocbase, false);
|
auto ctx = transaction::V8Context::Create(vocbase, true);
|
||||||
|
|
||||||
GraphManager gmngr{vocbase};
|
GraphManager gmngr{vocbase};
|
||||||
auto graph = gmngr.lookupGraphByName(graphName);
|
auto graph = gmngr.lookupGraphByName(graphName);
|
||||||
|
@ -481,7 +485,7 @@ static void JS_AddVertexCollection(v8::FunctionCallbackInfo<v8::Value> const& ar
|
||||||
}
|
}
|
||||||
TRI_ASSERT(graph.get() != nullptr);
|
TRI_ASSERT(graph.get() != nullptr);
|
||||||
|
|
||||||
GraphOperations gops{*(graph.get()), vocbase};
|
GraphOperations gops{*graph.get(), vocbase, ctx};
|
||||||
|
|
||||||
VPackBuilder builder;
|
VPackBuilder builder;
|
||||||
builder.openObject();
|
builder.openObject();
|
||||||
|
@ -548,7 +552,8 @@ static void JS_DropEdgeDefinition(v8::FunctionCallbackInfo<v8::Value> const& arg
|
||||||
}
|
}
|
||||||
TRI_ASSERT(graph.get() != nullptr);
|
TRI_ASSERT(graph.get() != nullptr);
|
||||||
|
|
||||||
GraphOperations gops{*(graph.get()), vocbase};
|
auto ctx = transaction::V8Context::Create(vocbase, true);
|
||||||
|
GraphOperations gops{*graph.get(), vocbase, ctx};
|
||||||
OperationResult r = gops.eraseEdgeDefinition(false, edgeDefinitionName, dropCollections);
|
OperationResult r = gops.eraseEdgeDefinition(false, edgeDefinitionName, dropCollections);
|
||||||
|
|
||||||
if (r.fail()) {
|
if (r.fail()) {
|
||||||
|
|
|
@ -0,0 +1,759 @@
|
||||||
|
/* jshint globalstrict:false, strict:false, maxlen: 200 */
|
||||||
|
/* global fail, arango, assertTrue, assertFalse, assertEqual, assertNotUndefined */
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief ArangoTransaction tests for graphs
|
||||||
|
// /
|
||||||
|
// /
|
||||||
|
// / DISCLAIMER
|
||||||
|
// /
|
||||||
|
// / Copyright 2018 ArangoDB 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
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var jsunity = require('jsunity');
|
||||||
|
var arangodb = require('@arangodb');
|
||||||
|
var ERRORS = arangodb.errors;
|
||||||
|
var Graph = require('@arangodb/general-graph');
|
||||||
|
var db = arangodb.db;
|
||||||
|
|
||||||
|
function JSTransactionGraphSuite () {
|
||||||
|
'use strict';
|
||||||
|
const graph = 'UnitTestsGraph';
|
||||||
|
const vertex = 'UnitTestsVertex';
|
||||||
|
const edge = 'UnitTestsEdge';
|
||||||
|
let g;
|
||||||
|
|
||||||
|
return {
|
||||||
|
setUp: function () {
|
||||||
|
if (Graph._exists(graph)) {
|
||||||
|
Graph._drop(graph, true);
|
||||||
|
}
|
||||||
|
g = Graph._create(graph,
|
||||||
|
[Graph._relation(edge, vertex, vertex)]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
tearDown: function () {
|
||||||
|
Graph._drop(graph, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert vertex
|
||||||
|
testVertexInsertUndeclaredJS: function () {
|
||||||
|
try {
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: {},
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].insert({ _key: "test", value: "test" });
|
||||||
|
},
|
||||||
|
params: { vertex, graph }
|
||||||
|
});
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert edge
|
||||||
|
testEdgeInsertUndeclaredJS: function () {
|
||||||
|
try {
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: {},
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.edge].insert({ _key: "test", value: "test", _from: params.vertex + "/1", _to: params.vertex + "/2" });
|
||||||
|
},
|
||||||
|
params: { vertex, edge, graph }
|
||||||
|
});
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert vertex
|
||||||
|
testVertexInsertJS: function () {
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: vertex },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].insert({ _key: "test", value: "test" });
|
||||||
|
|
||||||
|
let result = graph[params.vertex].document("test");
|
||||||
|
if (result.value !== "test") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert edge
|
||||||
|
testEdgeInsertJS: function () {
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: [ vertex, edge ] },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].insert({ _key: "1", value: "test" });
|
||||||
|
graph[params.vertex].insert({ _key: "2", value: "test" });
|
||||||
|
graph[params.edge].insert({ _key: "test", value: "test", _from: params.vertex + "/1", _to: params.vertex + "/2" });
|
||||||
|
|
||||||
|
let result = graph[params.edge].document("test");
|
||||||
|
if (result.value !== "test") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, edge, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
assertEqual(vertex + "/1", result._from);
|
||||||
|
assertEqual(vertex + "/2", result._to);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex
|
||||||
|
testVertexRemoveJS: function () {
|
||||||
|
db[vertex].insert({ _key: "test", value: "test" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: vertex },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
let ERRORS = require("@arangodb").errors;
|
||||||
|
graph[params.vertex].remove("test");
|
||||||
|
|
||||||
|
try {
|
||||||
|
graph[params.vertex].document("test");
|
||||||
|
throw "peng";
|
||||||
|
} catch (err) {
|
||||||
|
if (ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code !== err.errorNum) {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(vertex).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex with dependencies
|
||||||
|
testVertexRemoveWithDependenciesJS: function () {
|
||||||
|
db[vertex].insert({ _key: "1" });
|
||||||
|
db[vertex].insert({ _key: "2" });
|
||||||
|
db[edge].insert({ _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: [ vertex, edge ] },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
let ERRORS = require("@arangodb").errors;
|
||||||
|
graph[params.vertex].remove("1");
|
||||||
|
|
||||||
|
try {
|
||||||
|
graph[params.vertex].document("1");
|
||||||
|
throw "peng";
|
||||||
|
} catch (err) {
|
||||||
|
if (ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code !== err.errorNum) {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
graph[params.edge].document("test");
|
||||||
|
throw "peng";
|
||||||
|
} catch (err) {
|
||||||
|
if (ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code !== err.errorNum) {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, edge, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(vertex).document("1");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex with dependencies
|
||||||
|
testVertexRemoveWithDependenciesUndeclaredVertexCollectionJS: function () {
|
||||||
|
db[vertex].insert({ _key: "1" });
|
||||||
|
db[vertex].insert({ _key: "2" });
|
||||||
|
db[edge].insert({ _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: edge },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].remove("1");
|
||||||
|
throw "peng";
|
||||||
|
},
|
||||||
|
params: { vertex, edge, graph }
|
||||||
|
});
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = db._collection(vertex).document("1");
|
||||||
|
assertEqual("1", result._key);
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove edge
|
||||||
|
testEdgeRemoveJS: function () {
|
||||||
|
db[vertex].insert({ _key: "1" });
|
||||||
|
db[vertex].insert({ _key: "2" });
|
||||||
|
db[edge].insert({ _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: edge },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
let ERRORS = require("@arangodb").errors;
|
||||||
|
graph[params.edge].remove("test");
|
||||||
|
|
||||||
|
try {
|
||||||
|
graph[params.edge].document("test");
|
||||||
|
throw "peng";
|
||||||
|
} catch (err) {
|
||||||
|
if (ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code !== err.errorNum) {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { edge, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: update edge
|
||||||
|
testEdgeUpdateJS: function () {
|
||||||
|
db[vertex].insert({ _key: "1" });
|
||||||
|
db[vertex].insert({ _key: "2" });
|
||||||
|
db[edge].insert({ _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: edge },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.edge].update("test", { value: "meow" });
|
||||||
|
|
||||||
|
let result = graph[params.edge].document("test");
|
||||||
|
if (result.value !== "meow") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { edge, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: update vertex
|
||||||
|
testVertexUpdateJS: function () {
|
||||||
|
db[vertex].insert({ _key: "test", value: "test" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: vertex },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].update("test", { value: "meow" });
|
||||||
|
|
||||||
|
let result = graph[params.vertex].document("test");
|
||||||
|
if (result.value !== "meow") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: replace edge
|
||||||
|
testEdgeReplaceJS: function () {
|
||||||
|
db[vertex].insert({ _key: "1" });
|
||||||
|
db[vertex].insert({ _key: "2" });
|
||||||
|
db[edge].insert({ _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: edge },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.edge].replace("test", { value: "meow", _from: params.vertex + "/1", _to: params.vertex + "/2" });
|
||||||
|
|
||||||
|
let result = graph[params.edge].document("test");
|
||||||
|
if (result.value !== "meow") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, edge, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: replace vertex
|
||||||
|
testVertexReplaceJS: function () {
|
||||||
|
db[vertex].insert({ _key: "test", value: "test" });
|
||||||
|
|
||||||
|
db._executeTransaction({
|
||||||
|
collections: { write: vertex },
|
||||||
|
action: function(params) {
|
||||||
|
let graph = require("@arangodb/general-graph")._graph(params.graph);
|
||||||
|
graph[params.vertex].replace("test", { value: "meow" });
|
||||||
|
|
||||||
|
let result = graph[params.vertex].document("test");
|
||||||
|
if (result.value !== "meow") {
|
||||||
|
throw "peng";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
params: { vertex, graph }
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionGraphSuite () {
|
||||||
|
'use strict';
|
||||||
|
const graph = 'UnitTestsGraph';
|
||||||
|
const vertex = 'UnitTestsVertex';
|
||||||
|
const edge = 'UnitTestsEdge';
|
||||||
|
let g;
|
||||||
|
|
||||||
|
return {
|
||||||
|
setUp: function () {
|
||||||
|
if (Graph._exists(graph)) {
|
||||||
|
Graph._drop(graph, true);
|
||||||
|
}
|
||||||
|
g = Graph._create(graph,
|
||||||
|
[Graph._relation(edge, vertex, vertex)]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
tearDown: function () {
|
||||||
|
db._transactions().forEach(function(trx) {
|
||||||
|
try {
|
||||||
|
db._createTransaction(trx.id).abort();
|
||||||
|
} catch (err) {}
|
||||||
|
});
|
||||||
|
Graph._drop(graph, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert vertex
|
||||||
|
testVertexInsertUndeclared: function () {
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "test", value: "test" }, headers);
|
||||||
|
assertTrue(result.error);
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, result.errorNum);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert edge
|
||||||
|
testEdgeInsertUndeclared: function () {
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" }, headers);
|
||||||
|
assertTrue(result.error);
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, result.errorNum);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert vertex
|
||||||
|
testVertexInsert: function () {
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "test", value: "test" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
try {
|
||||||
|
db._collection(vertex).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: insert edge
|
||||||
|
testEdgeInsert: function () {
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex, edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" }, headers);
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" }, headers);
|
||||||
|
let result = arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
try {
|
||||||
|
db._collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
assertEqual(vertex + "/1", result._from);
|
||||||
|
assertEqual(vertex + "/2", result._to);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex
|
||||||
|
testVertexRemove: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "test", value: "test" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex, edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.DELETE("/_api/gharial/" + graph + "/vertex/" + vertex + "/test", null, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
trx.collection(vertex).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(vertex).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex with dependencies
|
||||||
|
testVertexRemoveWithDependencies: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex, edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.DELETE("/_api/gharial/" + graph + "/vertex/" + vertex + "/1", null, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(vertex).document("1");
|
||||||
|
assertEqual("1", result._key);
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
try {
|
||||||
|
trx.collection(vertex).document("1");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
trx.collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(vertex).document("1");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove vertex with dependencies
|
||||||
|
testVertexRemoveWithDependenciesUndeclaredVertexCollection: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.DELETE("/_api/gharial/" + graph + "/vertex/" + vertex + "/1", null, headers);
|
||||||
|
assertTrue(result.error);
|
||||||
|
assertEqual(ERRORS.ERROR_TRANSACTION_UNREGISTERED_COLLECTION.code, result.errorNum);
|
||||||
|
|
||||||
|
trx.abort();
|
||||||
|
|
||||||
|
result = db._collection(vertex).document("1");
|
||||||
|
assertEqual("1", result._key);
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: remove edge
|
||||||
|
testEdgeRemove: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.DELETE("/_api/gharial/" + graph + "/edge/" + edge + "/test", null, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
trx.collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
db._collection(edge).document("test");
|
||||||
|
fail();
|
||||||
|
} catch (err) {
|
||||||
|
assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, err.errorNum);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: update edge
|
||||||
|
testEdgeUpdate: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.PATCH("/_api/gharial/" + graph + "/edge/" + edge + "/test", { value: "meow" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
|
||||||
|
result = trx.collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: update vertex
|
||||||
|
testVertexUpdate: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "test", value: "test" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.PATCH("/_api/gharial/" + graph + "/vertex/" + vertex + "/test", { value: "meow" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
|
||||||
|
result = trx.collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: replace edge
|
||||||
|
testEdgeReplace: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "1" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "2" });
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/edge/" + edge, { _key: "test", value: "test", _from: vertex + "/1", _to: vertex + "/2" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ edge ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.PUT("/_api/gharial/" + graph + "/edge/" + edge + "/test", { value: "meow", _from: vertex + "/1", _to: vertex + "/2" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
|
||||||
|
result = trx.collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(edge).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
// / @brief test: replace vertex
|
||||||
|
testVertexReplace: function () {
|
||||||
|
arango.POST("/_api/gharial/" + graph + "/vertex/" + vertex, { _key: "test", value: "test" });
|
||||||
|
|
||||||
|
let trx = db._createTransaction({
|
||||||
|
collections: { write: [ vertex ] }
|
||||||
|
});
|
||||||
|
|
||||||
|
let headers = { "x-arango-trx-id" : trx.id() };
|
||||||
|
let result = arango.PUT("/_api/gharial/" + graph + "/vertex/" + vertex + "/test", { value: "meow" }, headers);
|
||||||
|
assertFalse(result.error);
|
||||||
|
assertEqual(202, result.code);
|
||||||
|
|
||||||
|
if (db._engine().name !== 'mmfiles') {
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("test", result.value);
|
||||||
|
|
||||||
|
result = trx.collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
trx.commit();
|
||||||
|
|
||||||
|
result = db._collection(vertex).document("test");
|
||||||
|
assertEqual("test", result._key);
|
||||||
|
assertEqual("meow", result.value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
jsunity.run(JSTransactionGraphSuite);
|
||||||
|
jsunity.run(TransactionGraphSuite);
|
||||||
|
|
||||||
|
return jsunity.done();
|
Loading…
Reference in New Issue