1
0
Fork 0

validate collection when leasing an existing transaction (#9990)

* validate collection when leasing an existing transaction

* use WITH statement

* allow implicit read collections again

* simplify collection name validation

* re-implement it

* remove dead code

* remove unused method

* apply review comments

* whoops, deleted too many methods

* fixit
This commit is contained in:
Jan 2019-09-13 19:03:55 +02:00 committed by KVS85
parent aeba4bc2c6
commit 351ca41553
19 changed files with 171 additions and 61 deletions

View File

@ -825,7 +825,7 @@ Collection* addCollectionToQuery(Query* query, std::string const& cname, bool as
auto cptr = query->trx()->vocbase().lookupCollection(cname); auto cptr = query->trx()->vocbase().lookupCollection(cname);
coll->setCollection(cptr.get()); coll->setCollection(cptr.get());
query->trx()->addCollectionAtRuntime(cname); query->trx()->addCollectionAtRuntime(cname, AccessMode::Type::READ);
} }
} }

View File

@ -611,7 +611,22 @@ void RestCollectionHandler::collectionRepresentation(
bool showProperties, bool showFigures, bool showCount, bool detailedCount) { bool showProperties, bool showFigures, bool showCount, bool detailedCount) {
if (showProperties || showCount) { if (showProperties || showCount) {
// Here we need a transaction // Here we need a transaction
auto trx = createTransaction(coll.name(), AccessMode::Type::READ); std::unique_ptr<transaction::Methods> trx;
try {
trx = createTransaction(coll.name(), AccessMode::Type::READ);
} catch (basics::Exception const& ex) {
if (ex.code() == TRI_ERROR_TRANSACTION_NOT_FOUND) {
// this will happen if the tid of a managed transaction is passed in,
// but the transaction hasn't yet started on the DB server. in
// this case, we create an ad-hoc transaction on the underlying
// collection
trx = std::make_unique<SingleCollectionTransaction>(transaction::StandaloneContext::Create(_vocbase), coll.name(), AccessMode::Type::READ);
} else {
throw;
}
}
TRI_ASSERT(trx != nullptr);
Result res = trx->begin(); Result res = trx->begin();
if (res.fail()) { if (res.fail()) {

View File

@ -27,10 +27,10 @@
#include "Aql/Variable.h" #include "Aql/Variable.h"
#include "Cluster/ClusterMethods.h" #include "Cluster/ClusterMethods.h"
#include "Cluster/ServerState.h" #include "Cluster/ServerState.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h" #include "Transaction/StandaloneContext.h"
#include "Utils/CollectionNameResolver.h" #include "Utils/CollectionNameResolver.h"
#include "Utils/OperationCursor.h" #include "Utils/OperationCursor.h"
#include "Utils/SingleCollectionTransaction.h"
#include <velocypack/Iterator.h> #include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
@ -64,7 +64,7 @@ RestStatus RestEdgesHandler::execute() {
void RestEdgesHandler::readCursor(aql::AstNode* condition, aql::Variable const* var, void RestEdgesHandler::readCursor(aql::AstNode* condition, aql::Variable const* var,
std::string const& collectionName, std::string const& collectionName,
SingleCollectionTransaction& trx, transaction::Methods& trx,
std::function<void(LocalDocumentId const&)> const& cb) { std::function<void(LocalDocumentId const&)> const& cb) {
transaction::Methods::IndexHandle indexId; transaction::Methods::IndexHandle indexId;
bool foundIdx = bool foundIdx =
@ -85,10 +85,10 @@ void RestEdgesHandler::readCursor(aql::AstNode* condition, aql::Variable const*
} }
bool RestEdgesHandler::getEdgesForVertex( bool RestEdgesHandler::getEdgesForVertex(
std::string const& id, std::string const& collectionName, std::string const& id, TRI_voc_cid_t cid, std::string const& collectionName,
TRI_edge_direction_e direction, SingleCollectionTransaction& trx, TRI_edge_direction_e direction, transaction::Methods& trx,
std::function<void(LocalDocumentId const&)> const& cb) { std::function<void(LocalDocumentId const&)> const& cb) {
trx.pinData(trx.cid()); // will throw when it fails trx.pinData(cid); // will throw when it fails
// Create a conditionBuilder that manages the AstNodes for querying // Create a conditionBuilder that manages the AstNodes for querying
aql::EdgeConditionBuilderContainer condBuilder; aql::EdgeConditionBuilderContainer condBuilder;
@ -234,7 +234,8 @@ bool RestEdgesHandler::readEdges() {
resultBuilder.add(VPackValue("edges")); // only key resultBuilder.add(VPackValue("edges")); // only key
resultBuilder.openArray(); resultBuilder.openArray();
auto collection = trx->documentCollection(); TRI_voc_cid_t cid = trx->addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
auto collection = trx->documentCollection(cid);
std::unordered_set<LocalDocumentId> foundTokens; std::unordered_set<LocalDocumentId> foundTokens;
auto cb = [&](LocalDocumentId const& token) { auto cb = [&](LocalDocumentId const& token) {
if (foundTokens.find(token) == foundTokens.end()) { if (foundTokens.find(token) == foundTokens.end()) {
@ -249,7 +250,7 @@ bool RestEdgesHandler::readEdges() {
}; };
// NOTE: collectionName is the shard-name in DBServer case // NOTE: collectionName is the shard-name in DBServer case
bool ok = getEdgesForVertex(startVertex, collectionName, direction, *(trx.get()), cb); bool ok = getEdgesForVertex(startVertex, cid, collectionName, direction, *(trx.get()), cb);
resultBuilder.close(); resultBuilder.close();
res = trx->finish(res); res = trx->finish(res);
@ -262,7 +263,7 @@ bool RestEdgesHandler::readEdges() {
if (ServerState::instance()->isDBServer()) { if (ServerState::instance()->isDBServer()) {
// If we are a DBserver, we want to use the cluster-wide collection // If we are a DBserver, we want to use the cluster-wide collection
// name for error reporting: // name for error reporting:
collectionName = trx->resolver()->getCollectionNameCluster(trx->cid()); collectionName = trx->resolver()->getCollectionNameCluster(cid);
} }
generateTransactionError(collectionName, res, ""); generateTransactionError(collectionName, res, "");
return false; return false;
@ -373,7 +374,8 @@ bool RestEdgesHandler::readEdgesForMultipleVertices() {
resultBuilder.add(VPackValue("edges")); // only key resultBuilder.add(VPackValue("edges")); // only key
resultBuilder.openArray(); resultBuilder.openArray();
auto collection = trx->documentCollection(); TRI_voc_cid_t cid = trx->addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
auto collection = trx->documentCollection(cid);
std::unordered_set<LocalDocumentId> foundTokens; std::unordered_set<LocalDocumentId> foundTokens;
auto cb = [&](LocalDocumentId const& token) { auto cb = [&](LocalDocumentId const& token) {
if (foundTokens.find(token) == foundTokens.end()) { if (foundTokens.find(token) == foundTokens.end()) {
@ -392,7 +394,7 @@ bool RestEdgesHandler::readEdgesForMultipleVertices() {
std::string startVertex = it.copyString(); std::string startVertex = it.copyString();
// We ignore if this fails // We ignore if this fails
getEdgesForVertex(startVertex, collectionName, direction, *(trx.get()), cb); getEdgesForVertex(startVertex, cid, collectionName, direction, *(trx.get()), cb);
} }
} }
resultBuilder.close(); resultBuilder.close();
@ -402,7 +404,7 @@ bool RestEdgesHandler::readEdgesForMultipleVertices() {
if (ServerState::instance()->isDBServer()) { if (ServerState::instance()->isDBServer()) {
// If we are a DBserver, we want to use the cluster-wide collection // If we are a DBserver, we want to use the cluster-wide collection
// name for error reporting: // name for error reporting:
collectionName = trx->resolver()->getCollectionNameCluster(trx->cid()); collectionName = trx->resolver()->getCollectionNameCluster(cid);
} }
generateTransactionError(collectionName, res, ""); generateTransactionError(collectionName, res, "");
return false; return false;

View File

@ -31,7 +31,9 @@
namespace arangodb { namespace arangodb {
class LocalDocumentId; class LocalDocumentId;
class SingleCollectionTransaction; namespace transaction {
class Methods;
}
namespace aql { namespace aql {
struct AstNode; struct AstNode;
@ -65,15 +67,16 @@ class RestEdgesHandler : public RestVocbaseBaseHandler {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
void readCursor(aql::AstNode* condition, aql::Variable const* var, void readCursor(aql::AstNode* condition, aql::Variable const* var,
std::string const& collectionName, SingleCollectionTransaction& trx, std::string const& collectionName, transaction::Methods& trx,
std::function<void(LocalDocumentId const&)> const& cb); std::function<void(LocalDocumentId const&)> const& cb);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief get all edges for a given vertex. Independent from the request /// @brief get all edges for a given vertex. Independent from the request
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
bool getEdgesForVertex(std::string const& id, std::string const& collectionName, bool getEdgesForVertex(std::string const& id, TRI_voc_cid_t cid,
TRI_edge_direction_e direction, SingleCollectionTransaction& trx, std::string const& collectionName,
TRI_edge_direction_e direction, transaction::Methods& trx,
std::function<void(LocalDocumentId const&)> const& cb); std::function<void(LocalDocumentId const&)> const& cb);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -26,6 +26,7 @@
#include "Cluster/ClusterInfo.h" #include "Cluster/ClusterInfo.h"
#include "Cluster/ServerState.h" #include "Cluster/ServerState.h"
#include "Rest/HttpRequest.h" #include "Rest/HttpRequest.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h" #include "Transaction/StandaloneContext.h"
#include "Utils/Events.h" #include "Utils/Events.h"
#include "Utils/SingleCollectionTransaction.h" #include "Utils/SingleCollectionTransaction.h"
@ -180,7 +181,7 @@ RestStatus RestIndexHandler::getSelectivityEstimates() {
} }
// transaction protects access onto selectivity estimates // transaction protects access onto selectivity estimates
std::unique_ptr<SingleCollectionTransaction> trx; std::unique_ptr<transaction::Methods> trx;
try { try {
trx = createTransaction(cName, AccessMode::Type::READ); trx = createTransaction(cName, AccessMode::Type::READ);
@ -204,7 +205,7 @@ RestStatus RestIndexHandler::getSelectivityEstimates() {
return RestStatus::DONE; return RestStatus::DONE;
} }
LogicalCollection* coll = trx->documentCollection(); LogicalCollection* coll = trx->documentCollection(cName);
auto idxs = coll->getIndexes(); auto idxs = coll->getIndexes();
VPackBuffer<uint8_t> buffer; VPackBuffer<uint8_t> buffer;

View File

@ -36,6 +36,7 @@
#include "Transaction/Helpers.h" #include "Transaction/Helpers.h"
#include "Transaction/Manager.h" #include "Transaction/Manager.h"
#include "Transaction/ManagerFeature.h" #include "Transaction/ManagerFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/SmartContext.h" #include "Transaction/SmartContext.h"
#include "Transaction/StandaloneContext.h" #include "Transaction/StandaloneContext.h"
#include "Utils/SingleCollectionTransaction.h" #include "Utils/SingleCollectionTransaction.h"
@ -51,6 +52,15 @@ using namespace arangodb;
using namespace arangodb::basics; using namespace arangodb::basics;
using namespace arangodb::rest; using namespace arangodb::rest;
namespace {
class SimpleTransaction : public transaction::Methods {
public:
SimpleTransaction(std::shared_ptr<transaction::Context>&& transactionContext,
transaction::Options&& options = transaction::Options())
: Methods(std::move(transactionContext), std::move(options)) {}
};
} // namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief agency public path /// @brief agency public path
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -543,7 +553,7 @@ void RestVocbaseBaseHandler::extractStringParameter(std::string const& name,
} }
} }
std::unique_ptr<SingleCollectionTransaction> RestVocbaseBaseHandler::createTransaction( std::unique_ptr<transaction::Methods> RestVocbaseBaseHandler::createTransaction(
std::string const& collectionName, 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);
@ -581,7 +591,7 @@ 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_MESSAGE(TRI_ERROR_TRANSACTION_NOT_FOUND, std::string("transaction '") + std::to_string(tid) + "' not found"); THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TRANSACTION_NOT_FOUND, std::string("transaction '") + std::to_string(tid) + "' not found");
} }
return std::make_unique<SingleCollectionTransaction>(ctx, collectionName, type); return std::make_unique<SimpleTransaction>(std::move(ctx));
} else { } else {
auto ctx = transaction::StandaloneContext::Create(_vocbase); auto ctx = transaction::StandaloneContext::Create(_vocbase);
return std::make_unique<SingleCollectionTransaction>(ctx, collectionName, type); return std::make_unique<SingleCollectionTransaction>(ctx, collectionName, type);

View File

@ -35,8 +35,10 @@
struct TRI_vocbase_t; struct TRI_vocbase_t;
namespace arangodb { namespace arangodb {
namespace transaction {
class Methods;
}
class SingleCollectionTransaction;
class VocbaseContext; class VocbaseContext;
/// @brief abstract base request handler /// @brief abstract base request handler
@ -228,7 +230,7 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
* @return A freshly created transaction for the given collection with proper * @return A freshly created transaction for the given collection with proper
* locking or a leased transaction. * locking or a leased transaction.
*/ */
std::unique_ptr<SingleCollectionTransaction> createTransaction(std::string const& cname, std::unique_ptr<transaction::Methods> createTransaction(std::string const& cname,
AccessMode::Type mode) const; AccessMode::Type mode) const;
/// @brief create proper transaction context, including the proper IDs /// @brief create proper transaction context, including the proper IDs

View File

@ -84,6 +84,24 @@ TransactionCollection* TransactionState::collection(TRI_voc_cid_t cid,
return trxCollection; return trxCollection;
} }
/// @brief return the collection from a transaction
TransactionCollection* TransactionState::collection(std::string const& name,
AccessMode::Type accessType) const {
TRI_ASSERT(_status == transaction::Status::CREATED ||
_status == transaction::Status::RUNNING);
auto it = std::find_if(_collections.begin(), _collections.end(), [&name](TransactionCollection const* trxColl) {
return trxColl->collectionName() == name;
});
if (it == _collections.end() || !(*it)->canAccess(accessType)) {
// not found or not accessible in the requested mode
return nullptr;
}
return (*it);
}
TransactionState::Cookie* TransactionState::cookie(void const* key) noexcept { TransactionState::Cookie* TransactionState::cookie(void const* key) noexcept {
auto itr = _cookies.find(key); auto itr = _cookies.find(key);

View File

@ -138,6 +138,10 @@ class TransactionState {
TransactionCollection* collection(TRI_voc_cid_t cid, TransactionCollection* collection(TRI_voc_cid_t cid,
AccessMode::Type accessType) const; AccessMode::Type accessType) const;
/// @brief return the collection from a transaction
TransactionCollection* collection(std::string const& name,
AccessMode::Type accessType) const;
/// @brief add a collection to a transaction /// @brief add a collection to a transaction
Result addCollection(TRI_voc_cid_t cid, std::string const& cname, Result addCollection(TRI_voc_cid_t cid, std::string const& cname,
AccessMode::Type accessType, int nestingLevel, bool force); AccessMode::Type accessType, int nestingLevel, bool force);

View File

@ -199,10 +199,10 @@ uint64_t Manager::getActiveTransactionCount() {
Manager::ManagedTrx::ManagedTrx(MetaType t, TransactionState* st) Manager::ManagedTrx::ManagedTrx(MetaType t, TransactionState* st)
: type(t), : type(t),
finalStatus(Status::UNDEFINED),
usedTimeSecs(TRI_microtime()), usedTimeSecs(TRI_microtime()),
state(st), state(st),
user(::currentUser()), user(::currentUser()),
finalStatus(Status::UNDEFINED),
rwlock() {} rwlock() {}
bool Manager::ManagedTrx::expired() const { bool Manager::ManagedTrx::expired() const {
@ -714,6 +714,8 @@ Result Manager::updateTransaction(TRI_voc_tid_t tid, transaction::Status status,
"transaction was not running"); "transaction was not running");
} }
bool const isCoordinator = state->isCoordinator();
auto ctx = std::make_shared<ManagedContext>(tid, state.get(), AccessMode::Type::NONE); auto ctx = std::make_shared<ManagedContext>(tid, state.get(), AccessMode::Type::NONE);
state.release(); // now owned by ctx state.release(); // now owned by ctx
@ -723,7 +725,7 @@ Result Manager::updateTransaction(TRI_voc_tid_t tid, transaction::Status status,
TRI_ASSERT(trx.state()->nestingLevel() == 1); TRI_ASSERT(trx.state()->nestingLevel() == 1);
trx.state()->decreaseNesting(); trx.state()->decreaseNesting();
TRI_ASSERT(trx.state()->isTopLevelTransaction()); TRI_ASSERT(trx.state()->isTopLevelTransaction());
if (clearServers) { if (clearServers && !isCoordinator) {
trx.state()->clearKnownServers(); trx.state()->clearKnownServers();
} }
if (status == transaction::Status::COMMITTED) { if (status == transaction::Status::COMMITTED) {

View File

@ -75,13 +75,13 @@ class Manager final {
public: public:
MetaType type; /// managed, AQL or tombstone MetaType type; /// managed, AQL or tombstone
double usedTimeSecs; /// last time used
TransactionState* state; /// Transaction, may be nullptr
std::string user; /// user owning the transaction
/// @brief final TRX state that is valid if this is a tombstone /// @brief final TRX state that is valid if this is a tombstone
/// necessary to avoid getting error on a 'diamond' commit or accidantally /// necessary to avoid getting error on a 'diamond' commit or accidantally
/// repeated commit / abort messages /// repeated commit / abort messages
transaction::Status finalStatus; transaction::Status finalStatus;
double usedTimeSecs; /// last time used
TransactionState* state; /// Transaction, may be nullptr
std::string user; /// user owning the transaction
/// cheap usage lock for *state /// cheap usage lock for *state
mutable basics::ReadWriteSpinLock rwlock; mutable basics::ReadWriteSpinLock rwlock;
}; };

View File

@ -826,6 +826,15 @@ TransactionCollection* transaction::Methods::trxCollection(TRI_voc_cid_t cid,
return _state->collection(cid, type); return _state->collection(cid, type);
} }
/// @brief return the transaction collection for a document collection
TransactionCollection* transaction::Methods::trxCollection(std::string const& name,
AccessMode::Type type) const {
TRI_ASSERT(_state != nullptr);
TRI_ASSERT(_state->status() == transaction::Status::RUNNING ||
_state->status() == transaction::Status::CREATED);
return _state->collection(name, type);
}
/// @brief order a ditch for a collection /// @brief order a ditch for a collection
void transaction::Methods::pinData(TRI_voc_cid_t cid) { void transaction::Methods::pinData(TRI_voc_cid_t cid) {
TRI_ASSERT(_state != nullptr); TRI_ASSERT(_state != nullptr);
@ -1104,16 +1113,24 @@ TRI_voc_cid_t transaction::Methods::addCollectionAtRuntime(TRI_voc_cid_t cid,
res = applyDataSourceRegistrationCallbacks(*dataSource, *this); res = applyDataSourceRegistrationCallbacks(*dataSource, *this);
if (res.ok()) {
res = _state->ensureCollections(_state->nestingLevel());
}
if (res.fail()) { if (res.fail()) {
THROW_ARANGO_EXCEPTION(res); THROW_ARANGO_EXCEPTION(res);
} }
_state->ensureCollections(_state->nestingLevel());
collection = trxCollection(cid); collection = trxCollection(cid);
if (collection == nullptr) { if (collection == nullptr) {
throwCollectionNotFound(cname.c_str()); throwCollectionNotFound(cname.c_str());
} }
} else {
AccessMode::Type collectionAccessType = collection->accessType();
if (AccessMode::isRead(collectionAccessType) && !AccessMode::isRead(type)) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION,
std::string(TRI_errno_string(TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION)) + ": " + cname +
" [" + AccessMode::typeString(type) + "]");
}
} }
TRI_ASSERT(collection != nullptr); TRI_ASSERT(collection != nullptr);
@ -1121,7 +1138,8 @@ TRI_voc_cid_t transaction::Methods::addCollectionAtRuntime(TRI_voc_cid_t cid,
} }
/// @brief add a collection to the transaction for read, at runtime /// @brief add a collection to the transaction for read, at runtime
TRI_voc_cid_t transaction::Methods::addCollectionAtRuntime(std::string const& collectionName) { TRI_voc_cid_t transaction::Methods::addCollectionAtRuntime(std::string const& collectionName,
AccessMode::Type type) {
if (collectionName == _collectionCache.name && !collectionName.empty()) { if (collectionName == _collectionCache.name && !collectionName.empty()) {
return _collectionCache.cid; return _collectionCache.cid;
} }
@ -1131,7 +1149,7 @@ TRI_voc_cid_t transaction::Methods::addCollectionAtRuntime(std::string const& co
if (cid == 0) { if (cid == 0) {
throwCollectionNotFound(collectionName.c_str()); throwCollectionNotFound(collectionName.c_str());
} }
addCollectionAtRuntime(cid, collectionName); addCollectionAtRuntime(cid, collectionName, type);
_collectionCache.cid = cid; _collectionCache.cid = cid;
_collectionCache.name = collectionName; _collectionCache.name = collectionName;
return cid; return cid;
@ -1164,8 +1182,9 @@ void transaction::Methods::invokeOnAllElements(std::string const& collectionName
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
} }
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
TransactionCollection* trxCol = trxCollection(cid, AccessMode::Type::READ); TransactionCollection* trxCol = trxCollection(cid, AccessMode::Type::READ);
TRI_ASSERT(trxCol != nullptr);
LogicalCollection* collection = documentCollection(trxCol); LogicalCollection* collection = documentCollection(trxCol);
TRI_ASSERT(collection != nullptr); TRI_ASSERT(collection != nullptr);
_transactionContextPtr->pinData(collection); _transactionContextPtr->pinData(collection);
@ -1218,7 +1237,7 @@ Result transaction::Methods::documentFastPath(std::string const& collectionName,
return Result(); return Result();
} }
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
pinData(cid); // will throw when it fails pinData(cid); // will throw when it fails
@ -1264,8 +1283,9 @@ Result transaction::Methods::documentFastPathLocal(std::string const& collection
TRI_ASSERT(!ServerState::instance()->isCoordinator()); TRI_ASSERT(!ServerState::instance()->isCoordinator());
TRI_ASSERT(_state->status() == transaction::Status::RUNNING); TRI_ASSERT(_state->status() == transaction::Status::RUNNING);
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
TransactionCollection* trxColl = trxCollection(cid); TransactionCollection* trxColl = trxCollection(cid);
TRI_ASSERT(trxColl != nullptr);
LogicalCollection* collection = documentCollection(trxColl); LogicalCollection* collection = documentCollection(trxColl);
TRI_ASSERT(collection != nullptr); TRI_ASSERT(collection != nullptr);
_transactionContextPtr->pinData(collection); // will throw when it fails _transactionContextPtr->pinData(collection); // will throw when it fails
@ -1491,7 +1511,7 @@ OperationResult transaction::Methods::documentCoordinator(std::string const& col
OperationResult transaction::Methods::documentLocal(std::string const& collectionName, OperationResult transaction::Methods::documentLocal(std::string const& collectionName,
VPackSlice const value, VPackSlice const value,
OperationOptions& options) { OperationOptions& options) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
if (!options.silent) { if (!options.silent) {
@ -1647,7 +1667,7 @@ static double chooseTimeout(size_t count, size_t totalBytes) {
OperationResult transaction::Methods::insertLocal(std::string const& collectionName, OperationResult transaction::Methods::insertLocal(std::string const& collectionName,
VPackSlice const value, VPackSlice const value,
OperationOptions& options) { OperationOptions& options) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::WRITE);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE); bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE);
@ -1995,7 +2015,7 @@ OperationResult transaction::Methods::modifyLocal(std::string const& collectionN
VPackSlice const newValue, VPackSlice const newValue,
OperationOptions& options, OperationOptions& options,
TRI_voc_document_operation_e operation) { TRI_voc_document_operation_e operation) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::WRITE);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE); bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE);
@ -2263,7 +2283,7 @@ OperationResult transaction::Methods::removeCoordinator(std::string const& colle
OperationResult transaction::Methods::removeLocal(std::string const& collectionName, OperationResult transaction::Methods::removeLocal(std::string const& collectionName,
VPackSlice const value, VPackSlice const value,
OperationOptions& options) { OperationOptions& options) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::WRITE);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE); bool const needsLock = !isLocked(collection, AccessMode::Type::WRITE);
@ -2487,7 +2507,7 @@ OperationResult transaction::Methods::allCoordinator(std::string const& collecti
OperationResult transaction::Methods::allLocal(std::string const& collectionName, OperationResult transaction::Methods::allLocal(std::string const& collectionName,
uint64_t skip, uint64_t limit, uint64_t skip, uint64_t limit,
OperationOptions& options) { OperationOptions& options) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
pinData(cid); // will throw when it fails pinData(cid); // will throw when it fails
@ -2550,7 +2570,7 @@ OperationResult transaction::Methods::truncateCoordinator(std::string const& col
/// @brief remove all documents in a collection, local /// @brief remove all documents in a collection, local
OperationResult transaction::Methods::truncateLocal(std::string const& collectionName, OperationResult transaction::Methods::truncateLocal(std::string const& collectionName,
OperationOptions& options) { OperationOptions& options) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::WRITE);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
@ -2604,7 +2624,6 @@ OperationResult transaction::Methods::truncateLocal(std::string const& collectio
TRI_ASSERT(isLocked(collection, AccessMode::Type::WRITE)); TRI_ASSERT(isLocked(collection, AccessMode::Type::WRITE));
auto res = collection->truncate(*this, options); auto res = collection->truncate(*this, options);
;
if (res.fail()) { if (res.fail()) {
if (lockResult.is(TRI_ERROR_LOCKED)) { if (lockResult.is(TRI_ERROR_LOCKED)) {
@ -2765,7 +2784,7 @@ OperationResult transaction::Methods::countCoordinatorHelper(
/// @brief count the number of documents in a collection /// @brief count the number of documents in a collection
OperationResult transaction::Methods::countLocal(std::string const& collectionName, OperationResult transaction::Methods::countLocal(std::string const& collectionName,
transaction::CountType type) { transaction::CountType type) {
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
LogicalCollection* collection = documentCollection(trxCollection(cid)); LogicalCollection* collection = documentCollection(trxCollection(cid));
Result lockResult = lockRecursive(cid, AccessMode::Type::READ); Result lockResult = lockRecursive(cid, AccessMode::Type::READ);
@ -3006,7 +3025,7 @@ std::unique_ptr<IndexIterator> transaction::Methods::indexScan(std::string const
THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_ONLY_ON_DBSERVER); THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_ONLY_ON_DBSERVER);
} }
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
TransactionCollection* trxColl = trxCollection(cid); TransactionCollection* trxColl = trxCollection(cid);
if (trxColl == nullptr) { if (trxColl == nullptr) {
THROW_ARANGO_EXCEPTION_MESSAGE( THROW_ARANGO_EXCEPTION_MESSAGE(
@ -3062,6 +3081,22 @@ arangodb::LogicalCollection* transaction::Methods::documentCollection(TRI_voc_ci
return trxColl->collection().get(); return trxColl->collection().get();
} }
/// @brief return the collection
arangodb::LogicalCollection* transaction::Methods::documentCollection(std::string const& name) const {
TRI_ASSERT(_state != nullptr);
TRI_ASSERT(_state->status() == transaction::Status::RUNNING);
auto trxColl = trxCollection(name, AccessMode::Type::READ);
if (trxColl == nullptr) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"could not find collection");
}
TRI_ASSERT(trxColl != nullptr);
TRI_ASSERT(trxColl->collection() != nullptr);
return trxColl->collection().get();
}
/// @brief add a collection by id, with the name supplied /// @brief add a collection by id, with the name supplied
Result transaction::Methods::addCollection(TRI_voc_cid_t cid, std::string const& cname, Result transaction::Methods::addCollection(TRI_voc_cid_t cid, std::string const& cname,
AccessMode::Type type) { AccessMode::Type type) {
@ -3183,7 +3218,7 @@ std::vector<std::shared_ptr<Index>> transaction::Methods::indexesForCollection(
} }
// For a DBserver we use the local case. // For a DBserver we use the local case.
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
LogicalCollection* document = documentCollection(trxCollection(cid)); LogicalCollection* document = documentCollection(trxCollection(cid));
std::vector<std::shared_ptr<Index>> indexes = document->getIndexes(); std::vector<std::shared_ptr<Index>> indexes = document->getIndexes();
if (!withHidden) { if (!withHidden) {
@ -3253,7 +3288,7 @@ transaction::Methods::IndexHandle transaction::Methods::getIndexByIdentifier(
return IndexHandle(idx); return IndexHandle(idx);
} }
TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName); TRI_voc_cid_t cid = addCollectionAtRuntime(collectionName, AccessMode::Type::READ);
LogicalCollection* document = documentCollection(trxCollection(cid)); LogicalCollection* document = documentCollection(trxCollection(cid));
if (indexHandle.empty()) { if (indexHandle.empty()) {

View File

@ -248,10 +248,11 @@ class Methods {
/// @brief add a collection to the transaction for read, at runtime /// @brief add a collection to the transaction for read, at runtime
ENTERPRISE_VIRT TRI_voc_cid_t ENTERPRISE_VIRT TRI_voc_cid_t
addCollectionAtRuntime(TRI_voc_cid_t cid, std::string const& collectionName, addCollectionAtRuntime(TRI_voc_cid_t cid, std::string const& collectionName,
AccessMode::Type type = AccessMode::Type::READ); AccessMode::Type type);
/// @brief add a collection to the transaction for read, at runtime /// @brief add a collection to the transaction for read, at runtime
virtual TRI_voc_cid_t addCollectionAtRuntime(std::string const& collectionName); virtual TRI_voc_cid_t addCollectionAtRuntime(std::string const& collectionName,
AccessMode::Type type);
/// @brief return the type of a collection /// @brief return the type of a collection
bool isEdgeCollection(std::string const& collectionName) const; bool isEdgeCollection(std::string const& collectionName) const;
@ -379,7 +380,10 @@ class Methods {
ENTERPRISE_VIRT bool isLocked(arangodb::LogicalCollection*, AccessMode::Type) const; ENTERPRISE_VIRT bool isLocked(arangodb::LogicalCollection*, AccessMode::Type) const;
/// @brief fetch the LogicalCollection by CID /// @brief fetch the LogicalCollection by CID
arangodb::LogicalCollection* documentCollection(TRI_voc_cid_t) const; arangodb::LogicalCollection* documentCollection(TRI_voc_cid_t cid) const;
/// @brief fetch the LogicalCollection by name
arangodb::LogicalCollection* documentCollection(std::string const& name) const;
/// @brief get the index by its identifier. Will either throw or /// @brief get the index by its identifier. Will either throw or
/// return a valid index. nullptr is impossible. /// return a valid index. nullptr is impossible.
@ -479,6 +483,9 @@ class Methods {
ENTERPRISE_VIRT TransactionCollection* trxCollection( ENTERPRISE_VIRT TransactionCollection* trxCollection(
TRI_voc_cid_t cid, AccessMode::Type type = AccessMode::Type::READ) const; TRI_voc_cid_t cid, AccessMode::Type type = AccessMode::Type::READ) const;
TransactionCollection* trxCollection(
std::string const& name, AccessMode::Type type = AccessMode::Type::READ) const;
OperationResult countCoordinator(std::string const& collectionName, CountType type); OperationResult countCoordinator(std::string const& collectionName, CountType type);
OperationResult countCoordinatorHelper(std::shared_ptr<LogicalCollection> const& collinfo, OperationResult countCoordinatorHelper(std::shared_ptr<LogicalCollection> const& collinfo,

View File

@ -22,6 +22,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "SingleCollectionTransaction.h" #include "SingleCollectionTransaction.h"
#include "Basics/StringUtils.h"
#include "StorageEngine/TransactionCollection.h" #include "StorageEngine/TransactionCollection.h"
#include "StorageEngine/TransactionState.h" #include "StorageEngine/TransactionState.h"
#include "Transaction/Context.h" #include "Transaction/Context.h"
@ -59,7 +60,7 @@ SingleCollectionTransaction::SingleCollectionTransaction(
_accessType(accessType) { _accessType(accessType) {
// add the (sole) collection // add the (sole) collection
_cid = resolver()->getCollectionId(name); _cid = resolver()->getCollectionId(name);
Result res = addCollection(_cid, name.c_str(), _accessType); Result res = addCollection(_cid, name, _accessType);
if (res.fail()) { if (res.fail()) {
THROW_ARANGO_EXCEPTION(res); THROW_ARANGO_EXCEPTION(res);
} }
@ -95,6 +96,19 @@ LogicalCollection* SingleCollectionTransaction::documentCollection() {
return _documentCollection; return _documentCollection;
} }
TRI_voc_cid_t SingleCollectionTransaction::addCollectionAtRuntime(std::string const& name,
AccessMode::Type type) {
// sanity check
TRI_ASSERT(!name.empty());
if ((name[0] < '0' || name[0] > '9') &&
name != resolveTrxCollection()->collectionName()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION,
std::string(TRI_errno_string(TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION)) + ": " + name);
}
return _cid;
}
/// @brief get the underlying collection's name /// @brief get the underlying collection's name
std::string SingleCollectionTransaction::name() { std::string SingleCollectionTransaction::name() {
// will ensure we have the _trxCollection object set // will ensure we have the _trxCollection object set

View File

@ -25,6 +25,7 @@
#define ARANGOD_UTILS_SINGLE_COLLECTION_TRANSACTION_H 1 #define ARANGOD_UTILS_SINGLE_COLLECTION_TRANSACTION_H 1
#include "Basics/Common.h" #include "Basics/Common.h"
#include "StorageEngine/TransactionCollection.h"
#include "Transaction/Methods.h" #include "Transaction/Methods.h"
#include "VocBase/AccessMode.h" #include "VocBase/AccessMode.h"
#include "VocBase/voc-types.h" #include "VocBase/voc-types.h"
@ -63,9 +64,7 @@ class SingleCollectionTransaction final : public transaction::Methods {
#endif #endif
/// @brief add a collection to the transaction for read, at runtime /// @brief add a collection to the transaction for read, at runtime
/// note that this can only be ourselves /// note that this can only be ourselves
TRI_voc_cid_t addCollectionAtRuntime(std::string const&) override final { TRI_voc_cid_t addCollectionAtRuntime(std::string const& name, AccessMode::Type type) override final;
return _cid;
}
/// @brief get the underlying collection's name /// @brief get the underlying collection's name
std::string name(); std::string name();

View File

@ -167,7 +167,7 @@ function selfHeal () {
const serviceCollection = utils.getStorage(); const serviceCollection = utils.getStorage();
const bundleCollection = utils.getBundleStorage(); const bundleCollection = utils.getBundleStorage();
const serviceDefinitions = db._query(aql` const serviceDefinitions = db._query(aql`
FOR doc IN ${serviceCollection} WITH ${bundleCollection} FOR doc IN ${serviceCollection}
FILTER LEFT(doc.mount, 2) != "/_" FILTER LEFT(doc.mount, 2) != "/_"
LET bundleExists = DOCUMENT(${bundleCollection}, doc.checksum) != null LET bundleExists = DOCUMENT(${bundleCollection}, doc.checksum) != null
RETURN [doc.mount, doc.checksum, doc._rev, bundleExists] RETURN [doc.mount, doc.checksum, doc._rev, bundleExists]

View File

@ -3268,7 +3268,7 @@ TEST_F(IResearchAnalyzerFeatureTest, test_upgrade_static_legacy) {
LEGACY_ANALYZER_COLLECTION_NAME, arangodb::AccessMode::Type::WRITE); LEGACY_ANALYZER_COLLECTION_NAME, arangodb::AccessMode::Type::WRITE);
EXPECT_TRUE((true == trx.begin().ok())); EXPECT_TRUE((true == trx.begin().ok()));
EXPECT_TRUE( EXPECT_TRUE(
(true == trx.insert(arangodb::tests::AnalyzerCollectionName, (true == trx.insert(LEGACY_ANALYZER_COLLECTION_NAME,
VPackParser::fromJson("{\"name\": \"legacy\"}")->slice(), options) VPackParser::fromJson("{\"name\": \"legacy\"}")->slice(), options)
.ok())); .ok()));
EXPECT_TRUE((trx.commit().ok())); EXPECT_TRUE((trx.commit().ok()));

View File

@ -136,7 +136,7 @@ function JSTransactionGraphSuite () {
db[vertex].insert({ _key: "test", value: "test" }); db[vertex].insert({ _key: "test", value: "test" });
db._executeTransaction({ db._executeTransaction({
collections: { write: vertex }, collections: { write: [vertex, edge] },
action: function(params) { action: function(params) {
let graph = require("@arangodb/general-graph")._graph(params.graph); let graph = require("@arangodb/general-graph")._graph(params.graph);
let ERRORS = require("@arangodb").errors; let ERRORS = require("@arangodb").errors;

View File

@ -1336,8 +1336,6 @@ function transactionCollectionsSuite () {
} }
assertEqual(10, tc1.count()); assertEqual(10, tc1.count());
assertEqual(10, tc2.count());
} catch(err) { } catch(err) {
fail(); fail();
} finally { } finally {