1
0
Fork 0

issue 381.6: enforce use of VIEW keyword for bound views and disallow for bound collections (#5522)

This commit is contained in:
Vasiliy 2018-06-05 14:45:53 +03:00 committed by Andrey Abramov
parent 5aa46ee0cd
commit 3e983adb2d
15 changed files with 1382 additions and 1363 deletions

View File

@ -731,6 +731,28 @@ AstNode* Ast::createNodeParameter(
return node; return node;
} }
AstNode* Ast::createNodeParameterCollection(char const* name, size_t length) {
auto node = createNodeParameter(name, length);
if (node) {
node->reserve(1);
node->addMember(createNode(NODE_TYPE_COLLECTION));
}
return node;
}
AstNode* Ast::createNodeParameterView(char const* name, size_t length) {
auto node = createNodeParameter(name, length);
if (node) {
node->reserve(1);
node->addMember(createNode(NODE_TYPE_VIEW));
}
return node;
}
/// @brief create an AST quantifier node /// @brief create an AST quantifier node
AstNode* Ast::createNodeQuantifier(int64_t type) { AstNode* Ast::createNodeQuantifier(int64_t type) {
AstNode* node = createNode(NODE_TYPE_QUANTIFIER); AstNode* node = createNode(NODE_TYPE_QUANTIFIER);
@ -1470,39 +1492,42 @@ void Ast::injectBindParameters(BindParameters& parameters) {
// bound data source parameter // bound data source parameter
TRI_ASSERT(value.isString()); TRI_ASSERT(value.isString());
char const* name = nullptr; // should have arrived here via createNodeParameterCollection(...) or createNodeParameterView(...)
VPackValueLength length; if (1 != node->numMembers() || !node->getMemberUnchecked(0)) {
char const* stringValue = value.getString(length); THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, "missing data source category"
);
}
// FIXME use external resolver // FIXME use external resolver
arangodb::CollectionNameResolver resolver(_query->vocbase()); arangodb::CollectionNameResolver resolver(_query->vocbase());
std::shared_ptr<LogicalDataSource> dataSource;
if (length > 0 && stringValue[0] >= '0' && stringValue[0] <= '9') { switch (node->getMemberUnchecked(0)->type) {
dataSource = resolver.getDataSource(basics::StringUtils::uint64(stringValue, length)); case NODE_TYPE_COLLECTION: {
} else { auto dataSource = resolver.getCollection(value.copyString());
dataSource = resolver.getDataSource(std::string(stringValue, length));
}
if (!dataSource) { if (!dataSource) {
THROW_ARANGO_EXCEPTION_FORMAT( THROW_ARANGO_EXCEPTION_FORMAT(
TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND, "%s", TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND,
"collection: %s",
value.copyString().c_str() value.copyString().c_str()
); );
} }
// TODO: can we get away without registering the string value here? // TODO: can we get away without registering the string value here?
auto& dataSourceName = dataSource->name(); auto* name = _query->registerString(dataSource->name());
name = _query->registerString(dataSourceName.c_str(), dataSourceName.size());
if (LogicalCollection::category() == dataSource->category()) {
// check if the collection was used in a data-modification query // check if the collection was used in a data-modification query
bool isWriteCollection = false; bool isWriteCollection = false;
arangodb::StringRef paramRef(param);
for (auto const& it : _writeCollections) { for (auto const& it : _writeCollections) {
auto const& c = it.first; auto const& c = it.first;
if (c->type == NODE_TYPE_PARAMETER && StringRef(param) == StringRef(c->getStringValue(), c->getStringLength())) {
if (c->type == NODE_TYPE_PARAMETER
&& paramRef == StringRef(c->getStringValue(), c->getStringLength())) {
isWriteCollection = true; isWriteCollection = true;
break; break;
} }
@ -1513,23 +1538,39 @@ void Ast::injectBindParameters(BindParameters& parameters) {
: AccessMode::Type::READ); : AccessMode::Type::READ);
if (isWriteCollection) { if (isWriteCollection) {
// must update AST info now for all nodes that contained this // must update AST info now for all nodes that contained this parameter
// parameter
for (size_t i = 0; i < _writeCollections.size(); ++i) { for (size_t i = 0; i < _writeCollections.size(); ++i) {
auto& c = _writeCollections[i].first; auto& c = _writeCollections[i].first;
if (c->type == NODE_TYPE_PARAMETER &&
StringRef(param) == StringRef(c->getStringValue(), c->getStringLength())) { if (c->type == NODE_TYPE_PARAMETER
&& paramRef == StringRef(c->getStringValue(), c->getStringLength())) {
c = node; c = node;
// no break here. replace all occurrences // no break here. replace all occurrences
} }
} }
} }
} else if (LogicalView::category() == dataSource->category()) {
node = createNodeView(name); break;
} else { }
case NODE_TYPE_VIEW: {
auto dataSource = resolver.getView(value.copyString());
if (!dataSource) {
THROW_ARANGO_EXCEPTION_FORMAT(
TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND,
"view: %s",
value.copyString().c_str()
);
}
// TODO: can we get away without registering the string value here?
node = createNodeView(_query->registerString(dataSource->name()));
break;
}
default:
THROW_ARANGO_EXCEPTION_MESSAGE( THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, TRI_ERROR_INTERNAL, "unexpected data source category"
"unexpected data source category"
); );
} }
} else { } else {

View File

@ -244,6 +244,8 @@ class Ast {
char const* name, char const* name,
size_t length size_t length
); );
AstNode* createNodeParameterCollection(char const* name, size_t length);
AstNode* createNodeParameterView(char const* name, size_t length);
/// @brief create an AST quantifier node /// @brief create an AST quantifier node
AstNode* createNodeQuantifier(int64_t); AstNode* createNodeQuantifier(int64_t);
@ -588,6 +590,7 @@ public:
/// @brief a singleton empty string node instance /// @brief a singleton empty string node instance
static AstNode const EmptyStringNode; static AstNode const EmptyStringNode;
}; };
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -126,7 +126,7 @@ extern int Aqldebug;
union YYSTYPE union YYSTYPE
{ {
#line 21 "Aql/grammar.y" /* yacc.c:1909 */ #line 19 "Aql/grammar.y" /* yacc.c:1909 */
arangodb::aql::AstNode* node; arangodb::aql::AstNode* node;
struct { struct {

View File

@ -126,7 +126,7 @@ extern int Aqldebug;
union YYSTYPE union YYSTYPE
{ {
#line 21 "Aql/grammar.y" /* yacc.c:1909 */ #line 19 "Aql/grammar.y" /* yacc.c:1909 */
arangodb::aql::AstNode* node; arangodb::aql::AstNode* node;
struct { struct {

View File

@ -326,7 +326,6 @@ static AstNode const* GetIntoExpression(AstNode const* node) {
%type <node> simple_value; %type <node> simple_value;
%type <node> value_literal; %type <node> value_literal;
%type <node> collection_name; %type <node> collection_name;
%type <node> view_name;
%type <node> in_or_into_collection; %type <node> in_or_into_collection;
%type <node> bind_parameter; %type <node> bind_parameter;
%type <strval> variable_name; %type <strval> variable_name;
@ -447,13 +446,7 @@ statement_block_statement:
; ;
for_statement: for_statement:
T_FOR variable_name T_IN T_VIEW view_name { T_FOR variable_name T_IN expression {
parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR);
auto node = parser->ast()->createNodeFor($2.value, $2.length, $5, true);
parser->ast()->addOperation(node);
}
| T_FOR variable_name T_IN expression {
parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR); parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR);
auto node = parser->ast()->createNodeFor($2.value, $2.length, $4, true); auto node = parser->ast()->createNodeFor($2.value, $2.length, $4, true);
@ -1471,7 +1464,15 @@ graph_direction_steps:
; ;
reference: reference:
T_STRING { T_VIEW T_STRING {
auto* ast = parser->ast();
auto* node = ast->createNodeView($2.value);
TRI_ASSERT(node != nullptr);
$$ = node;
}
| T_STRING {
// variable or collection // variable or collection
auto ast = parser->ast(); auto ast = parser->ast();
AstNode* node = nullptr; AstNode* node = nullptr;
@ -1680,29 +1681,20 @@ collection_name:
} }
; ;
view_name: bind_parameter:
T_STRING { T_VIEW T_DATA_SOURCE_PARAMETER {
$$ = parser->ast()->createNodeView($1.value); if ($2.length < 2 || $2.value[0] != '@') {
parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), $2.value, yylloc.first_line, yylloc.first_column);
} }
| T_QUOTED_STRING {
$$ = parser->ast()->createNodeView($1.value); $$ = parser->ast()->createNodeParameterView($2.value, $2.length);
} }
| T_DATA_SOURCE_PARAMETER { | T_DATA_SOURCE_PARAMETER {
if ($1.length < 2 || $1.value[0] != '@') { if ($1.length < 2 || $1.value[0] != '@') {
parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), $1.value, yylloc.first_line, yylloc.first_column); parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), $1.value, yylloc.first_line, yylloc.first_column);
} }
$$ = parser->ast()->createNodeParameter($1.value, $1.length); $$ = parser->ast()->createNodeParameterCollection($1.value, $1.length);
}
;
bind_parameter:
T_DATA_SOURCE_PARAMETER {
if ($1.length < 2 || $1.value[0] != '@') {
parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), $1.value, yylloc.first_line, yylloc.first_column);
}
$$ = parser->ast()->createNodeParameter($1.value, $1.length);
} }
| T_PARAMETER { | T_PARAMETER {
$$ = parser->ast()->createNodeParameter($1.value, $1.length); $$ = parser->ast()->createNodeParameter($1.value, $1.length);

View File

@ -231,7 +231,7 @@ bool IResearchLink::init(arangodb::velocypack::Slice const& definition) {
if (view) { if (view) {
wiew = logicalView; // remeber the DBServer view instance wiew = logicalView; // remeber the DBServer view instance
logicalView = view->ensure(id()); // repoint LogicalView at the per-cid instance logicalView = view->ensure(_collection->id()); // repoint LogicalView at the per-cid instance
} else { } else {
logicalView = nullptr; logicalView = nullptr;
} }

View File

@ -405,18 +405,15 @@ std::shared_ptr<arangodb::LogicalView> IResearchViewDBServer::ensure(
return nullptr; return nullptr;
} }
auto id = view->id(); // hold a reference to the original view in the deleter so that the view is still valid for the duration of the pointer wrapper
// hold a reference to the original view in the deleter so that the view is still valid inside the deleter
return std::shared_ptr<arangodb::LogicalView>( return std::shared_ptr<arangodb::LogicalView>(
view.get(), view.get(),
[this, id, cid](arangodb::LogicalView* ptr)->void { [this, view, cid](arangodb::LogicalView*)->void {
static const auto visitor = [](TRI_voc_cid_t)->bool { return false; }; static const auto visitor = [](TRI_voc_cid_t)->bool { return false; };
// same view in vocbase and with no collections // same view in vocbase and with no collections
if (ptr if (view.get() == vocbase().lookupView(view->id()).get() // avoid double dropView(...)
&& ptr == vocbase().lookupView(id).get() // avoid double dropView(...) && view->visitCollections(visitor)) {
&& ptr->visitCollections(visitor)) {
drop(cid); drop(cid);
} }
} }

View File

@ -132,6 +132,10 @@ void RocksDBTransactionCollection::freeOperations(
bool RocksDBTransactionCollection::canAccess( bool RocksDBTransactionCollection::canAccess(
AccessMode::Type accessType) const { AccessMode::Type accessType) const {
if (!_collection) {
return false; // not opened. probably a mistake made by the caller
}
// check if access type matches // check if access type matches
if (AccessMode::isWriteOrExclusive(accessType) && if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) { !AccessMode::isWriteOrExclusive(_accessType)) {

View File

@ -168,6 +168,10 @@ static OperationResult emptyResult(OperationOptions const& options) {
getStateRegistrationCallbacks().emplace_back(callback); getStateRegistrationCallbacks().emplace_back(callback);
} }
/*static*/ void transaction::Methods::clearStateRegistrationCallbacks() {
getStateRegistrationCallbacks().clear();
}
/// @brief Get the field names of the used index /// @brief Get the field names of the used index
std::vector<std::vector<std::string>> std::vector<std::vector<std::string>>
transaction::Methods::IndexHandle::fieldNames() const { transaction::Methods::IndexHandle::fieldNames() const {

View File

@ -154,6 +154,12 @@ class Methods {
/// @note not thread-safe on the assumption of static factory registration /// @note not thread-safe on the assumption of static factory registration
static void addStateRegistrationCallback(StateRegistrationCallback callback); static void addStateRegistrationCallback(StateRegistrationCallback callback);
/// @brief clear all called for state instance association events
/// @note not thread-safe on the assumption of static factory registration
/// @note FOR USE IN TESTS ONLY to reset test state
/// FIXME TODO StateRegistrationCallback logic should be moved into its own feature
static void clearStateRegistrationCallbacks();
/// @brief default batch size for index and other operations /// @brief default batch size for index and other operations
static constexpr uint64_t defaultBatchSize() { return 1000; } static constexpr uint64_t defaultBatchSize() { return 1000; }

View File

@ -439,78 +439,50 @@ TEST_CASE("IResearchQueryTestJoinDuplicateDataSource", "[iresearch][iresearch-qu
view->sync(); view->sync();
} }
// explicit collection and view with the same 'collection' name
{
std::string const query = "LET c=5 FOR x IN collection_1 FILTER x.seq == c FOR d IN VIEW collection_1 FILTER x.seq == d.seq RETURN x";
auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ }");
CHECK((arangodb::tests::assertRules(vocbase, query, {}, boundParameters)));
// arangodb::aql::ExecutionPlan::fromNodeFor(...) throws TRI_ERROR_INTERNAL
auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters);
CHECK((TRI_ERROR_INTERNAL == queryResult.code));
}
// explicit collection and view with the same 'view' name
{
std::string const query = "LET c=5 FOR x IN testView FILTER x.seq == c FOR d IN VIEW testView FILTER x.seq == d.seq RETURN x";
auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ }");
CHECK(arangodb::tests::assertRules(vocbase, query, {}, boundParameters));
// arangodb::transaction::Methods::addCollectionAtRuntime(...) throws TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND
auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters);
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == queryResult.code));
}
// bind collection and view with the same name // bind collection and view with the same name
{ {
std::string const query = "LET c=5 FOR x IN @@dataSource FILTER x.seq == c FOR d IN VIEW @@dataSource FILTER x.seq == d.seq RETURN x"; std::string const query = "LET c=5 FOR x IN @@dataSource FILTER x.seq == c FOR d IN VIEW @@dataSource FILTER x.seq == d.seq RETURN x";
auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ \"@dataSource\" : \"testView\" }"); auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ \"@dataSource\" : \"testView\" }");
/* FIXME will fail CHECK(arangodb::tests::assertRules(vocbase, query, {}, boundParameters));
* on TRI_ASSERT(trxCollection->collection() != nullptr);
* in transaction::Methods::documentCollection(...)
CHECK(arangodb::tests::assertRules(
vocbase, query, {
arangodb::aql::OptimizerRule::handleViewsRule_pass6,
},
boundParameters
));
std::vector<arangodb::velocypack::Slice> expectedDocs {
arangodb::velocypack::Slice(insertedDocsCollectionWithTheSameNameAsView[5].vpack()),
};
auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters); auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters);
REQUIRE(TRI_ERROR_INTERNAL == queryResult.code); CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == queryResult.code));
// FIXME remove line above and uncomment lines below
// will not return any results because of the:
// https://github.com/arangodb/backlog/issues/342
// unable to resolve collection and view with the same name for the time being
//
// REQUIRE(TRI_ERROR_NO_ERROR == queryResult.code);
//
// auto result = queryResult.result->slice();
// CHECK(result.isArray());
//
// arangodb::velocypack::ArrayIterator resultIt(result);
// REQUIRE(expectedDocs.size() == resultIt.size());
//
// // Check documents
// auto expectedDoc = expectedDocs.begin();
// for (;resultIt.valid(); resultIt.next(), ++expectedDoc) {
// auto const actualDoc = resultIt.value();
// auto const resolved = actualDoc.resolveExternals();
//
// CHECK((0 == arangodb::basics::VelocyPackHelper::compare(arangodb::velocypack::Slice(*expectedDoc), resolved, true)));
// }
// CHECK(expectedDoc == expectedDocs.end());
*/
} }
// bind collection and view with the same name // bind collection and view with the same name
//
// FIXME
// will not return any results because of the:
// https://github.com/arangodb/backlog/issues/342
{ {
std::string const query = "LET c=5 FOR x IN @@dataSource FILTER x.seq == c FOR d IN VIEW @@dataSource FILTER x.seq == d.seq RETURN d"; std::string const query = "LET c=5 FOR x IN @@dataSource FILTER x.seq == c FOR d IN VIEW @@dataSource FILTER x.seq == d.seq RETURN d";
auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ \"@dataSource\" : \"testView\" }"); auto const boundParameters = arangodb::velocypack::Parser::fromJson("{ \"@dataSource\" : \"testView\" }");
/* FIXME will fail CHECK(arangodb::tests::assertRules(vocbase, query, {}, boundParameters));
* on TRI_ASSERT(trxCollection->collection() != nullptr);
* in transaction::Methods::documentCollection(...)
CHECK(arangodb::tests::assertRules(
vocbase, query, {
arangodb::aql::OptimizerRule::handleViewsRule_pass6,
},
boundParameters
));
std::vector<arangodb::velocypack::Slice> expectedDocs {
arangodb::velocypack::Slice(insertedDocsCollectionWithTheSameNameAsView[5].vpack()),
};
auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters); auto queryResult = arangodb::tests::executeQuery(vocbase, query, boundParameters);
REQUIRE(TRI_ERROR_INTERNAL == queryResult.code); CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == queryResult.code));
*/
} }
} }

View File

@ -386,6 +386,8 @@ SECTION("test_query") {
REQUIRE((false == !wiewImpl)); REQUIRE((false == !wiewImpl));
auto logicalView = wiewImpl->ensure(42); auto logicalView = wiewImpl->ensure(42);
REQUIRE((false == !logicalView)); REQUIRE((false == !logicalView));
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(logicalView.get());
REQUIRE((false == !viewImpl));
arangodb::CollectionNameResolver resolver(vocbase); arangodb::CollectionNameResolver resolver(vocbase);
auto state = s.engine.createTransactionState(resolver, arangodb::transaction::Options()); auto state = s.engine.createTransactionState(resolver, arangodb::transaction::Options());
@ -891,7 +893,8 @@ SECTION("test_updateProperties") {
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
vocbase.createCollection(collectionJson->slice()); auto* logicalCollection = vocbase.createCollection(collectionJson->slice());
CHECK((nullptr != logicalCollection));
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42); auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
CHECK((false == !wiew)); CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get()); auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
@ -952,7 +955,8 @@ SECTION("test_updateProperties") {
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
vocbase.createCollection(collectionJson->slice()); auto* logicalCollection = vocbase.createCollection(collectionJson->slice());
CHECK((nullptr != logicalCollection));
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42); auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
CHECK((false == !wiew)); CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get()); auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
@ -1013,7 +1017,8 @@ SECTION("test_updateProperties") {
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
vocbase.createCollection(collectionJson->slice()); auto* logicalCollection = vocbase.createCollection(collectionJson->slice());
CHECK((nullptr != logicalCollection));
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42); auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
CHECK((false == !wiew)); CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get()); auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
@ -1073,10 +1078,14 @@ SECTION("test_updateProperties") {
s.agency->responses.clear(); s.agency->responses.clear();
s.agency->responses["POST /_api/agency/read HTTP/1.1\r\n\r\n[[\"/Sync/LatestID\"]]"] = "http/1.0 200\n\n[ { \"\": { \"Sync\": { \"LatestID\" : 1 } } } ]"; s.agency->responses["POST /_api/agency/read HTTP/1.1\r\n\r\n[[\"/Sync/LatestID\"]]"] = "http/1.0 200\n\n[ { \"\": { \"Sync\": { \"LatestID\" : 1 } } } ]";
s.agency->responses["POST /_api/agency/write HTTP/1.1"] = "http/1.0 200\n\n{\"results\": []}"; s.agency->responses["POST /_api/agency/write HTTP/1.1"] = "http/1.0 200\n\n{\"results\": []}";
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto collection0Json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
auto collection1Json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\", \"id\": \"123\" }");
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"properties\": { \"collections\": [ 3, 4, 5 ], \"threadsMaxIdle\": 24, \"threadsMaxTotal\": 42 } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
vocbase.createCollection(collectionJson->slice()); auto* logicalCollection0 = vocbase.createCollection(collection0Json->slice());
CHECK((nullptr != logicalCollection0));
auto* logicalCollection1 = vocbase.createCollection(collection1Json->slice());
CHECK((nullptr != logicalCollection1));
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42); auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
CHECK((false == !wiew)); CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get()); auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
@ -1111,7 +1120,7 @@ SECTION("test_updateProperties") {
wiew->toVelocyPack(builder, true, false); wiew->toVelocyPack(builder, true, false);
builder.close(); builder.close();
auto slice = builder.slice().get("properties"); auto slice = builder.slice().get("properties");
CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 2 == slice.get("collections").length())); CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 1 == slice.get("collections").length()));
CHECK((!slice.hasKey("links"))); CHECK((!slice.hasKey("links")));
CHECK((slice.hasKey("threadsMaxIdle") && slice.get("threadsMaxIdle").isNumber<size_t>() && 5 == slice.get("threadsMaxIdle").getNumber<size_t>())); CHECK((slice.hasKey("threadsMaxIdle") && slice.get("threadsMaxIdle").isNumber<size_t>() && 5 == slice.get("threadsMaxIdle").getNumber<size_t>()));
CHECK((slice.hasKey("threadsMaxTotal") && slice.get("threadsMaxTotal").isNumber<size_t>() && 52 == slice.get("threadsMaxTotal").getNumber<size_t>())); CHECK((slice.hasKey("threadsMaxTotal") && slice.get("threadsMaxTotal").isNumber<size_t>() && 52 == slice.get("threadsMaxTotal").getNumber<size_t>()));

View File

@ -1402,7 +1402,7 @@ TransactionCollectionMock::TransactionCollectionMock(arangodb::TransactionState*
} }
bool TransactionCollectionMock::canAccess(arangodb::AccessMode::Type accessType) const { bool TransactionCollectionMock::canAccess(arangodb::AccessMode::Type accessType) const {
return true; return nullptr != _collection; // collection must have be opened previously
} }
void TransactionCollectionMock::freeOperations(arangodb::transaction::Methods* activeTrx, bool mustRollback) { void TransactionCollectionMock::freeOperations(arangodb::transaction::Methods* activeTrx, bool mustRollback) {
@ -1491,7 +1491,16 @@ arangodb::Result TransactionStateMock::beginTransaction(arangodb::transaction::H
static std::atomic<TRI_voc_tid_t> lastId(0); static std::atomic<TRI_voc_tid_t> lastId(0);
++beginTransactionCount; ++beginTransactionCount;
useCollections(_nestingLevel);
auto res = useCollections(_nestingLevel);
if (!res.ok()) {
updateStatus(arangodb::transaction::Status::ABORTED);
const_cast<TRI_voc_tid_t&>(_id) = 0; // avoid use of TransactionManagerFeature::manager()->unregisterTransaction(...)
return res;
}
const_cast<TRI_voc_tid_t&>(_id) = ++lastId; // ensure each transaction state has a unique ID const_cast<TRI_voc_tid_t&>(_id) = ++lastId; // ensure each transaction state has a unique ID
updateStatus(arangodb::transaction::Status::RUNNING); updateStatus(arangodb::transaction::Status::RUNNING);

View File

@ -47,7 +47,9 @@ NS_END
NS_BEGIN(arangodb) NS_BEGIN(arangodb)
NS_BEGIN(tests) NS_BEGIN(tests)
void init(bool withICU /*= false*/) {} // nothing to do here void init(bool withICU /*= false*/) {
arangodb::transaction::Methods::clearStateRegistrationCallbacks();
}
bool assertRules( bool assertRules(
TRI_vocbase_t& vocbase, TRI_vocbase_t& vocbase,