1
0
Fork 0

make graphs transaction-aware (#9855)

* make graphs transaction-aware

* simranification

* fix tests with mmfiles
This commit is contained in:
Jan 2019-09-02 21:06:10 +02:00 committed by KVS85
parent 10d99b20cb
commit f8c156a44f
10 changed files with 927 additions and 301 deletions

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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

View File

@ -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(); });

View File

@ -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());

View File

@ -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) {

View File

@ -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

View File

@ -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()) {

View File

@ -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();