1
0
Fork 0

issue 496.1: switch scope of responsibility between a TRI_vocbase_t and a LogicalView in respect to view creation/deletion (#7101)

* issue 496.1: switch scope of responsibility between a TRI_vocbase_t and a LogicalView in respect to view creation/deletion

* backport: address test failures

* backport: ensure arangosearch links get exported in the dump

* backport: ensure view is created during restore on the coordinator

* Updates for ArangoSearch DDL tests, IResearchView unregistration and known issues

* Add fix for internal issue 483
This commit is contained in:
Vasiliy 2018-10-30 12:50:35 +03:00 committed by Andrey Abramov
parent 84de3f6052
commit 8f44afb6cf
37 changed files with 2270 additions and 1278 deletions

View File

@ -43,5 +43,4 @@ ArangoSearch
* ArangoSearch ignores `_id` attribute even if `includeAllFields` is set to `true` (internal #445)
* Using score functions (BM25/TFIDF) in ArangoDB expression is not supported (internal #316)
* Using a loop variable in expressions within a corresponding SEARCH condition is not supported (internal #318)
* ArangoSearch doesn't support joins with satellite collections (internal #440)
* RocksDB recovery fails sometimes after renaming a view (internal #469)

View File

@ -580,32 +580,28 @@ void ClusterInfo::loadPlan() {
viewPairSlice.key.copyString();
try {
auto preCommit = [this, viewId, databaseName](std::shared_ptr<LogicalView> const& view)->bool {
auto& views = _newPlannedViews[databaseName];
// register with name as well as with id:
views.reserve(views.size() + 2);
views[viewId] = view;
views[view->name()] = view;
views[view->guid()] = view;
return true;
};
const auto newView = LogicalView::create(
*vocbase,
viewPairSlice.value,
false, // false == coming from Agency
newPlanVersion,
preCommit
LogicalView::ptr view;
auto res = LogicalView::instantiate(
view, *vocbase, viewPairSlice.value, newPlanVersion
);
if (!newView) {
if (!res.ok() || !view) {
LOG_TOPIC(ERR, Logger::AGENCY)
<< "Failed to create view '" << viewId
<< "'. The view will be ignored for now and the invalid information "
"will be repaired. VelocyPack: "
<< viewSlice.toJson();
continue;
}
auto& views = _newPlannedViews[databaseName];
// register with guid/id/name
views.reserve(views.size() + 3);
views[viewId] = view;
views[view->name()] = view;
views[view->guid()] = view;
} catch (std::exception const& ex) {
// The Plan contains invalid view information.
// This should not happen in healthy situations.
@ -619,7 +615,6 @@ void ClusterInfo::loadPlan() {
<< viewSlice.toJson();
TRI_ASSERT(false);
continue;
} catch (...) {
// The Plan contains invalid view information.
// This should not happen in healthy situations.
@ -633,7 +628,6 @@ void ClusterInfo::loadPlan() {
<< viewSlice.toJson();
TRI_ASSERT(false);
continue;
}
}

View File

@ -345,11 +345,11 @@ void registerViewFactory() {
// DB server in custer or single-server
if (arangodb::ServerState::instance()->isCoordinator()) {
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewCoordinator::make);
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewCoordinator::factory());
} else if (arangodb::ServerState::instance()->isDBServer()) {
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewDBServer::make);
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewDBServer::factory());
} else if (arangodb::ServerState::instance()->isSingleServer()) {
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchView::make);
res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchView::factory());
} else {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_FAILED,

View File

@ -147,13 +147,13 @@ int IResearchLink::drop() {
_dropCollectionInDestructor = false; // will do drop now
_defaultGuid = _view->guid(); // remember view ID just in case (e.g. call to toVelocyPack(...) after unload())
TRI_voc_cid_t vid = _view->id();
auto view = _view;
_view = nullptr; // mark as unassociated
_viewLock.unlock(); // release read-lock on the IResearch View
// FIXME TODO this workaround should be in ClusterInfo when moving 'Plan' to 'Current', i.e. IResearchViewDBServer::drop
if (arangodb::ServerState::instance()->isDBServer()) {
return _collection.vocbase().dropView(vid, true).errorNumber(); // cluster-view in ClusterInfo should already not have cid-view
return view->drop().errorNumber(); // cluster-view in ClusterInfo should already not have cid-view
}
return TRI_ERROR_NO_ERROR;
@ -552,4 +552,4 @@ NS_END // arangodb
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -502,6 +502,68 @@ namespace iresearch {
return LINK_TYPE;
}
/*static*/ arangodb::Result IResearchLinkHelper::validateLinks(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& links
) {
if (!links.isObject()) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("while validating arangosearch link definition, error: definition is not an object")
);
}
size_t offset = 0;
arangodb::CollectionNameResolver resolver(vocbase);
for (arangodb::velocypack::ObjectIterator itr(links);
itr.valid();
++itr, ++offset
) {
auto collectionName = itr.key();
auto linkDefinition = itr.value();
if (!collectionName.isString()) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("while validating arangosearch link definition, error: collection at offset ") + std::to_string(offset) + " is not a string"
);
}
auto collection = resolver.getCollection(collectionName.copyString());
if (!collection) {
return arangodb::Result(
TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND,
std::string("while validating arangosearch link definition, error: collection '") + collectionName.copyString() + "' not a string"
);
}
// check link auth as per https://github.com/arangodb/backlog/issues/459
if (arangodb::ExecContext::CURRENT
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase.name(), collection->name(), arangodb::auth::Level::RO)) {
return arangodb::Result(
TRI_ERROR_FORBIDDEN,
std::string("while validating arangosearch link definition, error: collection '") + collectionName.copyString() + "' not authorised for read access"
);
}
IResearchLinkMeta meta;
std::string errorField;
if (!linkDefinition.isNull() && !meta.init(linkDefinition, errorField)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
errorField.empty()
? (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "': " + linkDefinition.toString())
: (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "' error in attribute: " + errorField)
);
}
}
return arangodb::Result();
}
/*static*/ arangodb::Result IResearchLinkHelper::updateLinks(
std::unordered_set<TRI_voc_cid_t>& modified,
TRI_vocbase_t& vocbase,
@ -577,4 +639,4 @@ namespace iresearch {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -65,6 +65,18 @@ struct IResearchLinkHelper {
////////////////////////////////////////////////////////////////////////////////
static std::string const& type() noexcept;
//////////////////////////////////////////////////////////////////////////////
/// @brief validate the link specifications for:
/// * valid link meta
/// * collection existence
/// * collection permissions
/// * valid link meta
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result validateLinks(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& links
);
//////////////////////////////////////////////////////////////////////////////
/// @brief updates the collections in 'vocbase' to match the specified
/// IResearchLink definitions

View File

@ -47,6 +47,7 @@
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/FlushFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/ExecContext.h"
@ -538,6 +539,115 @@ IResearchView::PersistedStore::PersistedStore(irs::utf8_path&& path)
: _path(std::move(path)) {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief IResearchView-specific implementation of a ViewFactory
////////////////////////////////////////////////////////////////////////////////
struct IResearchView::ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
auto* engine = arangodb::EngineSelectorFeature::ENGINE;
auto& properties = definition.isObject() ? definition : emptyObjectSlice(); // if no 'info' then assume defaults
auto links = properties.hasKey(StaticStrings::LinksField)
? properties.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
auto res = engine && engine->inRecovery()
? arangodb::Result() // do not validate if in recovery
: IResearchLinkHelper::validateLinks(vocbase, links);
if (!res.ok()) {
return res;
}
TRI_set_errno(TRI_ERROR_NO_ERROR); // reset before calling createView(...)
auto impl = vocbase.createView(definition);
if (!impl) {
return arangodb::Result(
TRI_ERROR_NO_ERROR == TRI_errno() ? TRI_ERROR_INTERNAL : TRI_errno(),
std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'"
);
}
// create links on a best-effor basis
// link creation failure does not cause view creation failure
try {
std::unordered_set<TRI_voc_cid_t> collections;
res = IResearchLinkHelper::updateLinks(
collections, vocbase, *impl, links
);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to create links while creating arangosearch view '" << view->name() << "': " << res.errorNumber() << " " << res.errorMessage();
}
} catch (arangodb::basics::Exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.code() << " " << e.what();
} catch (std::exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.what();
} catch (...) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "'";
}
view = impl;
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
auto* feature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabasePathFeature
>("DatabasePath");
if (!feature) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to find feature 'DatabasePath' while constructing arangosearch View in database '") + vocbase.name() + "'"
);
}
std::string error;
auto meta = std::make_shared<AsyncMeta>();
auto impl = std::shared_ptr<IResearchView>(
new IResearchView(vocbase, definition, *feature, planVersion)
);
if (!meta->init(definition, error)
|| !impl->_metaState.init(definition, error)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to initialize arangosearch View '") + impl->name() + "' from definition: " + definition.toString())
: (std::string("failed to initialize arangosearch View '") + impl->name() + "' from definition, error in attribute '" + error + "': " + definition.toString())
);
}
auto res = impl->updateProperties(meta); // update separately since per-instance async jobs already started
if (!res.ok()) {
return res;
}
view = impl;
return arangodb::Result();
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief container storing the view 'read' state for a given TransactionState
////////////////////////////////////////////////////////////////////////////////
@ -870,13 +980,6 @@ IResearchView::~IResearchView() {
}
}
}
// noexcept below
if (deleted()) {
StorageEngine* engine = EngineSelectorFeature::ENGINE;
TRI_ASSERT(engine);
engine->destroyView(vocbase(), *this);
}
}
arangodb::Result IResearchView::appendVelocyPackDetailed(
@ -1065,6 +1168,8 @@ arangodb::Result IResearchView::drop(
// if an errors occurs below than a drop retry would most likely happen
// ...........................................................................
_flushCallback.reset(); // unregister flush callback from flush thread
try {
if (_storePersisted) {
_storePersisted._writer->documents().remove(shared_filter);
@ -1274,6 +1379,12 @@ bool IResearchView::emplace(TRI_voc_cid_t cid) {
return false;
}
/*static*/ arangodb::ViewFactory const& IResearchView::factory() {
static const ViewFactory factory;
return factory;
}
arangodb::Result IResearchView::commit() {
ReadMutex mutex(_mutex); // '_storePersisted' can be asynchronously updated
SCOPED_LOCK(mutex);
@ -1513,122 +1624,6 @@ int IResearchView::insert(
return insertImpl(*ctx);
}
/*static*/ std::shared_ptr<LogicalView> IResearchView::make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit /*= {}*/
) {
auto* feature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabasePathFeature
>("DatabasePath");
if (!feature) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to find feature 'DatabasePath' while constructing arangosearch View in database '" << vocbase.id() << "'";
return nullptr;
}
auto view = std::shared_ptr<IResearchView>(
new IResearchView(vocbase, info, *feature, planVersion)
);
auto& impl = reinterpret_cast<IResearchView&>(*view);
auto meta = std::make_shared<AsyncMeta>();
auto& properties = info.isObject() ? info : emptyObjectSlice(); // if no 'info' then assume defaults
std::string error;
if (!meta->init(properties, error)
|| !impl.updateProperties(meta).ok() // update separately since per-instance async jobs already started
|| !impl._metaState.init(properties, error)) {
TRI_set_errno(TRI_ERROR_BAD_PARAMETER);
if (error.empty()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to initialize arangosearch view '" << impl.name() << "' from definition";
} else {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to initialize arangosearch view '" << impl.name() << "' from definition, error in attribute: " << error;
}
return nullptr;
}
auto links = properties.hasKey(StaticStrings::LinksField)
? properties.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
// check link auth as per https://github.com/arangodb/backlog/issues/459
if (arangodb::ExecContext::CURRENT) {
// check new links
for (arangodb::velocypack::ObjectIterator itr(links); itr.valid(); ++itr) {
if (!itr.key().isString()) {
continue; // not a resolvable collection (invalid jSON)
}
auto collection = vocbase.lookupCollection(itr.key().copyString());
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase.name(), collection->name(), arangodb::auth::Level::RO)) {
TRI_set_errno(TRI_ERROR_FORBIDDEN);
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "insufficient rights to create view with link to collection' " << collection->name() << "'";
return nullptr;
}
}
}
if (preCommit && !preCommit(view)) {
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure during pre-commit while constructing arangosearch View in database '" << vocbase.id() << "'";
return nullptr;
}
if (!isNew) {
return view; // nothing more to do
}
auto const res = create(static_cast<arangodb::LogicalViewStorageEngine&>(*view));
if (!res.ok()) {
TRI_set_errno(res.errorNumber());
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure during commit of created view while constructing arangosearch View in database '" << vocbase.id() << "', error: " << res.errorMessage();
return nullptr;
}
// create links on a best-effor basis
// link creation failure does not cause view creation failure
try {
std::unordered_set<TRI_voc_cid_t> collections;
auto res =
IResearchLinkHelper::updateLinks(collections, vocbase, *view, links);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to create links while creating arangosearch view '" << view->name() << "': " << res.errorNumber() << " " << res.errorMessage();
}
} catch (arangodb::basics::Exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.code() << " " << e.what();
} catch (std::exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.what();
} catch (...) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "'";
}
return view;
}
size_t IResearchView::memory() const {
ReadMutex mutex(_mutex); // view members can be asynchronously updated
SCOPED_LOCK(mutex);
@ -1921,33 +1916,20 @@ arangodb::Result IResearchView::updateProperties(
arangodb::velocypack::Slice const& slice,
bool partialUpdate
) {
std::string error;
IResearchViewMeta meta;
WriteMutex mutex(_mutex); // '_metaState' can be asynchronously read
arangodb::Result res;
SCOPED_LOCK_NAMED(mutex, mtx);
try {
auto links = slice.hasKey(StaticStrings::LinksField)
? slice.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
auto res = _inRecovery
? arangodb::Result() // do not validate if in recovery
: IResearchLinkHelper::validateLinks(vocbase(), links);
{
auto viewMeta = std::atomic_load(&_meta);
SCOPED_LOCK(viewMeta->write());
IResearchViewMeta* metaPtr = viewMeta.get();
auto& initialMeta = partialUpdate ? *metaPtr : IResearchViewMeta::DEFAULT();
if (!meta.init(slice, error, initialMeta)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to update arangosearch view '") + name() + "' from definition")
: (std::string("failed to update arangosearch view '") + name() + "' from definition, error in attribute: " + error)
);
if (!res.ok()) {
return res;
}
// reset non-updatable values to match current meta
meta._locale = viewMeta->_locale;
if (arangodb::ServerState::instance()->isDBServer()) {
viewMeta = std::make_shared<AsyncMeta>(); // create an instance not shared with cluster-view
}
WriteMutex mutex(_mutex); // '_metaState' can be asynchronously read
SCOPED_LOCK_NAMED(mutex, mtx);
// check link auth as per https://github.com/arangodb/backlog/issues/459
if (arangodb::ExecContext::CURRENT) {
@ -1957,70 +1939,105 @@ arangodb::Result IResearchView::updateProperties(
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase().name(), collection->name(), arangodb::auth::Level::RO)) {
return arangodb::Result(TRI_ERROR_FORBIDDEN);
}
}
auto links = slice.hasKey(StaticStrings::LinksField)
? slice.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
// check new links
for (arangodb::velocypack::ObjectIterator itr(links); itr.valid(); ++itr) {
if (!itr.key().isString()) {
continue; // not a resolvable collection (invalid jSON)
}
auto collection = vocbase().lookupCollection(itr.key().copyString());
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase().name(), collection->name(), arangodb::auth::Level::RO)) {
return arangodb::Result(TRI_ERROR_FORBIDDEN);
return arangodb::Result(
TRI_ERROR_FORBIDDEN,
std::string("while updating arangosearch definition, error: collection '") + collection->name() + "' not authorised for read access"
);
}
}
}
static_cast<IResearchViewMeta&>(*viewMeta) = std::move(meta);
updateProperties(viewMeta); // trigger reload of settings for async jobs
}
std::string error;
IResearchViewMeta meta;
mutex.unlock(true); // downgrade to a read-lock
{
auto viewMeta = std::atomic_load(&_meta);
SCOPED_LOCK(viewMeta->write());
IResearchViewMeta* metaPtr = viewMeta.get();
auto& initialMeta = partialUpdate ? *metaPtr : IResearchViewMeta::DEFAULT();
if (!slice.hasKey(StaticStrings::LinksField)
&& (partialUpdate || _inRecovery.load())) { // ignore missing links coming from WAL (inRecovery)
return res;
}
if (!meta.init(slice, error, initialMeta)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to update arangosearch view '") + name() + "' from definition: " + slice.toString())
: (std::string("failed to update arangosearch view '") + name() + "' from definition, error in attribute '" + error + "': " + slice.toString())
);
}
// ...........................................................................
// update links if requested (on a best-effort basis)
// indexing of collections is done in different threads so no locks can be held and rollback is not possible
// as a result it's also possible for links to be simultaneously modified via a different callflow (e.g. from collections)
// ...........................................................................
// reset non-updatable values to match current meta
meta._locale = viewMeta->_locale;
std::unordered_set<TRI_voc_cid_t> collections;
auto links = slice.hasKey(StaticStrings::LinksField)
? slice.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice(); // used for !partialUpdate
if (arangodb::ServerState::instance()->isDBServer()) {
viewMeta = std::make_shared<AsyncMeta>(); // create an instance not shared with cluster-view
}
static_cast<IResearchViewMeta&>(*viewMeta) = std::move(meta);
updateProperties(viewMeta); // trigger reload of settings for async jobs
}
mutex.unlock(true); // downgrade to a read-lock
if (links.isEmptyObject() && (partialUpdate || _inRecovery.load())) { // ignore missing links coming from WAL (inRecovery)
return res;
}
// ...........................................................................
// update links if requested (on a best-effort basis)
// indexing of collections is done in different threads so no locks can be held and rollback is not possible
// as a result it's also possible for links to be simultaneously modified via a different callflow (e.g. from collections)
// ...........................................................................
std::unordered_set<TRI_voc_cid_t> collections;
if (partialUpdate) {
mtx.unlock(); // release lock
SCOPED_LOCK(_updateLinksLock);
return IResearchLinkHelper::updateLinks(
collections, vocbase(), *this, links
);
}
auto stale = _metaState._collections;
if (partialUpdate) {
mtx.unlock(); // release lock
SCOPED_LOCK(_updateLinksLock);
return IResearchLinkHelper::updateLinks(
collections, vocbase(), *this, links
collections, vocbase(), *this, links, stale
); } catch (arangodb::basics::Exception& e) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '" << name() << "': " << e.code() << " " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
e.code(),
std::string("error updating properties for arangosearch view '") + name() + "'"
);
} catch (std::exception const& e) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '" << name() << "': " << e.what();
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") + name() + "'"
);
} catch (...) {
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "caught exception while updating properties for arangosearch view '" << name() << "'";
IR_LOG_EXCEPTION();
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("error updating properties for arangosearch view '") + name() + "'"
);
}
auto stale = _metaState._collections;
mtx.unlock(); // release lock
SCOPED_LOCK(_updateLinksLock);
return IResearchLinkHelper::updateLinks(
collections, vocbase(), *this, links, stale
);
return arangodb::Result();
}
arangodb::Result IResearchView::updateProperties(

View File

@ -50,6 +50,7 @@ namespace arangodb {
class DatabasePathFeature; // forward declaration
class TransactionState; // forward declaration
struct ViewFactory; // forward declaration
class ViewIterator; // forward declaration
namespace aql {
@ -199,6 +200,11 @@ class IResearchView final
////////////////////////////////////////////////////////////////////////////////
bool emplace(TRI_voc_cid_t cid);
//////////////////////////////////////////////////////////////////////////////
/// @brief the factory for this type of view
//////////////////////////////////////////////////////////////////////////////
static arangodb::ViewFactory const& factory();
////////////////////////////////////////////////////////////////////////////////
/// @brief insert a document into this IResearch View and the underlying
/// IResearch stores
@ -227,18 +233,6 @@ class IResearchView final
IResearchLinkMeta const& meta
);
///////////////////////////////////////////////////////////////////////////////
/// @brief view factory
/// @returns initialized view object
///////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit = {}
);
////////////////////////////////////////////////////////////////////////////////
/// @brief amount of memory in bytes occupied by this iResearch Link
////////////////////////////////////////////////////////////////////////////////
@ -334,6 +328,7 @@ class IResearchView final
PersistedStore(irs::utf8_path&& path);
};
struct ViewFactory; // forward declaration
class ViewStateHelper; // forward declaration
struct ViewStateRead; // forward declaration
struct ViewStateWrite; // forward declaration

View File

@ -334,6 +334,8 @@ IResearchViewBlockBase::getSome(size_t atMost) {
std::pair<ExecutionState, size_t> IResearchViewBlockBase::skipSome(size_t atMost) {
traceSkipSomeBegin(atMost);
if (_done) {
// aggregate stats
_engine->_stats.scannedIndex += static_cast<int64_t>(_inflight);
size_t skipped = _inflight;
_inflight = 0;
traceSkipSomeEnd(skipped, ExecutionState::DONE);
@ -351,6 +353,8 @@ std::pair<ExecutionState, size_t> IResearchViewBlockBase::skipSome(size_t atMost
_upstreamState = upstreamRes.first;
if (!upstreamRes.second) {
_done = true;
// aggregate stats
_engine->_stats.scannedIndex += static_cast<int64_t>(_inflight);
size_t skipped = _inflight;
_inflight = 0;
traceSkipSomeEnd(skipped, ExecutionState::DONE);

View File

@ -31,6 +31,7 @@
#include "Cluster/ServerState.h"
#include "IResearch/IResearchFeature.h"
#include "IResearch/VelocyPackHelper.h"
#include "RestServer/ViewTypesFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/CollectionNameResolver.h"
@ -51,6 +52,134 @@ typedef irs::async_utils::read_write_mutex::write_mutex WriteMutex;
namespace arangodb {
namespace iresearch {
////////////////////////////////////////////////////////////////////////////////
/// @brief IResearchView-specific implementation of a ViewFactory
////////////////////////////////////////////////////////////////////////////////
struct IResearchViewCoordinator::ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
auto* ci = ClusterInfo::instance();
if (!ci) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to find 'ClusterInfo' instance while creating arangosearch View in database '") + vocbase.name() + "'"
);
}
auto& properties = definition.isObject() ? definition : emptyObjectSlice(); // if no 'info' then assume defaults
auto links = properties.hasKey(StaticStrings::LinksField)
? properties.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
auto res = IResearchLinkHelper::validateLinks(vocbase, links);
if (!res.ok()) {
return res;
}
arangodb::LogicalView::ptr impl;
res = instantiate(impl, vocbase, definition, 0);
if (!res.ok()) {
return res;
}
if (!impl) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'"
);
}
arangodb::velocypack::Builder builder;
builder.openObject();
res = impl->toVelocyPack(builder, true, true); // include links so that Agency will always have a full definition
if (!res.ok()) {
return res;
}
builder.close();
std::string error;
auto resNum = ci->createViewCoordinator(
vocbase.name(), std::to_string(impl->id()), builder.slice(), error
);
if (TRI_ERROR_NO_ERROR != resNum) {
return arangodb::Result(
resNum,
std::string("failure during ClusterInfo persistance of created view while creating arangosearch View in database '") + vocbase.name() + "', error: " + error
);
}
// create links on a best-effor basis
// link creation failure does not cause view creation failure
try {
std::unordered_set<TRI_voc_cid_t> collections;
res = IResearchLinkHelper::updateLinks(
collections, vocbase, *impl, links
);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to create links while creating arangosearch view '" << view->name() << "': " << res.errorNumber() << " " << res.errorMessage();
}
} catch (arangodb::basics::Exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.code() << " " << e.what();
} catch (std::exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.what();
} catch (...) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "'";
}
view = ci->getView(vocbase.name(), std::to_string(impl->id())); // refresh view from Agency
if (view) {
view->open(); // open view to match the behaviour in StorageEngine::openExistingDatabase(...) and original behaviour of TRI_vocbase_t::createView(...)
}
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
std::string error;
auto impl = std::shared_ptr<IResearchViewCoordinator>(
new IResearchViewCoordinator(vocbase, definition, planVersion)
);
if (!impl->_meta.init(definition, error)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to initialize arangosearch View '") + impl->name() + "' from definition: " + definition.toString())
: (std::string("failed to initialize arangosearch View '") + impl->name() + "' from definition, error in attribute '" + error + "': " + definition.toString())
);
}
view = impl;
return arangodb::Result();
}
};
arangodb::Result IResearchViewCoordinator::appendVelocyPackDetailed(
arangodb::velocypack::Builder& builder,
bool forPersistence
@ -169,136 +298,10 @@ bool IResearchViewCoordinator::emplace(
).second;
}
/*static*/ std::shared_ptr<LogicalView> IResearchViewCoordinator::make(
TRI_vocbase_t& vocbase,
velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit
) {
auto& properties = info.isObject() ? info : emptyObjectSlice(); // if no 'info' then assume defaults
std::string error;
/*static*/ arangodb::ViewFactory const& IResearchViewCoordinator::factory() {
static const ViewFactory factory;
auto view = std::shared_ptr<IResearchViewCoordinator>(
new IResearchViewCoordinator(vocbase, info, planVersion)
);
if (!view->_meta.init(properties, error)) {
TRI_set_errno(TRI_ERROR_BAD_PARAMETER);
LOG_TOPIC(WARN, iresearch::TOPIC)
<< "failed to initialize arangosearch view from definition, error: " << error;
return nullptr;
}
auto links = properties.hasKey(StaticStrings::LinksField)
? properties.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
if (links.length()) {
auto* engine = arangodb::ClusterInfo::instance();
if (!engine) {
return nullptr;
}
// check link auth as per https://github.com/arangodb/backlog/issues/459
if (arangodb::ExecContext::CURRENT) {
// check new links
if (info.hasKey(StaticStrings::LinksField)) {
for (arangodb::velocypack::ObjectIterator itr(info.get(StaticStrings::LinksField)); itr.valid(); ++itr) {
if (!itr.key().isString()) {
continue; // not a resolvable collection (invalid jSON)
}
auto collection =
engine->getCollection(vocbase.name(), itr.key().copyString());
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase.name(), collection->name(), arangodb::auth::Level::RO)) {
TRI_set_errno(TRI_ERROR_FORBIDDEN);
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "insufficient rights to create view with link to collection' " << collection->name() << "'";
return nullptr;
}
}
}
}
}
if (preCommit && !preCommit(view)) {
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure during pre-commit while constructing arangosearch view in database '" << vocbase.id() << "'";
return nullptr;
}
if (!isNew) {
return view; // nothing more to do
}
arangodb::velocypack::Builder builder;
auto* ci = ClusterInfo::instance();
if (!ci) {
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure to find ClusterInfo instance while constructing arangosearch view in database '" << vocbase.id() << "'";
return nullptr;
}
builder.openObject();
auto res = view->toVelocyPack(builder, true, true); // include links so that Agency will always have a full definition
if (!res.ok()) {
TRI_set_errno(res.errorNumber());
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure to generate definitionf created view while constructing arangosearch view in database '" << vocbase.id() << "', error: " << res.errorMessage();
return nullptr;
}
builder.close();
auto resNum = ci->createViewCoordinator(
vocbase.name(), std::to_string(view->id()), builder.slice(), error
);
if (TRI_ERROR_NO_ERROR != resNum) {
TRI_set_errno(resNum);
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "Failure during commit of created view while constructing arangosearch view in database '" << vocbase.id() << "', error: " << error;
return nullptr;
}
// create links on a best-effor basis
// link creation failure does not cause view creation failure
try {
std::unordered_set<TRI_voc_cid_t> collections;
auto res =
IResearchLinkHelper::updateLinks(collections, vocbase, *view, links);
if (!res.ok()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to create links while creating arangosearch view '" << view->name() << "': " << res.errorNumber() << " " << res.errorMessage();
}
} catch (arangodb::basics::Exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.code() << " " << e.what();
} catch (std::exception const& e) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "': " << e.what();
} catch (...) {
IR_LOG_EXCEPTION();
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "caught exception while creating links while creating arangosearch view '" << view->name() << "'";
}
return view;
return factory;
}
IResearchViewCoordinator::IResearchViewCoordinator(
@ -339,20 +342,15 @@ arangodb::Result IResearchViewCoordinator::updateProperties(
}
try {
IResearchViewMeta meta;
std::string error;
auto links = slice.hasKey(StaticStrings::LinksField)
? slice.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice();
auto res = IResearchLinkHelper::validateLinks(vocbase(), links);
auto const& defaults = partialUpdate
? _meta
: IResearchViewMeta::DEFAULT();
if (!meta.init(slice, error, defaults)) {
return { TRI_ERROR_BAD_PARAMETER, error };
if (!res.ok()) {
return res;
}
// reset non-updatable values to match current meta
meta._locale = _meta._locale;
// check link auth as per https://github.com/arangodb/backlog/issues/459
if (arangodb::ExecContext::CURRENT) {
// check existing links
@ -362,31 +360,35 @@ arangodb::Result IResearchViewCoordinator::updateProperties(
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase().name(), collection->name(), arangodb::auth::Level::RO)) {
return arangodb::Result(TRI_ERROR_FORBIDDEN);
}
}
// check new links
if (slice.hasKey(StaticStrings::LinksField)) {
for (arangodb::velocypack::ObjectIterator itr(slice.get(StaticStrings::LinksField)); itr.valid(); ++itr) {
if (!itr.key().isString()) {
continue; // not a resolvable collection (invalid jSON)
}
auto collection =
engine->getCollection(vocbase().name(), itr.key().copyString());
if (collection
&& !arangodb::ExecContext::CURRENT->canUseCollection(vocbase().name(), collection->name(), arangodb::auth::Level::RO)) {
return arangodb::Result(TRI_ERROR_FORBIDDEN);
}
return arangodb::Result(
TRI_ERROR_FORBIDDEN,
std::string("while updating arangosearch definition, error: collection '") + collection->name() + "' not authorised for read access"
);
}
}
}
std::string error;
IResearchViewMeta meta;
auto const& defaults = partialUpdate
? _meta
: IResearchViewMeta::DEFAULT();
if (!meta.init(slice, error, defaults)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to update arangosearch view '") + name() + "' from definition: " + slice.toString())
: (std::string("failed to update arangosearch view '") + name() + "' from definition, error in attribute '" + error + "': " + slice.toString())
);
}
// reset non-updatable values to match current meta
meta._locale = _meta._locale;
// only trigger persisting of properties if they have changed
if (_meta != meta) {
arangodb::velocypack::Builder builder;
builder.openObject();
@ -408,7 +410,7 @@ arangodb::Result IResearchViewCoordinator::updateProperties(
}
}
if (!slice.hasKey(StaticStrings::LinksField) && partialUpdate) {
if (links.isEmptyObject() && partialUpdate) {
return arangodb::Result(); // nothing more to do
}
@ -436,9 +438,6 @@ arangodb::Result IResearchViewCoordinator::updateProperties(
}
std::unordered_set<TRI_voc_cid_t> collections;
auto links = slice.hasKey(StaticStrings::LinksField)
? slice.get(StaticStrings::LinksField)
: arangodb::velocypack::Slice::emptyObjectSlice(); // used for !partialUpdate
if (partialUpdate) {
return IResearchLinkHelper::updateLinks(
@ -478,10 +477,10 @@ arangodb::Result IResearchViewCoordinator::updateProperties(
);
}
return {};
return arangodb::Result();
}
Result IResearchViewCoordinator::drop() {
Result IResearchViewCoordinator::dropImpl() {
auto* engine = arangodb::ClusterInfo::instance();
if (!engine) {
@ -527,21 +526,6 @@ Result IResearchViewCoordinator::drop() {
}
}
// drop view then
std::string errorMsg;
int const res = ClusterInfo::instance()->dropViewCoordinator(
vocbase().name(), std::to_string(id()), errorMsg
);
if (res != TRI_ERROR_NO_ERROR) {
LOG_TOPIC(ERR, arangodb::Logger::CLUSTER)
<< "Could not drop view in agency, error: " << errorMsg
<< ", errorCode: " << res;
return { res, errorMsg };
}
return {};
}
@ -550,4 +534,4 @@ Result IResearchViewCoordinator::drop() {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -30,6 +30,12 @@
#include <velocypack/Builder.h>
#include <velocypack/Slice.h>
namespace arangodb {
struct ViewFactory; // forward declaration
} // arangodb
namespace arangodb {
namespace iresearch {
@ -41,6 +47,8 @@ namespace iresearch {
class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo {
public:
using LogicalView::drop;
//////////////////////////////////////////////////////////////////////////////
/// @brief remove all documents matching collection 'cid' from this IResearch
/// View
@ -63,17 +71,10 @@ class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo {
arangodb::velocypack::Slice const& value
);
///////////////////////////////////////////////////////////////////////////////
/// @brief view factory
/// @returns initialized view object
///////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit
);
//////////////////////////////////////////////////////////////////////////////
/// @brief the factory for this type of view
//////////////////////////////////////////////////////////////////////////////
static arangodb::ViewFactory const& factory();
bool visitCollections(CollectionVisitor const& visitor) const override;
@ -81,8 +82,6 @@ class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo {
// NOOP
}
Result drop() override;
virtual Result rename(
std::string&& /*newName*/,
bool /*doSync*/
@ -103,7 +102,11 @@ class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo {
bool forPersistence
) const override;
virtual arangodb::Result dropImpl() override;
private:
struct ViewFactory; // forward declaration
IResearchViewCoordinator(
TRI_vocbase_t& vocbase, velocypack::Slice info, uint64_t planVersion
);

View File

@ -32,6 +32,7 @@
#include "Cluster/ClusterInfo.h"
#include "Logger/LogMacros.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "StorageEngine/TransactionState.h"
#include "Transaction/Methods.h"
#include "VocBase/LogicalCollection.h"
@ -176,6 +177,240 @@ std::string generateName(TRI_voc_cid_t viewId, TRI_voc_cid_t collectionId) {
namespace arangodb {
namespace iresearch {
////////////////////////////////////////////////////////////////////////////////
/// @brief IResearchView-specific implementation of a ViewFactory
////////////////////////////////////////////////////////////////////////////////
struct IResearchViewDBServer::ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
auto* ci = ClusterInfo::instance();
if (!ci) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to find 'ClusterInfo' instance while creating arangosearch View in database '") + vocbase.name() + "'"
);
}
arangodb::LogicalView::ptr impl;
auto res = instantiate(impl, vocbase, definition, 0);
if (!res.ok()) {
return res;
}
if (!impl) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'"
);
}
arangodb::velocypack::Builder builder;
builder.openObject();
res = impl->toVelocyPack(builder, true, true); // include links so that Agency will always have a full definition
if (!res.ok()) {
return res;
}
builder.close();
std::string error;
auto resNum = ci->createViewCoordinator(
vocbase.name(), std::to_string(impl->id()), builder.slice(), error
);
if (TRI_ERROR_NO_ERROR != resNum) {
return arangodb::Result(
resNum,
std::string("failure during ClusterInfo persistance of created view while creating arangosearch View in database '") + vocbase.name() + "', error: " + error
);
}
// NOTE: link creation is ignored since on the db-server links are created
// by their LogicalCollections themselves
view = ci->getView(vocbase.name(), std::to_string(impl->id())); // refresh view from Agency
if (view) {
view->open(); // open view to match the behaviour in StorageEngine::openExistingDatabase(...) and original behaviour of TRI_vocbase_t::createView(...)
}
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
irs::string_ref name;
bool seen;
if (!getString(name, definition, arangodb::StaticStrings::DataSourceName, seen, irs::string_ref::EMPTY)
|| !seen) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("definition supplied without a 'name' while instantiating arangosearch View in database '") + vocbase.name() + "'"
);
}
// not a per-cid view instance (get here from ClusterInfo)
if (!irs::starts_with(name, VIEW_NAME_PREFIX)) {
auto* feature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabasePathFeature
>("DatabasePath");
if (!feature) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to find feature 'DatabasePath' while constructing arangosearch View in database '") + vocbase.name() + "'"
);
}
auto* ci = ClusterInfo::instance();
if (!ci) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to find 'ClusterInfo' instance while constructing arangosearch View in database '") + vocbase.name() + "'"
);
}
auto wiew = std::shared_ptr<IResearchViewDBServer>(
new IResearchViewDBServer(vocbase, definition, *feature, planVersion)
);
std::string error;
IResearchViewMeta meta;
if (!meta.init(definition, error)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
error.empty()
? (std::string("failed to initialize arangosearch View '") + wiew->name() + "' from definition: " + definition.toString())
: (std::string("failed to initialize arangosearch View '") + wiew->name() + "' from definition, error in attribute '" + error + "': " + definition.toString())
);
}
// search for the previous view instance and check if it's meta is the same
{
auto oldLogicalWiew =
ci->getViewCurrent(vocbase.name(), std::to_string(wiew->id()));
auto* oldWiew = arangodb::LogicalView::cast<IResearchViewDBServer>(
oldLogicalWiew.get()
);
if (oldWiew && *(oldWiew->_meta) == meta) {
wiew->_meta = oldWiew->_meta;
}
}
if (!(wiew->_meta)) {
wiew->_meta = std::make_shared<AsyncMeta>();
static_cast<IResearchViewMeta&>(*(wiew->_meta)) = std::move(meta);
}
view = wiew;
return arangodb::Result();
}
// .........................................................................
// a per-cid view instance
// get here only from StorageEngine startup or WAL recovery
// .........................................................................
view = vocbase.lookupView(name);
if (view) {
return arangodb::Result(); // resuse view from vocbase
}
auto* ci = ClusterInfo::instance();
std::shared_ptr<AsyncMeta> meta;
// reference meta from cluster-wide view if available to
// avoid memory and thread allocation
// if not availble then the meta will be reassigned when
// the per-cid instance is associated with the cluster-wide view
if (ci) {
auto planId = arangodb::basics::VelocyPackHelper::stringUInt64(
definition.get(arangodb::StaticStrings::DataSourcePlanId)
); // planId set in ensure(...)
auto wiewId = std::to_string(planId);
auto logicalWiew = ci->getView(vocbase.name(), wiewId); // here if creating per-cid view during loadPlan()
auto* wiew = arangodb::LogicalView::cast<IResearchViewDBServer>(
logicalWiew.get()
);
// if not found in 'Plan' then search in 'Current'
if (!wiew) {
logicalWiew = ci->getViewCurrent(vocbase.name(), wiewId); // here if creating per-cid view outisde of loadPlan()
wiew = arangodb::LogicalView::cast<IResearchViewDBServer>(
logicalWiew.get()
);
}
if (wiew) {
meta = wiew->_meta;
}
}
// no view for shard
arangodb::LogicalView::ptr impl;
auto res = IResearchView::factory().instantiate(
impl, vocbase, definition, planVersion
);
if (!res.ok()) {
return res;
}
if (!impl) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure during instantiation while creating an arangosearch View '") + std::string(name) + "' in database '" + vocbase.name() + "'"
);
}
if (meta) {
res = arangodb::LogicalView::cast<IResearchView>(*impl).updateProperties(meta);
if (!res.ok()) {
return res;
}
}
// a wrapper to remove the view from vocbase if it no longer has any links
// hold a reference to the original view in the deleter so that the view is
// still valid for the duration of the pointer wrapper
view = std::shared_ptr<arangodb::LogicalView>(
impl.get(),
[impl](arangodb::LogicalView*)->void {
static const auto visitor = [](TRI_voc_cid_t)->bool { return false; };
auto& vocbase = impl->vocbase();
// same view in vocbase and with no collections
if (impl.get() == vocbase.lookupView(impl->id()).get() // avoid double dropView(...)
&& impl->visitCollections(visitor)
&& !impl->drop().ok()) { // per-cid collections always system
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to drop stale arangosearch View '" << impl->name() << "' while from database '" << vocbase.name() << "'";
}
}
);
return arangodb::Result();
}
};
IResearchViewDBServer::IResearchViewDBServer(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
@ -214,12 +449,12 @@ arangodb::Result IResearchViewDBServer::appendVelocyPackDetailed(
}
arangodb::Result IResearchViewDBServer::drop() {
arangodb::Result IResearchViewDBServer::dropImpl() {
WriteMutex mutex(_mutex);
SCOPED_LOCK(mutex); // 'collections_' can be asynchronously read
for (auto itr = _collections.begin(); itr != _collections.end();) {
auto res = vocbase().dropView(itr->second->id(), true); // per-cid collections always system
auto res = itr->second->drop();
if (!res.ok()) {
return res; // fail on first failure
@ -241,7 +476,7 @@ arangodb::Result IResearchViewDBServer::drop(TRI_voc_cid_t cid) noexcept {
return arangodb::Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND);
}
auto res = vocbase().dropView(itr->second->id(), true); // per-cid collections always system
auto res = itr->second->drop();
if (res.ok()) {
_collections.erase(itr);
@ -373,161 +608,10 @@ std::shared_ptr<arangodb::LogicalView> IResearchViewDBServer::ensure(
);
}
/*static*/ std::shared_ptr<LogicalView> IResearchViewDBServer::make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit /*= {}*/
) {
if (!info.isObject()) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "non-object definition supplied while instantiating arangosearch view in database '" << vocbase.name() << "'";
/*static*/ arangodb::ViewFactory const& IResearchViewDBServer::factory() {
static const ViewFactory factory;
return nullptr;
}
irs::string_ref name;
bool seen;
if (!getString(name, info, arangodb::StaticStrings::DataSourceName, seen, std::string())
|| !seen) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "definition supplied without a 'name' while instantiating arangosearch view in database '" << vocbase.name() << "'";
return nullptr;
}
// not a per-cid view instance (get here from ClusterInfo)
if (!irs::starts_with(name, VIEW_NAME_PREFIX)) {
auto* feature = arangodb::application_features::ApplicationServer::lookupFeature<
arangodb::DatabasePathFeature
>("DatabasePath");
if (!feature) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to find feature 'DatabasePath' while constructing arangosearch view in database '" << vocbase.id() << "'";
return nullptr;
}
auto* ci = ClusterInfo::instance();
if (!ci) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to find ClusterInfo instance while constructing arangosearch view in database '" << vocbase.id() << "'";
TRI_set_errno(TRI_ERROR_INTERNAL);
return nullptr;
}
auto wiew = std::shared_ptr<IResearchViewDBServer>(
new IResearchViewDBServer(vocbase, info, *feature, planVersion)
);
auto& properties = info.isObject() ? info : emptyObjectSlice(); // if no 'info' then assume defaults
std::string error;
IResearchViewMeta meta;
if (!meta.init(properties, error)) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failed to initialize arangosearch view from definition, error: " << error;
return nullptr;
}
// search for the previous view instance and check if it's meta is the same
{
auto oldLogicalWiew =
ci->getViewCurrent(vocbase.name(), std::to_string(wiew->id()));
auto* oldWiew =
LogicalView::cast<IResearchViewDBServer>(oldLogicalWiew.get());
if (oldWiew && *(oldWiew->_meta) == meta) {
wiew->_meta = oldWiew->_meta;
}
}
if (!(wiew->_meta)) {
wiew->_meta = std::make_shared<AsyncMeta>();
static_cast<IResearchViewMeta&>(*(wiew->_meta)) = std::move(meta);
}
if (preCommit && !preCommit(wiew)) {
LOG_TOPIC(ERR, arangodb::iresearch::TOPIC)
<< "failure during pre-commit while constructing arangosearch view in database '" << vocbase.id() << "'";
return nullptr;
}
return wiew;
}
// ...........................................................................
// a per-cid view instance (get here only from StorageEngine startup or WAL recovery)
// ...........................................................................
auto view = vocbase.lookupView(name);
if (view) {
return view;
}
auto* ci = ClusterInfo::instance();
std::shared_ptr<AsyncMeta> meta;
// reference meta from cluster-wide view if available to
// avoid memory and thread allocation
// if not availble then the meta will be reassigned when
// the per-cid instance is associated with the cluster-wide view
if (ci) {
auto planId = arangodb::basics::VelocyPackHelper::stringUInt64(
info.get(arangodb::StaticStrings::DataSourcePlanId)
); // planId set in ensure(...)
auto wiewId = std::to_string(planId);
auto logicalWiew = ci->getView(vocbase.name(), wiewId); // here if creating per-cid view during loadPlan()
auto* wiew = LogicalView::cast<IResearchViewDBServer>(logicalWiew.get());
// if not found in 'Plan' then search in 'Current'
if (!wiew) {
logicalWiew = ci->getViewCurrent(vocbase.name(), wiewId); // here if creating per-cid view outisde of loadPlan()
wiew = LogicalView::cast<IResearchViewDBServer>(logicalWiew.get());
}
if (wiew) {
meta = wiew->_meta;
}
}
// no view for shard
view = IResearchView::make(vocbase, info, isNew, planVersion, preCommit);
if (!view
|| (meta && !LogicalView::cast<IResearchView>(*view).updateProperties(meta).ok())) {
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure while creating an arangosearch view '" << name << "' in database '" << vocbase.name() << "'";
return nullptr;
}
// a wrapper to remove the view from vocbase if it no longer has any links
// hold a reference to the original view in the deleter so that the view is
// still valid for the duration of the pointer wrapper
return std::shared_ptr<arangodb::LogicalView>(
view.get(),
[view](arangodb::LogicalView*)->void {
static const auto visitor = [](TRI_voc_cid_t)->bool { return false; };
auto& vocbase = view->vocbase();
// same view in vocbase and with no collections
if (view.get() == vocbase.lookupView(view->id()).get() // avoid double dropView(...)
&& view->visitCollections(visitor)
&& !vocbase.dropView(view->id(), true).ok()) { // per-cid collections always system
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
<< "failure to drop stale arangosearch view '" << view->name() << "' while from database '" << vocbase.name() << "'";
}
}
);
return factory;
}
void IResearchViewDBServer::open() {
@ -784,4 +868,4 @@ bool IResearchViewDBServer::visitCollections(
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -35,6 +35,7 @@ namespace arangodb {
class DatabasePathFeature;
class TransactionState;
struct ViewFactory; // forward declaration
class CollectionNameResolver;
namespace transaction {
@ -55,8 +56,7 @@ class IResearchViewDBServer final: public arangodb::LogicalViewClusterInfo {
public:
virtual ~IResearchViewDBServer();
/// @return success
virtual arangodb::Result drop() override;
using LogicalView::drop;
//////////////////////////////////////////////////////////////////////////////
/// @brief drop the view association for the specified 'cid'
@ -76,17 +76,10 @@ class IResearchViewDBServer final: public arangodb::LogicalViewClusterInfo {
bool create = true
);
///////////////////////////////////////////////////////////////////////////////
/// @brief view factory
/// @returns initialized view object
///////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool isNew,
uint64_t planVersion,
LogicalView::PreCommitCallback const& preCommit = {}
);
//////////////////////////////////////////////////////////////////////////////
/// @brief the factory for this type of view
//////////////////////////////////////////////////////////////////////////////
static arangodb::ViewFactory const& factory();
virtual void open() override;
virtual arangodb::Result rename(std::string&& newName, bool doSync) override;
@ -118,7 +111,11 @@ class IResearchViewDBServer final: public arangodb::LogicalViewClusterInfo {
bool forPersistence
) const override;
virtual arangodb::Result dropImpl() override;
private:
struct ViewFactory; // forward declaration
std::map<TRI_voc_cid_t, std::shared_ptr<arangodb::LogicalView>> _collections;
std::shared_ptr<AsyncMeta> _meta; // the shared view configuration (never null!!!)
mutable irs::async_utils::read_write_mutex _mutex; // for use with members
@ -134,4 +131,4 @@ class IResearchViewDBServer final: public arangodb::LogicalViewClusterInfo {
} // iresearch
} // arangodb
#endif
#endif

View File

@ -2090,9 +2090,10 @@ std::unique_ptr<TRI_vocbase_t> MMFilesEngine::openExistingDatabase(
);
}
auto const view = LogicalView::create(*vocbase, it, false);
LogicalView::ptr view;
auto res = LogicalView::instantiate(view, *vocbase, it);
if (!view) {
if (!res.ok() || !view) {
auto const message = "failed to instantiate view '" + name + "'";
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, message.c_str());
@ -3641,3 +3642,7 @@ void MMFilesEngine::enableCompaction() {
bool MMFilesEngine::isCompactionDisabled() const {
return _compactionDisabled.load() > 0;
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -1125,7 +1125,7 @@ bool MMFilesWalRecoverState::ReplayMarker(MMFilesMarker const* marker,
vocbase->lookupView(viewId);
if (view != nullptr) {
vocbase->dropView(view->id(), true); // drop an existing view
view->drop(); // drop an existing view
}
// check if there is another view with the same name as the one that
@ -1138,7 +1138,7 @@ bool MMFilesWalRecoverState::ReplayMarker(MMFilesMarker const* marker,
view = vocbase->lookupView(name);
if (view != nullptr) {
vocbase->dropView(view->id(), true);
view->drop();
}
} else {
LOG_TOPIC(WARN, arangodb::Logger::ENGINES)
@ -1162,7 +1162,8 @@ bool MMFilesWalRecoverState::ReplayMarker(MMFilesMarker const* marker,
TRI_DEFER(state->databaseFeature->forceSyncProperties(oldSync));
}
view = vocbase->createView(payloadSlice);
auto res = arangodb::LogicalView::create(view,*vocbase, payloadSlice);
TRI_ASSERT(res.ok());
TRI_ASSERT(view != nullptr);
TRI_ASSERT(view->id() == viewId); // otherwise this a corrupt marker
} catch (basics::Exception const& ex) {

View File

@ -1740,6 +1740,57 @@ void RestReplicationHandler::handleCommandRestoreView() {
LOG_TOPIC(TRACE, Logger::REPLICATION) << "restoring view: "
<< nameSlice.copyString();
if (ServerState::instance()->isCoordinator()) {
try {
auto* ci = ClusterInfo::instance();
auto view = ci->getView(_vocbase.name(), nameSlice.toString());
if (view) {
if (overwrite) {
auto res = view->drop();
if (!res.ok()) {
generateError(res);
return;
}
generateError(TRI_ERROR_ARANGO_DUPLICATE_NAME);
return;
}
}
auto res = LogicalView::create(view, _vocbase, slice); // must create() since view was drop()ed
if (!res.ok()) {
generateError(res);
return;
}
if (!view) {
generateError(Result(TRI_ERROR_INTERNAL, "problem creating view"));
return;
}
velocypack::Builder result;
result.openObject();
result.add("result", velocypack::Slice::trueSlice());
result.close();
generateResult(rest::ResponseCode::OK, result.slice());
} catch (basics::Exception const& ex) {
generateError(Result(ex.code(), ex.message()));
} catch (...) {
generateError(Result(TRI_ERROR_INTERNAL, "problem creating view"));
}
return; // done
}
auto view = _vocbase.lookupView(nameSlice.copyString());
if (view) {
@ -1760,6 +1811,7 @@ void RestReplicationHandler::handleCommandRestoreView() {
try {
view = _vocbase.createView(slice);
if (view == nullptr) {
generateError(rest::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL,
"problem creating view");

View File

@ -200,32 +200,38 @@ void RestViewHandler::createView() {
}
try {
auto view = _vocbase.createView(body);
LogicalView::ptr view;
auto res = LogicalView::create(view, _vocbase, body);
if (view != nullptr) {
VPackBuilder props;
if (!res.ok()) {
generateError(res);
props.openObject();
auto res = view->toVelocyPack(props, true, false);
if (!res.ok()) {
generateError(res);
return;
}
props.close();
generateResult(rest::ResponseCode::CREATED, props.slice());
} else {
generateError(rest::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL,
"problem creating view");
return;
}
if (!view) {
generateError(arangodb::Result(TRI_ERROR_INTERNAL, "problem creating view"));
return;
}
velocypack::Builder builder;
builder.openObject();
res = view->toVelocyPack(builder, true, false);
if (!res.ok()) {
generateError(res);
return;
}
builder.close();
generateResult(rest::ResponseCode::CREATED, builder.slice());
} catch (basics::Exception const& ex) {
generateError(GeneralResponse::responseCode(ex.code()), ex.code(), ex.message());
generateError(arangodb::Result(ex.code(), ex.message()));
} catch (...) {
generateError(rest::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL,
"problem creating view");
generateError(arangodb::Result(TRI_errno(), "problem creating view"));
}
}
@ -417,13 +423,22 @@ void RestViewHandler::deleteView() {
return;
}
auto res = _vocbase.dropView(view->id(), allowDropSystem);
// prevent dropping of system views
if (!allowDropSystem && view->system()) {
generateError(Result(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view"));
if (res.ok()) {
generateOk(rest::ResponseCode::OK, VPackSlice::trueSlice());
} else {
generateError(res);
return;
}
auto res = view->drop();
if (!res.ok()) {
generateError(res);
return;
}
generateOk(rest::ResponseCode::OK, VPackSlice::trueSlice());
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -29,8 +29,33 @@
namespace {
struct InvalidViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr&,
TRI_vocbase_t&,
arangodb::velocypack::Slice const& definition
) const override {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("failure to create view without a factory for definition: ") + definition.toString()
);
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr&,
TRI_vocbase_t&,
arangodb::velocypack::Slice const& definition,
uint64_t
) const override {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("failure to instantiate view without a factory for definition: ") + definition.toString()
);
}
};
std::string const FEATURE_NAME("ViewTypes");
arangodb::ViewTypesFeature::ViewFactory const INVALID{};
InvalidViewFactory const INVALID;
} // namespace
@ -47,13 +72,6 @@ arangodb::Result ViewTypesFeature::emplace(
LogicalDataSource::Type const& type,
ViewFactory const& factory
) {
if (!factory) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("view factory undefined during view factory registration for view type '") + type.name() + "'"
);
}
auto* feature =
arangodb::application_features::ApplicationServer::lookupFeature("Bootstrap");
auto* bootstrapFeature = dynamic_cast<BootstrapFeature*>(feature);
@ -66,7 +84,7 @@ arangodb::Result ViewTypesFeature::emplace(
);
}
if (!_factories.emplace(&type, factory).second) {
if (!_factories.emplace(&type, &factory).second) {
return arangodb::Result(
TRI_ERROR_ARANGO_DUPLICATE_IDENTIFIER,
std::string("view factory previously registered during view factory registration for view type '") + type.name() + "'"
@ -76,12 +94,13 @@ arangodb::Result ViewTypesFeature::emplace(
return arangodb::Result();
}
ViewTypesFeature::ViewFactory const& ViewTypesFeature::factory(
ViewFactory const& ViewTypesFeature::factory(
LogicalDataSource::Type const& type
) const noexcept {
auto itr = _factories.find(&type);
TRI_ASSERT(itr == _factories.end() || false == !(itr->second)); // ViewTypesFeature::emplace(...) inserts non-nullptr
return itr == _factories.end() ? INVALID : itr->second;
return itr == _factories.end() ? INVALID : *(itr->second);
}
/*static*/ std::string const& ViewTypesFeature::name() {

View File

@ -28,27 +28,36 @@
namespace arangodb {
////////////////////////////////////////////////////////////////////////////////
/// @brief LogicalView factory for both end-user and internal instantiation
////////////////////////////////////////////////////////////////////////////////
struct ViewFactory {
virtual ~ViewFactory() = default; // define to silence warning
//////////////////////////////////////////////////////////////////////////////
/// @brief LogicalView factory for end-user validation instantiation and
/// persistence
/// @return if success then 'view' is set, else 'view' state is undefined
//////////////////////////////////////////////////////////////////////////////
virtual Result create(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice const& definition
) const = 0;
//////////////////////////////////////////////////////////////////////////////
/// @brief LogicalView factory for internal instantiation only
//////////////////////////////////////////////////////////////////////////////
virtual Result instantiate(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice const& definition,
uint64_t planVersion // cluster plan version ('0' by default for non-cluster)
) const = 0;
};
class ViewTypesFeature final: public application_features::ApplicationFeature {
public:
//////////////////////////////////////////////////////////////////////////////
/// @brief typedef for a LogicalView factory function
/// This typedef is used when registering the factory function for any view
/// type. the creator function is called when a view is first created or
/// re-opened after a server restart. the VelocyPack Slice will contain all
/// information about the view's general and implementation-specific properties.
/// @param preCommit called before completing view creation (IFF returns true)
/// e.g. before persisting definition to filesystem
/// IFF preCommit == false then skip invocation
//////////////////////////////////////////////////////////////////////////////
typedef std::function<std::shared_ptr<LogicalView>(
TRI_vocbase_t& vocbase, // database
velocypack::Slice const& definition, // view definition
bool isNew, // new view mark
uint64_t planVersion, // cluster plan version ('0' by default for non-cluster)
LogicalView::PreCommitCallback const& preCommit
)> ViewFactory;
explicit ViewTypesFeature(application_features::ApplicationServer& server);
/// @return 'factory' for 'type' was added successfully
@ -67,7 +76,7 @@ class ViewTypesFeature final: public application_features::ApplicationFeature {
void unprepare() override final;
private:
std::unordered_map<LogicalDataSource::Type const*, ViewFactory> _factories;
std::unordered_map<LogicalDataSource::Type const*, ViewFactory const*> _factories;
};
} // arangodb

View File

@ -1900,9 +1900,10 @@ std::unique_ptr<TRI_vocbase_t> RocksDBEngine::openExistingDatabase(
TRI_ASSERT(!it.get("id").isNone());
auto const view = LogicalView::create(*vocbase, it, false);
LogicalView::ptr view;
auto res = LogicalView::instantiate(view, *vocbase, it);
if (!view) {
if (!res.ok() || !view) {
auto const message = "failed to instantiate view '" + name + "'";
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, message);
@ -2293,4 +2294,4 @@ bool RocksDBEngine::canUseRangeDeleteInWal() const {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -199,11 +199,19 @@ static void JS_CreateViewVocbase(
);
try {
auto view = vocbase.createView(builder.slice());
LogicalView::ptr view;
auto res = LogicalView::create(view, vocbase, builder.slice());
TRI_ASSERT(view != nullptr);
if (!res.ok()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(res.errorNumber(), res.errorMessage());
}
if (!view) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "problem creating view");
}
v8::Handle<v8::Value> result = WrapView(isolate, view);
if (result.IsEmpty()) {
TRI_V8_THROW_EXCEPTION_MEMORY();
}
@ -268,7 +276,12 @@ static void JS_DropViewVocbase(
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view");
}
auto res = vocbase.dropView(view->id(), allowDropSystem);
// prevent dropping of system views
if (!allowDropSystem && view->system()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view");
}
auto res = view->drop();
if (!res.ok()) {
TRI_V8_THROW_EXCEPTION(res);
@ -319,7 +332,12 @@ static void JS_DropViewVocbaseObj(
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop view");
}
auto res = view->vocbase().dropView(view->id(), allowDropSystem);
// prevent dropping of system views
if (!allowDropSystem && view->system()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FORBIDDEN, "insufficient rights to drop system view");
}
auto res = view->drop();
if (!res.ok()) {
TRI_V8_THROW_EXCEPTION(res);

View File

@ -147,7 +147,7 @@ namespace arangodb {
std::lock_guard<std::mutex> lock(mutex);
auto itr = types.emplace(name, Type());
if (itr.second) {
if (itr.second && name.data()) {
const_cast<std::string&>(itr.first->second._name) = name.toString(); // update '_name'
const_cast<arangodb::velocypack::StringRef&>(itr.first->first) =
itr.first->second.name(); // point key at value stored in '_name'

View File

@ -90,53 +90,51 @@ LogicalView::LogicalView(
return category;
}
/*static*/ std::shared_ptr<LogicalView> LogicalView::create(
/*static*/ Result LogicalView::create(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice definition,
bool isNew,
uint64_t planVersion /*= 0*/,
PreCommitCallback const& preCommit /*= PreCommitCallback()*/
velocypack::Slice definition
) {
auto const* viewTypes =
auto* viewTypes =
application_features::ApplicationServer::lookupFeature<ViewTypesFeature>();
if (!viewTypes) {
LOG_TOPIC(ERR, Logger::VIEWS)
<< "Failure to get 'ViewTypes' feature while creating LogicalView";
return nullptr;
return Result(
TRI_ERROR_INTERNAL,
"Failure to get 'ViewTypes' feature while creating LogicalView"
);
}
auto const viewType = basics::VelocyPackHelper::getStringRef(
definition, StaticStrings::DataSourceType, ""
auto type = basics::VelocyPackHelper::getStringRef(
definition, StaticStrings::DataSourceType, velocypack::StringRef(nullptr, 0)
);
auto const& dataSourceType =
arangodb::LogicalDataSource::Type::emplace(viewType);
auto const& viewFactory = viewTypes->factory(dataSourceType);
auto& factory = viewTypes->factory(LogicalDataSource::Type::emplace(type));
if (!viewFactory) {
TRI_set_errno(TRI_ERROR_BAD_PARAMETER);
LOG_TOPIC(ERR, Logger::VIEWS)
<< "Found view type for which there is no factory, type: "
<< viewType.toString();
return factory.create(view, vocbase, definition);
}
return nullptr;
/*static*/ Result LogicalView::instantiate(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice definition,
uint64_t planVersion /*= 0*/
) {
auto* viewTypes =
application_features::ApplicationServer::lookupFeature<ViewTypesFeature>();
if (!viewTypes) {
return Result(
TRI_ERROR_INTERNAL,
"Failure to get 'ViewTypes' feature while creating LogicalView"
);
}
auto view = viewFactory(vocbase, definition, isNew, planVersion, preCommit);
auto type = basics::VelocyPackHelper::getStringRef(
definition, StaticStrings::DataSourceType, velocypack::StringRef(nullptr, 0)
);
auto& factory = viewTypes->factory(LogicalDataSource::Type::emplace(type));
if (!view) {
LOG_TOPIC(ERR, Logger::VIEWS)
<< "Failure to instantiate view of type: " << viewType.toString();
return nullptr;
}
LOG_TOPIC(DEBUG, Logger::VIEWS)
<< "created '" << viewType.toString() << "' view '" << view->name() << "' ("
<< view->guid() << ")";
return view;
return factory.instantiate(view, vocbase, definition, planVersion);
}
// -----------------------------------------------------------------------------
@ -188,6 +186,53 @@ arangodb::Result LogicalViewClusterInfo::appendVelocyPack(
return arangodb::Result();
}
arangodb::Result LogicalViewClusterInfo::drop() {
if (deleted()) {
return Result(); // view already dropped
}
auto* engine = arangodb::ClusterInfo::instance();
if (!engine) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to get storage engine while dropping View '") + name() + "'"
);
}
try {
deleted(true); // mark as deleted to avoid double-delete (including recursive calls)
auto res = dropImpl();
if (!res.ok()) {
deleted(false); // not fully deleted
return res;
}
std::string error;
auto resNum = engine->dropViewCoordinator(
vocbase().name(), std::to_string(id()), error
);
if (TRI_ERROR_NO_ERROR != resNum) {
deleted(false); // not fully deleted
return arangodb::Result(
resNum,
std::string("failure during ClusterInfo removal of View in database '") + vocbase().name() + "', error: " + error
);
}
} catch (...) {
deleted(false); // not fully deleted
throw;
}
return arangodb::Result();
}
// -----------------------------------------------------------------------------
// --SECTION-- LogicalViewStorageEngine
// -----------------------------------------------------------------------------
@ -207,7 +252,7 @@ LogicalViewStorageEngine::~LogicalViewStorageEngine() {
if (deleted()) {
StorageEngine* engine = EngineSelectorFeature::ENGINE;
TRI_ASSERT(engine);
// FIXME TODO is this required?
engine->destroyView(vocbase(), *this);
}
}
@ -260,51 +305,36 @@ arangodb::Result LogicalViewStorageEngine::appendVelocyPack(
return arangodb::Result();
}
/*static*/ arangodb::Result LogicalViewStorageEngine::create(
LogicalViewStorageEngine const& view
) {
TRI_ASSERT(!ServerState::instance()->isCoordinator());
StorageEngine* engine = EngineSelectorFeature::ENGINE;
if (!engine) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failure to get storage engine during storage engine persistance of view '") + view.name() + "'"
);
}
try {
return engine->createView(view.vocbase(), view.id(), view);
} catch (std::exception const& e) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("caught exception during storage engine persistance of view '") + view.name() + "': " + e.what()
);
} catch (...) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("caught exception during storage engine persistance of view '") + view.name() + "'"
);
}
}
arangodb::Result LogicalViewStorageEngine::drop() {
if (deleted()) {
return Result(); // view already dropped
}
TRI_ASSERT(!ServerState::instance()->isCoordinator());
StorageEngine* engine = EngineSelectorFeature::ENGINE;
TRI_ASSERT(engine);
auto res = dropImpl();
try {
deleted(true); // mark as deleted to avoid double-delete (including recursive calls)
// skip on error or if already called by dropImpl()
if (res.ok() && !deleted()) {
deleted(true);
engine->dropView(vocbase(), *this);
auto res = dropImpl();
if (!res.ok()) {
deleted(false); // not fully deleted
return res;
}
res = vocbase().dropView(id(), true); // true since caller should have checked for 'system'
if (!res.ok()) {
deleted(false); // not fully deleted
return res;
}
} catch (...) {
deleted(false); // not fully deleted
throw;
}
return res;
return arangodb::Result();
}
Result LogicalViewStorageEngine::rename(std::string&& newName, bool doSync) {

View File

@ -47,18 +47,9 @@ class Builder;
////////////////////////////////////////////////////////////////////////////////
class LogicalView : public LogicalDataSource {
public:
typedef std::shared_ptr<LogicalView> ptr;
typedef std::function<bool(TRI_voc_cid_t)> CollectionVisitor;
//////////////////////////////////////////////////////////////////////////////
/// @brief typedef for a LogicalView pre-commit callback
/// called before completing view creation
/// e.g. before persisting definition to filesystem
//////////////////////////////////////////////////////////////////////////////
typedef std::function<bool(
std::shared_ptr<LogicalView>const& view // a pointer to the created view
)> PreCommitCallback;
//////////////////////////////////////////////////////////////////////////////
/// @brief casts a specified 'LogicalView' to a provided Target type
//////////////////////////////////////////////////////////////////////////////
@ -123,16 +114,30 @@ class LogicalView : public LogicalDataSource {
static Category const& category() noexcept;
//////////////////////////////////////////////////////////////////////////////
/// @brief creates view according to a definition
/// @param preCommit called before completing view creation (IFF returns true)
/// e.g. before persisting definition to filesystem
/// @brief creates a new view according to a definition
/// @param view out-param for created view on success
/// on success non-null, on failure undefined
/// @param vocbase database where the view resides
/// @param definition the view definition
/// @return success and sets 'view' or failure
//////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<LogicalView> create(
static Result create(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice definition
);
//////////////////////////////////////////////////////////////////////////////
/// @brief instantiates an existing view according to a definition
/// @param vocbase database where the view resides
/// @param definition the view definition
/// @return view instance or nullptr on error
//////////////////////////////////////////////////////////////////////////////
static Result instantiate(
LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
velocypack::Slice definition,
bool isNew,
uint64_t planVersion = 0,
PreCommitCallback const& preCommit = PreCommitCallback() // called before
uint64_t planVersion = 0 // '0' by default for non-cluster
);
//////////////////////////////////////////////////////////////////////////////
@ -187,6 +192,13 @@ class LogicalView : public LogicalDataSource {
/// @brief a LogicalView base class for ClusterInfo view implementations
////////////////////////////////////////////////////////////////////////////////
class LogicalViewClusterInfo: public LogicalView {
public:
virtual Result drop() override final;
/*FIXME TODO add to trigger rename via vocbase or cluster-info
virtual Result rename(std::string&& newName, bool doSync) override final;
*/
protected:
LogicalViewClusterInfo(
TRI_vocbase_t& vocbase,
@ -207,6 +219,12 @@ class LogicalViewClusterInfo: public LogicalView {
velocypack::Builder& builder,
bool forPersistence
) const = 0;
protected:
//////////////////////////////////////////////////////////////////////////////
/// @brief drop implementation-specific parts of an existing view
//////////////////////////////////////////////////////////////////////////////
virtual arangodb::Result dropImpl() = 0;
};
////////////////////////////////////////////////////////////////////////////////
@ -216,12 +234,10 @@ class LogicalViewStorageEngine: public LogicalView {
public:
~LogicalViewStorageEngine() override;
arangodb::Result drop() override final;
virtual Result drop() override final;
Result rename(
std::string&& newName,
bool doSync
) override final;
// FIXME TODO rename via vocbase or cluster-info
virtual Result rename(std::string&& newName, bool doSync) override final;
arangodb::Result updateProperties(
velocypack::Slice const& properties,
@ -250,12 +266,6 @@ class LogicalViewStorageEngine: public LogicalView {
bool forPersistence
) const = 0;
//////////////////////////////////////////////////////////////////////////////
/// @brief called by view factories during view creation to persist the view
/// to the storage engine
//////////////////////////////////////////////////////////////////////////////
static arangodb::Result create(LogicalViewStorageEngine const& view);
//////////////////////////////////////////////////////////////////////////////
/// @brief drop implementation-specific parts of an existing view
//////////////////////////////////////////////////////////////////////////////
@ -272,4 +282,4 @@ class LogicalViewStorageEngine: public LogicalView {
} // namespace arangodb
#endif
#endif

View File

@ -1016,10 +1016,8 @@ void TRI_vocbase_t::inventory(
collection->getIndexesVPack(result, Index::makeFlags(), [](arangodb::Index const* idx) {
// we have to exclude the primary and the edge index here, because otherwise
// at least the MMFiles engine will try to create it
// AND exclude arangosearch indexes
return (idx->type() != arangodb::Index::TRI_IDX_TYPE_PRIMARY_INDEX &&
idx->type() != arangodb::Index::TRI_IDX_TYPE_EDGE_INDEX &&
idx->type() != arangodb::Index::TRI_IDX_TYPE_IRESEARCH_LINK);
idx->type() != arangodb::Index::TRI_IDX_TYPE_EDGE_INDEX);
});
result.add("parameters", VPackValue(VPackValueType::Object));
collection->toVelocyPackIgnore(result, { "objectId", "path", "statusString", "indexes" }, true, false);
@ -1647,92 +1645,65 @@ void TRI_vocbase_t::releaseCollection(arangodb::LogicalCollection* collection) {
std::shared_ptr<arangodb::LogicalView> TRI_vocbase_t::createView(
arangodb::velocypack::Slice parameters
) {
TRI_ASSERT(!ServerState::instance()->isCoordinator());
auto* engine = EngineSelectorFeature::ENGINE;
if (!engine) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL,
"failure to get storage engine during creation of view"
);
}
// check that the name does not contain any strange characters
if (!IsAllowedName(parameters)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_ILLEGAL_NAME);
}
std::shared_ptr<arangodb::LogicalView> view;
arangodb::LogicalView::ptr view;
auto res = LogicalView::instantiate(view, *this, parameters);
if (ServerState::instance()->isCoordinator()) {
auto* ci = ClusterInfo::instance();
if (!ci) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL,
std::string("failed to find ClusterInfo while creating view")
);
}
TRI_set_errno(TRI_ERROR_NO_ERROR); // clear error state so can get valid error below
view = LogicalView::create(*this, parameters, true);
if (!view) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_NO_ERROR == TRI_errno() ? TRI_ERROR_INTERNAL : TRI_errno(),
std::string("failed to instantiate view in agency'")
);
}
view = ci->getView(name(), std::to_string(view->id())); // refresh view from Agency
} else {
std::shared_ptr<arangodb::LogicalView> registeredView;
auto callback = [this, &registeredView](
std::shared_ptr<arangodb::LogicalView> const& view
)->bool {
TRI_ASSERT(false == !view);
RECURSIVE_WRITE_LOCKER(_dataSourceLock, _dataSourceLockWriteOwner);
auto itr = _dataSourceByName.find(view->name());
if (itr != _dataSourceByName.end()) {
events::CreateView(view->name(), TRI_ERROR_ARANGO_DUPLICATE_NAME);
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DUPLICATE_NAME);
}
registerView(basics::ConditionalLocking::DoNotLock, view);
registeredView = view;
return true;
};
READ_LOCKER(readLocker, _inventoryLock);
// Try to create a new view. This is not registered yet
TRI_set_errno(TRI_ERROR_NO_ERROR); // clear error state so can get valid error below
view = LogicalView::create(*this, parameters, true, 0, callback);
if (!view) {
auto errorNumber = TRI_ERROR_NO_ERROR == TRI_errno()
? TRI_ERROR_INTERNAL : TRI_errno();
if (registeredView) {
unregisterView(*registeredView);
}
auto name = arangodb::basics::VelocyPackHelper::getStringValue(
parameters, StaticStrings::DataSourceName, ""
);
THROW_ARANGO_EXCEPTION_MESSAGE(
errorNumber,
std::string("failed to instantiate view '") + name + "'"
);
}
events::CreateView(view->name(), TRI_ERROR_NO_ERROR);
if (DatabaseFeature::DATABASE != nullptr &&
DatabaseFeature::DATABASE->versionTracker() != nullptr) {
DatabaseFeature::DATABASE->versionTracker()->track("create view");
}
if (!res.ok() || !view) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL,
std::string("failed to instantiate view from definition: ") + parameters.toString()
);
}
// And lets open it.
if (view) {
view->open();
READ_LOCKER(readLocker, _inventoryLock);
RECURSIVE_WRITE_LOCKER(_dataSourceLock, _dataSourceLockWriteOwner);
auto itr = _dataSourceByName.find(view->name());
if (itr != _dataSourceByName.end()) {
events::CreateView(view->name(), TRI_ERROR_ARANGO_DUPLICATE_NAME);
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DUPLICATE_NAME);
}
registerView(basics::ConditionalLocking::DoNotLock, view);
try {
auto res = engine->createView(view->vocbase(), view->id(), *view);
if (!res.ok()) {
unregisterView(*view);
THROW_ARANGO_EXCEPTION_MESSAGE(res.errorNumber(), res.errorMessage());
}
} catch (...) {
unregisterView(*view);
throw;
}
events::CreateView(view->name(), TRI_ERROR_NO_ERROR);
if (DatabaseFeature::DATABASE != nullptr &&
DatabaseFeature::DATABASE->versionTracker() != nullptr) {
DatabaseFeature::DATABASE->versionTracker()->track("create view");
}
view->open(); // And lets open it.
return view;
}
@ -1741,29 +1712,24 @@ arangodb::Result TRI_vocbase_t::dropView(
TRI_voc_cid_t cid,
bool allowDropSystem
) {
TRI_ASSERT(!ServerState::instance()->isCoordinator());
auto const view = lookupView(cid);
if (!view) {
return TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND;
}
if (!allowDropSystem && view->system()) {
StorageEngine* engine = EngineSelectorFeature::ENGINE;
StorageEngine* engine = EngineSelectorFeature::ENGINE;
if (!engine) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failed to find StorageEngine while dropping view '") + view->name() + "'"
);
}
if (!engine->inRecovery()) {
return TRI_ERROR_FORBIDDEN; // prevent dropping of system views
}
if (!engine) {
return arangodb::Result(
TRI_ERROR_INTERNAL,
std::string("failed to find StorageEngine while dropping view '") + view->name() + "'"
);
}
if (ServerState::instance()->isCoordinator()) {
return view->drop(); // will internally drop view from ClusterInfo
if (!allowDropSystem && view->system() && !engine->inRecovery()) {
return TRI_ERROR_FORBIDDEN; // prevent dropping of system views
}
READ_LOCKER(readLocker, _inventoryLock);
@ -1807,7 +1773,7 @@ arangodb::Result TRI_vocbase_t::dropView(
arangodb::aql::PlanCache::instance()->invalidate(this);
arangodb::aql::QueryCache::instance()->invalidate(this);
auto res = view->drop();
auto res = engine->dropView(*this, *view);
if (!res.ok()) {
return res;

View File

@ -315,7 +315,8 @@ SECTION("test_create_drop") {
{
auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"id\" : \"42\", \"type\": \"arangosearch\", \"view\": 42 }");
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::create(logicalView, *vocbase, viewJson->slice()).ok()));
REQUIRE(logicalView);
auto const viewId = std::to_string(logicalView->planId());
CHECK("42" == viewId);
@ -390,7 +391,7 @@ SECTION("test_create_drop") {
CHECK(!arangodb::iresearch::IResearchLinkCoordinator::find(*updatedCollection, *logicalView));
// drop view
CHECK(vocbase->dropView(logicalView->planId(), false).ok());
CHECK(logicalView->drop().ok());
CHECK(nullptr == ci->getView(vocbase->name(), viewId));
// old index remains valid
@ -421,7 +422,8 @@ SECTION("test_create_drop") {
{
auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"id\":\"42\", \"type\": \"arangosearch\", \"view\": 42 }");
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::create(logicalView, *vocbase, viewJson->slice()).ok()));
REQUIRE(logicalView);
auto const viewId = std::to_string(logicalView->planId());
CHECK("42" == viewId);
@ -513,4 +515,4 @@ SECTION("test_create_drop") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -39,6 +39,7 @@
#include "Aql/Function.h"
#include "Aql/SortCondition.h"
#include "Basics/ArangoGlobalContext.h"
#include "Basics/error.h"
#include "Basics/files.h"
#include "Utils/ExecContext.h"
@ -266,8 +267,9 @@ SECTION("test_defaults") {
// view definition with LogicalView (for persistence)
{
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto view = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
arangodb::LogicalView::ptr view;
CHECK((arangodb::iresearch::IResearchView::factory().create(view, vocbase, json->slice()).ok()));
CHECK((false == !view));
arangodb::iresearch::IResearchViewMeta expectedMeta;
@ -297,7 +299,8 @@ SECTION("test_defaults") {
// view definition with LogicalView
{
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto view = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr view;
CHECK((arangodb::iresearch::IResearchView::factory().create(view, vocbase, json->slice()).ok()));
CHECK((false == !view));
arangodb::iresearch::IResearchViewMeta expectedMeta;
@ -322,96 +325,42 @@ SECTION("test_defaults") {
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// new view definition with links
// new view definition with links to missing collections
{
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": 101, \"links\": { \"testCollection\": {} } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
CHECK((true == !vocbase.lookupView("testView")));
arangodb::LogicalView::ptr view;
auto res = arangodb::iresearch::IResearchView::factory().create(view, vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == res.errorNumber()));
CHECK((true == !vocbase.lookupView("testView")));
}
// new view definition with links with invalid definition
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\", \"id\": 100 }");
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": 101, \"links\": { \"testCollection\": {} } }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": 101, \"links\": { \"testCollection\": 42 } }");
Vocbase 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");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
CHECK((nullptr != logicalCollection));
CHECK((true == !vocbase.lookupView("testView")));
CHECK((true == logicalCollection->getIndexes().empty()));
auto logicalView = vocbase.createView(viewJson->slice());
REQUIRE((false == !logicalView));
std::set<TRI_voc_cid_t> cids;
logicalView->visitCollections([&cids](TRI_voc_cid_t cid)->bool { cids.emplace(cid); return true; });
CHECK((1 == cids.size()));
CHECK((false == logicalCollection->getIndexes().empty()));
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 1 == tmpSlice.length()));
CHECK((true == tmpSlice.hasKey("testCollection")));
arangodb::LogicalView::ptr view;
auto res = arangodb::iresearch::IResearchView::factory().create(view, vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_BAD_PARAMETER == res.errorNumber()));
CHECK((true == !vocbase.lookupView("testView")));
}
// view definition with links
// new view definition with links (collection not authorized)
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\", \"id\": 100 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": { \"testCollection\": {} } }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"links\": { \"testCollection\": {} } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = vocbase.createView(viewCreateJson->slice());
REQUIRE((false == !logicalView));
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
CHECK((logicalView->updateProperties(viewUpdateJson->slice(), true, false).ok()));
CHECK((false == logicalCollection->getIndexes().empty()));
CHECK((false == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK((false == slice.hasKey("deleted")));
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 1 == tmpSlice.length()));
CHECK((true == tmpSlice.hasKey("testCollection")));
}
// view definition with links (collection not authorized)
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\", \"id\": 100 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": { \"testCollection\": {} } }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = vocbase.createView(viewCreateJson->slice());
REQUIRE((false == !logicalView));
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
CHECK((logicalView->updateProperties(viewUpdateJson->slice(), true, false).ok()));
CHECK((false == logicalCollection->getIndexes().empty()));
CHECK((false == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "",
@ -426,10 +375,51 @@ SECTION("test_defaults") {
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto resetUserManager = irs::make_finally([userManager]()->void{ userManager->removeAllUsers(); });
CHECK((true == !vocbase.lookupView("testView")));
arangodb::LogicalView::ptr view;
auto res = arangodb::iresearch::IResearchView::factory().create(view, vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_FORBIDDEN == res.errorNumber()));
CHECK((true == !vocbase.lookupView("testView")));
}
// new view definition with links
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\", \"id\": 100 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": 101, \"links\": { \"testCollection\": {} } }");
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
CHECK((nullptr != logicalCollection));
CHECK((true == !vocbase.lookupView("testView")));
CHECK((true == logicalCollection->getIndexes().empty()));
arangodb::LogicalView::ptr logicalView;
CHECK((arangodb::iresearch::IResearchView::factory().create(logicalView, vocbase, viewCreateJson->slice()).ok()));
REQUIRE((false == !logicalView));
std::set<TRI_voc_cid_t> cids;
logicalView->visitCollections([&cids](TRI_voc_cid_t cid)->bool { cids.emplace(cid); return true; });
CHECK((1 == cids.size()));
CHECK((false == logicalCollection->getIndexes().empty()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
CHECK((TRI_ERROR_FORBIDDEN == logicalView->toVelocyPack(builder, true, false).errorNumber()));
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((false == slice.hasKey("deleted")));
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 1 == tmpSlice.length()));
CHECK((true == tmpSlice.hasKey("testCollection")));
}
}
@ -529,7 +519,7 @@ SECTION("test_drop") {
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((false == !vocbase.lookupView("testView")));
CHECK((true == TRI_IsDirectory(dataPath.c_str())));
CHECK((true == vocbase.dropView(view->id(), false).ok()));
CHECK((true == view->drop().ok()));
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == !vocbase.lookupView("testView")));
CHECK((false == TRI_IsDirectory(dataPath.c_str())));
@ -586,7 +576,7 @@ SECTION("test_drop_with_link") {
user.grantCollection(vocbase.name(), "testCollection", arangodb::auth::Level::NONE); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((TRI_ERROR_FORBIDDEN == vocbase.dropView(view->id(), false).errorNumber()));
CHECK((TRI_ERROR_FORBIDDEN == view->drop().errorNumber()));
CHECK((false == logicalCollection->getIndexes().empty()));
CHECK((false == !vocbase.lookupView("testView")));
CHECK((true == TRI_IsDirectory(dataPath.c_str())));
@ -599,7 +589,7 @@ SECTION("test_drop_with_link") {
user.grantCollection(vocbase.name(), "testCollection", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((true == vocbase.dropView(view->id(), false).ok()));
CHECK((true == view->drop().ok()));
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == !vocbase.lookupView("testView")));
CHECK((false == TRI_IsDirectory(dataPath.c_str())));
@ -1457,7 +1447,8 @@ SECTION("test_insert") {
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1501,7 +1492,8 @@ SECTION("test_insert") {
StorageEngineMock::inRecoveryResult = true;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::inRecoveryResult = before; });
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1545,7 +1537,8 @@ SECTION("test_insert") {
{
StorageEngineMock::inRecoveryResult = false;
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1629,7 +1622,8 @@ SECTION("test_insert") {
{
StorageEngineMock::inRecoveryResult = false;
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1672,7 +1666,8 @@ SECTION("test_insert") {
{
StorageEngineMock::inRecoveryResult = false;
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1713,7 +1708,8 @@ SECTION("test_insert") {
{
StorageEngineMock::inRecoveryResult = false;
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1757,7 +1753,8 @@ SECTION("test_insert") {
{
StorageEngineMock::inRecoveryResult = false;
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto viewImpl = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr viewImpl;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(viewImpl, vocbase, json->slice(), 0).ok()));
CHECK((false == !viewImpl));
auto* view = dynamic_cast<arangodb::iresearch::IResearchView*>(viewImpl.get());
CHECK((nullptr != view));
@ -1806,7 +1803,8 @@ SECTION("test_open") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"id\": 123, \"name\": \"testView\", \"type\": \"testType\" }");
CHECK((false == TRI_IsDirectory(dataPath.c_str())));
auto view = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(view, vocbase, json->slice(), 0).ok()));
CHECK((false == !view));
CHECK((false == TRI_IsDirectory(dataPath.c_str())));
view->open();
@ -2515,7 +2513,7 @@ SECTION("test_unregister_link") {
view->visitCollections([&cids](TRI_voc_cid_t cid)->bool { cids.emplace(cid); return true; });
CHECK((1 == cids.size()));
CHECK((false == !vocbase.lookupView("testView")));
CHECK((true == vocbase.dropView(view->id(), false).ok()));
CHECK((true == view->drop().ok()));
CHECK((true == !vocbase.lookupView("testView")));
CHECK((nullptr != vocbase.lookupCollection("testCollection")));
CHECK((true == vocbase.dropCollection(logicalCollection->id(), true, -1).ok()));
@ -2549,7 +2547,8 @@ SECTION("test_unregister_link") {
// create a new view with same ID to validate links
{
auto json = arangodb::velocypack::Parser::fromJson("{}");
auto view = arangodb::iresearch::IResearchView::make(vocbase, viewJson->slice(), true, 0);
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(view, vocbase, viewJson->slice(), 0).ok()));
REQUIRE((false == !view));
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(view.get());
REQUIRE((nullptr != viewImpl));
@ -2577,7 +2576,8 @@ SECTION("test_self_token") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\" }");
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto view = arangodb::iresearch::IResearchView::make(vocbase, json->slice(), true, 0);
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(view, vocbase, json->slice(), 0).ok()));
CHECK((false == !view));
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(view.get());
REQUIRE((nullptr != viewImpl));
@ -2598,7 +2598,8 @@ SECTION("test_tracked_cids") {
{
s.engine.views.clear();
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto view = arangodb::iresearch::IResearchView::make(vocbase, viewJson->slice(), true, 0);
arangodb::LogicalView::ptr view;
CHECK((arangodb::iresearch::IResearchView::factory().create(view, vocbase, viewJson->slice()).ok()));
CHECK((nullptr != view));
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(view.get());
REQUIRE((nullptr != viewImpl));
@ -2615,8 +2616,10 @@ SECTION("test_tracked_cids") {
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = arangodb::iresearch::IResearchView::make(vocbase, viewJson->slice(), true, 0);
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(logicalView, vocbase, viewJson->slice(), 0).ok()));
REQUIRE((false == !logicalView));
s.engine.createView(vocbase, logicalView->id(), *logicalView); // ensure link can find view
StorageEngineMock(s.server).registerView(vocbase, logicalView); // ensure link can find view
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(logicalView.get());
REQUIRE((nullptr != viewImpl));
@ -2643,8 +2646,10 @@ SECTION("test_tracked_cids") {
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = arangodb::iresearch::IResearchView::make(vocbase, viewJson->slice(), true, 0);
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(logicalView, vocbase, viewJson->slice(), 0).ok()));
REQUIRE((false == !logicalView));
s.engine.createView(vocbase, logicalView->id(), *logicalView); // ensure link can find view
StorageEngineMock(s.server).registerView(vocbase, logicalView); // ensure link can find view
auto* viewImpl = dynamic_cast<arangodb::iresearch::IResearchView*>(logicalView.get());
REQUIRE((nullptr != viewImpl));
@ -3384,6 +3389,195 @@ SECTION("test_update_overwrite") {
}
}
// test rollback on meta modification failure (as an example invalid value for 'cleanupIntervalStep')
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 0.123 }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(updateJson->slice(), false, false).errorNumber()));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));
CHECK((false == slice.hasKey("links")));
}
}
// modify meta params with links to missing collections
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": {} } }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == logicalView->updateProperties(updateJson->slice(), false, false).errorNumber()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));
CHECK((false == slice.hasKey("links")));
}
}
// modify meta params with links with invalid definition
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
CHECK((true == logicalCollection->getIndexes().empty()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": 42 } }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(updateJson->slice(), false, false).errorNumber()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));
CHECK((false == slice.hasKey("links")));
}
}
// modify meta params with links
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
@ -4397,26 +4591,24 @@ SECTION("test_update_partial") {
// test rollback on meta modification failure (as an example invalid value for 'cleanupIntervalStep')
{
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto view = vocbase.createView(createJson->slice());
REQUIRE((false == !view));
REQUIRE(view->category() == arangodb::LogicalView::category());
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson(std::string() + "{ \
\"cleanupIntervalStep\": 0.123 \
}");
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 0.123 }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_BAD_PARAMETER == view->updateProperties(updateJson->slice(), true, false).errorNumber()));
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(updateJson->slice(), true, false).errorNumber()));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, false);
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
@ -4424,11 +4616,11 @@ SECTION("test_update_partial") {
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK(slice.isObject());
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK(slice.get("deleted").isNone()); // no system properties
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
@ -4439,7 +4631,7 @@ SECTION("test_update_partial") {
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, true);
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
@ -4447,10 +4639,138 @@ SECTION("test_update_partial") {
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK(slice.isObject());
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));
CHECK((false == slice.hasKey("links")));
}
}
// modify meta params with links to missing collections
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": {} } }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == logicalView->updateProperties(updateJson->slice(), true, false).errorNumber()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));
CHECK((false == slice.hasKey("links")));
}
}
// modify meta params with links with invalid definition
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE((nullptr != logicalCollection));
auto logicalView = vocbase.createView(createJson->slice());
REQUIRE((false == !logicalView));
REQUIRE((logicalView->category() == arangodb::LogicalView::category()));
CHECK((true == logicalCollection->getIndexes().empty()));
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::iresearch::IResearchViewMetaState expectedMetaState;
auto updateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": 42 } }");
expectedMeta._cleanupIntervalStep = 52;
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(updateJson->slice(), true, false).errorNumber()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
// not for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((7U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.get("deleted").isNone())); // no system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
auto tmpSlice = slice.get("links");
CHECK((true == tmpSlice.isObject() && 0 == tmpSlice.length()));
}
// for persistence
{
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
arangodb::iresearch::IResearchViewMetaState metaState;
std::string error;
CHECK((slice.isObject()));
CHECK((11U == slice.length()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("deleted") && slice.get("deleted").isBoolean() && false == slice.get("deleted").getBoolean())); // has system properties
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState));

View File

@ -261,8 +261,8 @@ SECTION("test_rename") {
"{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\", \"collections\": [1,2,3] }");
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR, 1, "testVocbase");
auto view = arangodb::LogicalView::create(vocbase, json->slice(), false); // false == do not persist
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::instantiate(view, vocbase, json->slice(), 0).ok()));
CHECK(nullptr != view);
CHECK(nullptr != std::dynamic_pointer_cast<arangodb::iresearch::IResearchViewCoordinator>(view));
CHECK(0 == view->planVersion());
@ -281,7 +281,8 @@ SECTION("test_rename") {
SECTION("visit_collections") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\" }");
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR, 1, "testVocbase");
auto logicalView = arangodb::LogicalView::create(vocbase, json->slice(), false); // false == do not persist
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::instantiate(logicalView, vocbase, json->slice(), 0).ok()));
auto* view = dynamic_cast<arangodb::iresearch::IResearchViewCoordinator*>(logicalView.get());
CHECK(nullptr != view);
@ -307,106 +308,234 @@ SECTION("visit_collections") {
}
SECTION("test_defaults") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\" }");
auto* database = arangodb::DatabaseFeature::DATABASE;
REQUIRE(nullptr != database);
auto* ci = arangodb::ClusterInfo::instance();
REQUIRE((nullptr != ci));
TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature
std::string error;
// create database
{
// simulate heartbeat thread
REQUIRE((TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase", vocbase)));
REQUIRE((nullptr != vocbase));
CHECK(("testDatabase" == vocbase->name()));
CHECK((TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR == vocbase->type()));
CHECK((1 == vocbase->id()));
CHECK((TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator(
vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0
)));
CHECK(("no error" == error));
}
// view definition with LogicalView (for persistence)
Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR, 1, "testVocbase");
auto view = arangodb::LogicalView::create(vocbase, json->slice(), false); // false == do not persist
CHECK(nullptr != view);
CHECK(nullptr != std::dynamic_pointer_cast<arangodb::iresearch::IResearchViewCoordinator>(view));
CHECK(0 == view->planVersion());
CHECK("testView" == view->name());
CHECK(false == view->deleted());
CHECK(1 == view->id());
CHECK(arangodb::iresearch::DATA_SOURCE_TYPE == view->type());
CHECK(arangodb::LogicalView::category() == view->category());
CHECK(&vocbase == &view->vocbase());
// visit default view
CHECK(true == view->visitCollections([](TRI_voc_cid_t) { return false; }));
// +system, +properties
{
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR, 1, "testVocbase");
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::instantiate(view, vocbase, json->slice(), 0).ok()));
CHECK((10U == slice.length()));
CHECK((slice.hasKey("globallyUniqueId") && slice.get("globallyUniqueId").isString() && false == slice.get("globallyUniqueId").copyString().empty()));
CHECK(slice.get("id").copyString() == "1");
CHECK((slice.hasKey("isSystem") && slice.get("isSystem").isBoolean() && false == slice.get("isSystem").getBoolean()));
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK(slice.hasKey("planId"));
CHECK(false == slice.get("deleted").getBool());
CHECK((!slice.hasKey("links"))); // for persistence so no links
CHECK((meta.init(slice, error) && expectedMeta == meta));
CHECK((nullptr != view));
CHECK((nullptr != std::dynamic_pointer_cast<arangodb::iresearch::IResearchViewCoordinator>(view)));
CHECK((0 == view->planVersion()));
CHECK(("testView" == view->name()));
CHECK((false == view->deleted()));
CHECK((1 == view->id()));
CHECK((arangodb::iresearch::DATA_SOURCE_TYPE == view->type()));
CHECK((arangodb::LogicalView::category() == view->category()));
CHECK((&vocbase == &view->vocbase()));
// visit default view
CHECK((true == view->visitCollections([](TRI_voc_cid_t) { return false; })));
// +system, +properties
{
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, true);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
CHECK((10U == slice.length()));
CHECK((slice.hasKey("globallyUniqueId") && slice.get("globallyUniqueId").isString() && false == slice.get("globallyUniqueId").copyString().empty()));
CHECK((slice.get("id").copyString() == "1"));
CHECK((slice.hasKey("isSystem") && slice.get("isSystem").isBoolean() && false == slice.get("isSystem").getBoolean()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((slice.hasKey("planId")));
CHECK((false == slice.get("deleted").getBool()));
CHECK((!slice.hasKey("links"))); // for persistence so no links
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// -system, +properties
{
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
CHECK((7U == slice.length()));
CHECK((slice.get("id").copyString() == "1"));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((!slice.hasKey("planId")));
CHECK((!slice.hasKey("deleted")));
CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length()));
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// -system, -properties
{
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, false, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
CHECK((3 == slice.length()));
CHECK((slice.get("id").copyString() == "1"));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((!slice.hasKey("planId")));
CHECK((!slice.hasKey("deleted")));
CHECK((!slice.hasKey("properties")));
}
// +system, -properties
{
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, false, true);
builder.close();
auto slice = builder.slice();
CHECK((7 == slice.length()));
CHECK((slice.hasKey("globallyUniqueId") && slice.get("globallyUniqueId").isString() && false == slice.get("globallyUniqueId").copyString().empty()));
CHECK((slice.get("id").copyString() == "1"));
CHECK((slice.hasKey("isSystem") && slice.get("isSystem").isBoolean() && false == slice.get("isSystem").getBoolean()));
CHECK((slice.get("name").copyString() == "testView"));
CHECK((slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name()));
CHECK((false == slice.get("deleted").getBool()));
CHECK((slice.hasKey("planId")));
CHECK((!slice.hasKey("properties")));
}
}
// -system, +properties
// new view definition with links to missing collections
{
arangodb::iresearch::IResearchViewMeta expectedMeta;
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, true, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\", \"links\": { \"testCollection\": {} } }");
auto viewId = "testView";
CHECK((7U == slice.length()));
CHECK(slice.get("id").copyString() == "1");
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK(!slice.hasKey("planId"));
CHECK(!slice.hasKey("deleted"));
CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length()));
CHECK((meta.init(slice, error) && expectedMeta == meta));
arangodb::LogicalView::ptr logicalView;
auto res = arangodb::iresearch::IResearchViewCoordinator::factory().create(logicalView, *vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == res.errorNumber()));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((true == !logicalView));
}
// -system, -properties
// new view definition with links with invalid definition
{
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, false, false);
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
std::string error;
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\", \"links\": { \"testCollection\": 42 } }");
auto collectionId = std::to_string(1);
auto viewId = "testView";
CHECK(3 == slice.length());
CHECK(slice.get("id").copyString() == "1");
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK(!slice.hasKey("planId"));
CHECK(!slice.hasKey("deleted"));
CHECK(!slice.hasKey("properties"));
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createCollectionCoordinator(vocbase->name(), collectionId, 0, 1, false, collectionJson->slice(), error, 0.0)));
CHECK((error.empty()));
auto logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
auto dropLogicalCollection = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &collectionId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropCollectionCoordinator(vocbase->name(), collectionId, error, 0); });
arangodb::LogicalView::ptr logicalView;
auto res = arangodb::iresearch::IResearchViewCoordinator::factory().create(logicalView, *vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_BAD_PARAMETER == res.errorNumber()));
logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((true == !logicalView));
CHECK((true == logicalCollection->getIndexes().empty()));
}
// +system, -properties
// new view definition with links (collection not authorized)
{
arangodb::velocypack::Builder builder;
builder.openObject();
view->toVelocyPack(builder, false, true);
builder.close();
auto slice = builder.slice();
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\", \"links\": { \"testCollection\": {} } }");
auto collectionId = std::to_string(1);
auto viewId = "testView";
CHECK(7 == slice.length());
CHECK((slice.hasKey("globallyUniqueId") && slice.get("globallyUniqueId").isString() && false == slice.get("globallyUniqueId").copyString().empty()));
CHECK(slice.get("id").copyString() == "1");
CHECK((slice.hasKey("isSystem") && slice.get("isSystem").isBoolean() && false == slice.get("isSystem").getBoolean()));
CHECK(slice.get("name").copyString() == "testView");
CHECK(slice.get("type").copyString() == arangodb::iresearch::DATA_SOURCE_TYPE.name());
CHECK(false == slice.get("deleted").getBool());
CHECK(slice.hasKey("planId"));
CHECK(!slice.hasKey("properties"));
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createCollectionCoordinator(vocbase->name(), collectionId, 0, 1, false, collectionJson->slice(), error, 0.0)));
CHECK((error.empty()));
auto logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
auto dropLogicalCollection = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &collectionId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropCollectionCoordinator(vocbase->name(), collectionId, error, 0); });
struct ExecContext: public arangodb::ExecContext {
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "",
arangodb::auth::Level::NONE, arangodb::auth::Level::NONE) {}
} execContext;
arangodb::ExecContextScope execContextScope(&execContext);
auto* authFeature = arangodb::AuthenticationFeature::instance();
auto* userManager = authFeature->userManager();
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
userManager->setQueryRegistry(&queryRegistry);
auto resetUserManager = std::shared_ptr<arangodb::auth::UserManager>(userManager, [](arangodb::auth::UserManager* ptr)->void { ptr->removeAllUsers(); });
arangodb::LogicalView::ptr logicalView;
auto res = arangodb::iresearch::IResearchViewCoordinator::factory().create(logicalView, *vocbase, viewCreateJson->slice());
CHECK((TRI_ERROR_FORBIDDEN == res.errorNumber()));
logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((true == !logicalView));
CHECK((true == logicalCollection->getIndexes().empty()));
}
// new view definition with links
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\", \"links\": { \"testCollection\": {} } }");
auto collectionId = std::to_string(1);
auto viewId = "testView";
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createCollectionCoordinator(vocbase->name(), collectionId, 0, 1, false, collectionJson->slice(), error, 0.0)));
CHECK((error.empty()));
auto logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
auto dropLogicalCollection = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &collectionId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropCollectionCoordinator(vocbase->name(), collectionId, error, 0); });
// simulate heartbeat thread (create index in current)
{
auto const path = "/Current/Collections/" + vocbase->name() + "/" + std::to_string(logicalCollection->id());
auto const value = arangodb::velocypack::Parser::fromJson("{ \"shard-id-does-not-matter\": { \"indexes\" : [ { \"id\": \"1\" } ] } }");
CHECK((arangodb::AgencyComm().setValue(path, value->slice(), 0.0).successful()));
}
arangodb::LogicalView::ptr logicalView;
CHECK((arangodb::iresearch::IResearchViewCoordinator::factory().create(logicalView, *vocbase, viewCreateJson->slice()).ok()));
logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
CHECK((false == logicalCollection->getIndexes().empty()));
CHECK((false == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
}
}
@ -513,11 +642,7 @@ SECTION("test_create_drop_view") {
CHECK(nullptr == ci->getView(vocbase->name(), view->name()));
// drop already dropped view
{
auto const res = view->drop();
CHECK(res.fail());
//CHECK(TRI_ERROR_... == res.errorNumber()) FIXME
}
CHECK((view->drop().ok()));
}
// create and drop view
@ -562,11 +687,7 @@ SECTION("test_create_drop_view") {
CHECK(nullptr == ci->getView(vocbase->name(), view->name()));
// drop already dropped view
{
auto const res = view->drop();
CHECK(res.fail());
//CHECK(TRI_ERROR_... == res.errorNumber()) FIXME
}
CHECK((view->drop().ok()));
}
}
@ -657,7 +778,7 @@ SECTION("test_drop_with_link") {
user.grantCollection(vocbase->name(), "testCollection", arangodb::auth::Level::NONE); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((TRI_ERROR_FORBIDDEN == vocbase->dropView(logicalView->id(), false).errorNumber()));
CHECK((TRI_ERROR_FORBIDDEN == logicalView->drop().errorNumber()));
logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
CHECK((false == logicalCollection->getIndexes().empty()));
@ -671,7 +792,7 @@ SECTION("test_drop_with_link") {
user.grantCollection(vocbase->name(), "testCollection", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
CHECK((TRI_ERROR_NO_ERROR == vocbase->dropView(logicalView->id(), false).errorNumber()));
CHECK((true == logicalView->drop().ok()));
logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
CHECK((true == logicalCollection->getIndexes().empty()));
@ -933,7 +1054,8 @@ SECTION("test_update_links_partial_remove") {
// create view
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto view = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::create(view, *vocbase, viewJson->slice()).ok()));
REQUIRE(view);
auto const viewId = std::to_string(view->planId());
CHECK("42" == viewId);
@ -1497,7 +1619,8 @@ SECTION("test_update_links_partial_add") {
// create view
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto view = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::create(view, *vocbase, viewJson->slice()).ok()));
REQUIRE(view);
auto const viewId = std::to_string(view->planId());
CHECK("42" == viewId);
@ -1983,7 +2106,8 @@ SECTION("test_update_links_partial_add") {
auto const collectionId = "1";
logicalCollection1 = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection1));
auto logicalView = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::create(logicalView, *vocbase, viewJson->slice()).ok()));
REQUIRE((false == !logicalView));
CHECK((true == logicalCollection1->getIndexes().empty()));
@ -2103,7 +2227,8 @@ SECTION("test_update_links_replace") {
// create view
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto view = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::create(view, *vocbase, viewJson->slice()).ok()));
REQUIRE(view);
auto const viewId = std::to_string(view->planId());
CHECK("42" == viewId);
@ -2670,7 +2795,8 @@ SECTION("test_update_links_clear") {
// create view
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto view = vocbase->createView(viewJson->slice());
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::LogicalView::create(view, *vocbase, viewJson->slice()).ok()));
REQUIRE(view);
auto const viewId = std::to_string(view->planId());
CHECK("42" == viewId);
@ -3071,7 +3197,9 @@ SECTION("test_drop_link") {
// update link
{
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto view = std::dynamic_pointer_cast<arangodb::iresearch::IResearchViewCoordinator>(vocbase->createView(viewJson->slice()));
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::create(logicalView, *vocbase, viewJson->slice()).ok()));
auto view = std::dynamic_pointer_cast<arangodb::iresearch::IResearchViewCoordinator>(logicalView);
REQUIRE(view);
auto const viewId = std::to_string(view->planId());
CHECK("42" == viewId);
@ -3259,7 +3387,8 @@ SECTION("test_drop_link") {
auto const collectionId = "1";
auto logicalCollection1 = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection1));
auto logicalView = vocbase->createView(viewCreateJson->slice());
arangodb::LogicalView::ptr logicalView;
REQUIRE((arangodb::LogicalView::create(logicalView, *vocbase, viewCreateJson->slice()).ok()));
REQUIRE((false == !logicalView));
auto const viewId = std::to_string(logicalView->planId());
@ -3315,6 +3444,83 @@ SECTION("test_update_overwrite") {
CHECK(("no error" == error));
}
// modify meta params with links to missing collections
{
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": { \"testCollection\": {} } }");
auto viewId = std::to_string(42);
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), viewId, viewCreateJson->slice(), error)));
CHECK((error.empty()));
auto logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
auto dropLogicalView = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &viewId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropViewCoordinator(vocbase->name(), viewId, error); });
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::iresearch::IResearchViewMeta expectedMeta;
expectedMeta._cleanupIntervalStep = 10;
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == logicalView->updateProperties(viewUpdateJson->slice(), false, false).errorNumber()));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true); // 'forPersistence' to avoid auth check
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
error.clear();
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// modify meta params with links with invalid definition
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": { \"testCollection\": 42 } }");
auto collectionId = std::to_string(1);
auto viewId = std::to_string(42);
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createCollectionCoordinator(vocbase->name(), collectionId, 0, 1, false, collectionJson->slice(), error, 0.0)));
CHECK((error.empty()));
auto logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
auto dropLogicalCollection = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &collectionId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropCollectionCoordinator(vocbase->name(), collectionId, error, 0); });
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), viewId, viewCreateJson->slice(), error)));
CHECK((error.empty()));
auto logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
auto dropLogicalView = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &viewId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropViewCoordinator(vocbase->name(), viewId, error); });
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::iresearch::IResearchViewMeta expectedMeta;
expectedMeta._cleanupIntervalStep = 10;
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(viewUpdateJson->slice(), false, false).errorNumber()));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true); // 'forPersistence' to avoid auth check
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
error.clear();
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// modify meta params with links (collection not authorized)
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
@ -3815,6 +4021,83 @@ SECTION("test_update_partial") {
CHECK(("no error" == error));
}
// modify meta params with links to missing collections
{
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": {} } }");
auto viewId = std::to_string(42);
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), viewId, viewCreateJson->slice(), error)));
CHECK((error.empty()));
auto logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
auto dropLogicalView = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &viewId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropViewCoordinator(vocbase->name(), viewId, error); });
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::iresearch::IResearchViewMeta expectedMeta;
expectedMeta._cleanupIntervalStep = 10;
CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == logicalView->updateProperties(viewUpdateJson->slice(), true, false).errorNumber()));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true); // 'forPersistence' to avoid auth check
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
error.clear();
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// modify meta params with links with invalid definition
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1, \"type\": 1 }");
auto viewCreateJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"id\": \"42\", \"type\": \"arangosearch\" }");
auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"cleanupIntervalStep\": 62, \"links\": { \"testCollection\": 42 } }");
auto collectionId = std::to_string(1);
auto viewId = std::to_string(42);
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createCollectionCoordinator(vocbase->name(), collectionId, 0, 1, false, collectionJson->slice(), error, 0.0)));
CHECK((error.empty()));
auto logicalCollection = ci->getCollection(vocbase->name(), collectionId);
REQUIRE((false == !logicalCollection));
auto dropLogicalCollection = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &collectionId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropCollectionCoordinator(vocbase->name(), collectionId, error, 0); });
error.clear();
CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), viewId, viewCreateJson->slice(), error)));
CHECK((error.empty()));
auto logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
auto dropLogicalView = std::shared_ptr<arangodb::ClusterInfo>(ci, [vocbase, &viewId](arangodb::ClusterInfo* ci)->void { std::string error; ci->dropViewCoordinator(vocbase->name(), viewId, error); });
CHECK((true == logicalCollection->getIndexes().empty()));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::iresearch::IResearchViewMeta expectedMeta;
expectedMeta._cleanupIntervalStep = 10;
CHECK((TRI_ERROR_BAD_PARAMETER == logicalView->updateProperties(viewUpdateJson->slice(), true, false).errorNumber()));
logicalView = ci->getView(vocbase->name(), viewId);
REQUIRE((false == !logicalView));
CHECK((true == logicalView->visitCollections([](TRI_voc_cid_t)->bool { return false; })));
arangodb::velocypack::Builder builder;
builder.openObject();
logicalView->toVelocyPack(builder, true, true); // 'forPersistence' to avoid auth check
builder.close();
auto slice = builder.slice();
arangodb::iresearch::IResearchViewMeta meta;
error.clear();
CHECK((meta.init(slice, error) && expectedMeta == meta));
}
// modify meta params with links (collection not authorized)
{
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"1\", \"planId\": \"1\", \"name\": \"testCollection\", \"replicationFactor\": 1 }");
@ -4391,4 +4674,4 @@ SECTION("IResearchViewNode::createBlock") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -195,14 +195,34 @@ TEST_CASE("IResearchViewDBServerTest", "[cluster][iresearch][iresearch-view]") {
(void)(s);
SECTION("test_drop") {
auto* database = arangodb::DatabaseFeature::DATABASE;
REQUIRE(nullptr != database);
auto* ci = arangodb::ClusterInfo::instance();
REQUIRE(nullptr != ci);
std::string error;
TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature
// create database
{
// simulate heartbeat thread
REQUIRE(TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase", vocbase));
REQUIRE(nullptr != vocbase);
CHECK("testDatabase" == vocbase->name());
CHECK(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL == vocbase->type());
CHECK(1 == vocbase->id());
CHECK(TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator(
vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0
));
CHECK("no error" == error);
}
// drop empty
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -214,8 +234,8 @@ SECTION("test_drop") {
{
auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -225,8 +245,8 @@ SECTION("test_drop") {
auto jsonShard = arangodb::velocypack::Parser::fromJson(
"{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }"
);
CHECK((true == !vocbase.lookupView(shardViewName)));
auto shardView = vocbase.createView(jsonShard->slice());
CHECK((true == !vocbase->lookupView(shardViewName)));
auto shardView = vocbase->createView(jsonShard->slice());
CHECK(shardView);
auto view = impl->ensure(123);
@ -235,9 +255,9 @@ SECTION("test_drop") {
CHECK(view == shardView);
static auto visitor = [](TRI_voc_cid_t)->bool { return false; };
CHECK((false == impl->visitCollections(visitor)));
CHECK((false == !vocbase.lookupView(view->id())));
CHECK((false == !vocbase->lookupView(view->id())));
CHECK((true == impl->drop().ok()));
CHECK((true == !vocbase.lookupView(viewId)));
CHECK((true == !vocbase->lookupView(viewId)));
CHECK((true == impl->visitCollections(visitor)));
}
@ -245,8 +265,8 @@ SECTION("test_drop") {
{
auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -256,8 +276,8 @@ SECTION("test_drop") {
auto jsonShard = arangodb::velocypack::Parser::fromJson(
"{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }"
);
CHECK((true == !vocbase.lookupView(shardViewName)));
auto shardView = vocbase.createView(jsonShard->slice());
CHECK((true == !vocbase->lookupView(shardViewName)));
auto shardView = vocbase->createView(jsonShard->slice());
CHECK(shardView);
auto view = impl->ensure(123);
@ -265,14 +285,14 @@ SECTION("test_drop") {
CHECK(view == shardView);
static auto visitor = [](TRI_voc_cid_t)->bool { return false; };
CHECK((false == impl->visitCollections(visitor)));
CHECK((false == !vocbase.lookupView(view->id())));
CHECK((false == !vocbase->lookupView(view->id())));
auto before = StorageEngineMock::before;
auto restore = irs::make_finally([&before]()->void { StorageEngineMock::before = before; });
StorageEngineMock::before = []()->void { throw std::exception(); };
CHECK_THROWS((impl->drop()));
CHECK((false == !vocbase.lookupView(view->id())));
CHECK((false == !vocbase->lookupView(view->id())));
CHECK((false == impl->visitCollections(visitor)));
}
}
@ -280,7 +300,8 @@ SECTION("test_drop") {
SECTION("test_drop_cid") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -349,7 +370,8 @@ SECTION("test_drop_database") {
SECTION("test_ensure") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"collections\": [ 3, 4, 5 ] }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -377,7 +399,8 @@ SECTION("test_make") {
auto const wiewId = ci->uniqid() + 1; // +1 because LogicalView creation will generate a new ID
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -396,7 +419,8 @@ SECTION("test_make") {
auto json = arangodb::velocypack::Parser::fromJson("{ \"id\": 100, \"name\": \"_iresearch_123_456\", \"type\": \"arangosearch\", \"isSystem\": true }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
CHECK((true == !vocbase.lookupView("testView")));
auto view = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr view;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(view, vocbase, json->slice(), 42).ok()));
CHECK((false == !view));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchView*>(view.get());
CHECK((nullptr != impl));
@ -419,7 +443,8 @@ SECTION("test_open") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -435,7 +460,8 @@ SECTION("test_open") {
std::string dataPath = (((irs::utf8_path()/=s.testFilesystemPath)/=std::string("databases"))/=std::string("arangosearch-123")).utf8();
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -483,7 +509,8 @@ SECTION("test_query") {
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }");
auto logicalCollection = vocbase.createCollection(collectionJson->slice());
REQUIRE(nullptr != logicalCollection);
auto logicalWiew = vocbase.createView(createJson->slice());
arangodb::LogicalView::ptr logicalWiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(logicalWiew, vocbase, createJson->slice(), 42).ok()));
REQUIRE((false == !logicalWiew));
auto* wiewImpl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(logicalWiew.get());
REQUIRE((false == !wiewImpl));
@ -736,7 +763,8 @@ SECTION("test_rename") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -775,7 +803,8 @@ SECTION("test_rename") {
auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -826,7 +855,8 @@ SECTION("test_toVelocyPack") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -847,7 +877,8 @@ SECTION("test_toVelocyPack") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -868,7 +899,8 @@ SECTION("test_toVelocyPack") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -1376,7 +1408,8 @@ SECTION("test_visitCollections") {
{
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -1390,7 +1423,8 @@ SECTION("test_visitCollections") {
auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID
auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
auto wiew = arangodb::iresearch::IResearchViewDBServer::make(vocbase, json->slice(), true, 42);
arangodb::LogicalView::ptr wiew;
REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok()));
CHECK((false == !wiew));
auto* impl = dynamic_cast<arangodb::iresearch::IResearchViewDBServer*>(wiew.get());
CHECK((nullptr != impl));
@ -1424,4 +1458,4 @@ SECTION("test_visitCollections") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -61,22 +61,35 @@ struct TestView: public arangodb::LogicalView {
return _appendVelocyPackResult;
}
virtual arangodb::Result drop() override { return arangodb::Result(); }
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
bool isNew,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
return preCommit(view) ? view : nullptr;
}
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
// -----------------------------------------------------------------------------
@ -88,6 +101,7 @@ struct RestUsersHandlerSetup {
arangodb::application_features::ApplicationServer server;
std::unique_ptr<TRI_vocbase_t> system;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
RestUsersHandlerSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -131,7 +145,7 @@ struct RestUsersHandlerSetup {
viewTypesFeature->emplace(
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
TestView::make
viewFactory
);
}

View File

@ -50,23 +50,36 @@ struct TestView: public arangodb::LogicalView {
builder.add("properties", _properties.slice());
return _appendVelocyPackResult;
}
virtual arangodb::Result drop() override { return arangodb::Result(); }
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
bool isNew,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
return preCommit(view) ? view : nullptr;
}
virtual arangodb::Result drop() override { return vocbase().dropView(id(), true); }
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
// -----------------------------------------------------------------------------
@ -77,6 +90,7 @@ struct RestViewHandlerSetup {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
RestViewHandlerSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -113,7 +127,7 @@ struct RestViewHandlerSetup {
viewTypesFeature->emplace(
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
TestView::make
viewFactory
);
}

View File

@ -36,51 +36,40 @@
namespace {
std::shared_ptr<arangodb::LogicalView> makeTestView(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool /*isNew*/,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
struct Impl: public arangodb::LogicalViewStorageEngine {
Impl(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
uint64_t planVersion
): arangodb::LogicalViewStorageEngine(vocbase, info, planVersion) {
}
virtual arangodb::Result appendVelocyPackDetailed(
arangodb::velocypack::Builder&,
bool
) const override {
return arangodb::Result();
}
arangodb::Result create() {
return LogicalViewStorageEngine::create(*this);
}
virtual arangodb::Result dropImpl() override { return arangodb::Result(); }
virtual void open() override {}
virtual arangodb::Result updateProperties(
arangodb::velocypack::Slice const&,
bool
) override {
return arangodb::Result();
}
virtual bool visitCollections(
std::function<bool(TRI_voc_cid_t)> const&
) const override {
return true;
}
};
struct TestView: public arangodb::LogicalView {
TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion)
: arangodb::LogicalView(vocbase, definition, planVersion) {
}
virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); }
virtual arangodb::Result drop() override { deleted(true); return vocbase().dropView(id(), true); }
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const&, bool, bool) override { return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
auto view = std::make_shared<Impl>(vocbase, info, planVersion);
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return
(!preCommit || preCommit(std::static_pointer_cast<arangodb::LogicalView>(view)))
&& view->create().ok()
? view : nullptr;
}
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
@ -92,6 +81,7 @@ struct CollectionNameResolverSetup {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
CollectionNameResolverSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -123,7 +113,7 @@ struct CollectionNameResolverSetup {
arangodb::LogicalDataSource::Type::emplace(
arangodb::velocypack::StringRef("testViewType")
),
makeTestView
viewFactory
);
}
@ -241,7 +231,7 @@ SECTION("test_getDataSource") {
}
CHECK((true == vocbase.dropCollection(collection->id(), true, 0).ok()));
CHECK((true == vocbase.dropView(view->id(), true).ok()));
CHECK((true == view->drop().ok()));
CHECK((true == collection->deleted()));
CHECK((true == view->deleted()));
@ -280,4 +270,4 @@ SECTION("test_getDataSource") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -80,22 +80,35 @@ struct TestView: public arangodb::LogicalView {
return _appendVelocyPackResult;
}
virtual arangodb::Result drop() override { return arangodb::Result(); }
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
bool isNew,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
return preCommit(view) ? view : nullptr;
}
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
// -----------------------------------------------------------------------------
@ -107,6 +120,7 @@ struct V8UsersSetup {
arangodb::application_features::ApplicationServer server;
std::unique_ptr<TRI_vocbase_t> system;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
V8UsersSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -153,7 +167,7 @@ struct V8UsersSetup {
viewTypesFeature->emplace(
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
TestView::make
viewFactory
);
}
@ -455,4 +469,4 @@ SECTION("test_collection_auth") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -77,23 +77,36 @@ struct TestView: public arangodb::LogicalView {
builder.add("properties", _properties.slice());
return _appendVelocyPackResult;
}
virtual arangodb::Result drop() override { return arangodb::Result(); }
static std::shared_ptr<LogicalView> make(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
bool isNew,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
return preCommit(view) ? view : nullptr;
}
virtual arangodb::Result drop() override { return vocbase().dropView(id(), true); }
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
// -----------------------------------------------------------------------------
@ -104,6 +117,7 @@ struct V8ViewsSetup {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
V8ViewsSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -142,7 +156,7 @@ struct V8ViewsSetup {
viewTypesFeature->emplace(
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
TestView::make
viewFactory
);
}
@ -1403,4 +1417,4 @@ SECTION("test_auth") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -37,51 +37,40 @@
namespace {
std::shared_ptr<arangodb::LogicalView> makeTestView(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
bool /*isNew*/,
uint64_t planVersion,
arangodb::LogicalView::PreCommitCallback const& preCommit
) {
struct Impl: public arangodb::LogicalViewStorageEngine {
Impl(
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& info,
uint64_t planVersion
): LogicalViewStorageEngine(vocbase, info, planVersion) {
}
virtual arangodb::Result appendVelocyPackDetailed(
arangodb::velocypack::Builder&,
bool
) const override {
return arangodb::Result();
}
arangodb::Result create() {
return LogicalViewStorageEngine::create(*this);
}
virtual arangodb::Result dropImpl() override { return arangodb::Result(); }
virtual void open() override {}
virtual arangodb::Result updateProperties(
arangodb::velocypack::Slice const&,
bool
) override {
return arangodb::Result();
}
virtual bool visitCollections(
std::function<bool(TRI_voc_cid_t)> const&
) const override {
return true;
}
};
struct TestView: public arangodb::LogicalView {
TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion)
: arangodb::LogicalView(vocbase, definition, planVersion) {
}
virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); }
virtual arangodb::Result drop() override { deleted(true); return vocbase().dropView(id(), true); }
virtual void open() override {}
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const&, bool, bool) override { return arangodb::Result(); }
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
};
auto view = std::make_shared<Impl>(vocbase, info, planVersion);
struct ViewFactory: public arangodb::ViewFactory {
virtual arangodb::Result create(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition
) const override {
view = vocbase.createView(definition);
return
(!preCommit || preCommit(std::static_pointer_cast<arangodb::LogicalView>(view)))
&& view->create().ok()
? view : nullptr;
}
return arangodb::Result();
}
virtual arangodb::Result instantiate(
arangodb::LogicalView::ptr& view,
TRI_vocbase_t& vocbase,
arangodb::velocypack::Slice const& definition,
uint64_t planVersion
) const override {
view = std::make_shared<TestView>(vocbase, definition, planVersion);
return arangodb::Result();
}
};
}
@ -93,6 +82,7 @@ struct VocbaseSetup {
StorageEngineMock engine;
arangodb::application_features::ApplicationServer server;
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
ViewFactory viewFactory;
VocbaseSetup(): engine(server), server(nullptr, nullptr) {
arangodb::EngineSelectorFeature::ENGINE = &engine;
@ -124,7 +114,7 @@ struct VocbaseSetup {
arangodb::LogicalDataSource::Type::emplace(
arangodb::velocypack::StringRef("testViewType")
),
makeTestView
viewFactory
);
}
@ -346,7 +336,7 @@ SECTION("test_lookupDataSource") {
}
CHECK((true == vocbase.dropCollection(collection->id(), true, 0).ok()));
CHECK((true == vocbase.dropView(view->id(), true).ok()));
CHECK((true == view->drop().ok()));
CHECK((true == collection->deleted()));
CHECK((true == view->deleted()));
@ -387,4 +377,4 @@ SECTION("test_lookupDataSource") {
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
/*jshint globalstrict:false, strict:false, maxlen: 500 */
/*global fail, assertUndefined, assertNotEqual, assertEqual, assertTrue, assertFalse*/
/*global fail, assertUndefined, assertEqual, assertNotEqual, assertTrue, assertFalse*/
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
@ -65,8 +65,10 @@ function IResearchFeatureDDLTestSuite () {
db._drop("TestCollection0");
db._dropView("TestView");
db._create("TestCollection0");
for (let i = 0; i < 100; ++i) {
db._createView("TestView", "arangosearch", {links:{"TestCollection0":{}}});
db.TestCollection0.save({ name : i.toString() });
db._createView("TestView", "arangosearch", {links:{"TestCollection0":{ includeAllFields:true}}});
var view = db._view("TestView");
assertTrue(null != view);
assertEqual(Object.keys(view.properties().links).length, 1);
@ -80,9 +82,10 @@ function IResearchFeatureDDLTestSuite () {
db._dropView("TestView");
db._create("TestCollection0");
var addLink = { links: { "TestCollection0": {} } };
var addLink = { links: { "TestCollection0": { includeAllFields:true} } };
for (let i = 0; i < 100; ++i) {
db.TestCollection0.save({ name : i.toString() });
var view = db._createView("TestView", "arangosearch", {});
view.properties(addLink, true); // partial update
let properties = view.properties();
@ -109,10 +112,11 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection0");
var view = db._createView("TestView", "arangosearch", {});
var addLink = { links: { "TestCollection0": {} } };
var addLink = { links: { "TestCollection0": { includeAllFields:true} } };
var removeLink = { links: { "TestCollection0": null } };
for (let i = 0; i < 100; ++i) {
db.TestCollection0.save({ name : i.toString() });
view.properties(addLink, true); // partial update
let properties = view.properties();
assertTrue(Object === properties.links.constructor);
@ -137,7 +141,8 @@ function IResearchFeatureDDLTestSuite () {
var view = db._createView("TestView", "arangosearch", {});
db._create("TestCollection0");
var addLink = { links: { "TestCollection0": {} } };
db.TestCollection0.save({ name : 'foo' });
var addLink = { links: { "TestCollection0": { includeAllFields: true } } };
view.properties(addLink, true); // partial update
let properties = view.properties();
assertTrue(Object === properties.links.constructor);
@ -159,23 +164,29 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection2");
var view = db._createView("TestView", "arangosearch", {});
for (var i = 0; i < 1000; ++i) {
db.TestCollection0.save({ name : i.toString() });
db.TestCollection1.save({ name : i.toString() });
db.TestCollection2.save({ name : i.toString() });
}
var properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(0, Object.keys(properties.links).length);
var meta = { links: { "TestCollection0": {} } };
var meta = { links: { "TestCollection0": { includeAllFields:true } } };
view.properties(meta, true); // partial update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(1, Object.keys(properties.links).length);
meta = { links: { "TestCollection1": {} } };
meta = { links: { "TestCollection1": { includeAllFields:true } } };
view.properties(meta, true); // partial update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(2, Object.keys(properties.links).length);
meta = { links: { "TestCollection2": {} } };
meta = { links: { "TestCollection2": { includeAllFields:true } } };
view.properties(meta, false); // full update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
@ -240,8 +251,14 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection2");
var view = db._createView("TestView", "arangosearch", {});
for (var i = 0; i < 1000; ++i) {
db.TestCollection0.save({ name : i.toString() });
db.TestCollection1.save({ name : i.toString() });
db.TestCollection2.save({ name : i.toString() });
}
var meta = { links: {
"TestCollection0": {},
"TestCollection0": { },
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "full" },
"TestCollection2": { fields: {
"b": { fields: { "b1": {} } },
@ -398,11 +415,11 @@ function IResearchFeatureDDLTestSuite () {
var meta = { links: { "TestCollection0": { includeAllFields: true } } };
view.properties(meta, true); // partial update
var result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true} SORT doc.name RETURN doc").toArray();
var result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true } SORT doc.name RETURN doc").toArray();
assertEqual(0, result.length);
col0.save({ name: "quarter", text: "quick over" });
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true} SORT doc.name RETURN doc").toArray();
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true } SORT doc.name RETURN doc").toArray();
assertEqual(1, result.length);
assertEqual("quarter", result[0].name);
@ -420,7 +437,7 @@ function IResearchFeatureDDLTestSuite () {
meta = { links: { "TestCollection0": { includeAllFields: true } } };
view.properties(meta, true); // partial update
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true} SORT doc.name RETURN doc").toArray();
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true } SORT doc.name RETURN doc").toArray();
assertEqual(4, result.length);
assertEqual("full", result[0].name);
assertEqual("half", result[1].name);
@ -446,7 +463,7 @@ function IResearchFeatureDDLTestSuite () {
} };
view.properties(meta, true); // partial update
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true} SORT doc.name RETURN doc").toArray();
result = db._query("FOR doc IN TestView OPTIONS { waitForSync: true } SORT doc.name RETURN doc").toArray();
assertEqual(4, result.length);
assertEqual("full", result[0].name);
assertEqual("half", result[1].name);

View File

@ -65,8 +65,10 @@ function IResearchFeatureDDLTestSuite () {
db._drop("TestCollection0");
db._dropView("TestView");
db._create("TestCollection0");
for (let i = 0; i < 100; ++i) {
db._createView("TestView", "arangosearch", {links:{"TestCollection0":{}}});
db.TestCollection0.save({ name : i.toString() });
db._createView("TestView", "arangosearch", {links:{"TestCollection0":{ includeAllFields:true}}});
var view = db._view("TestView");
assertTrue(null != view);
assertEqual(Object.keys(view.properties().links).length, 1);
@ -80,9 +82,10 @@ function IResearchFeatureDDLTestSuite () {
db._dropView("TestView");
db._create("TestCollection0");
var addLink = { links: { "TestCollection0": {} } };
var addLink = { links: { "TestCollection0": { includeAllFields:true} } };
for (let i = 0; i < 100; ++i) {
db.TestCollection0.save({ name : i.toString() });
var view = db._createView("TestView", "arangosearch", {});
view.properties(addLink, true); // partial update
let properties = view.properties();
@ -109,10 +112,11 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection0");
var view = db._createView("TestView", "arangosearch", {});
var addLink = { links: { "TestCollection0": {} } };
var addLink = { links: { "TestCollection0": { includeAllFields:true} } };
var removeLink = { links: { "TestCollection0": null } };
for (let i = 0; i < 100; ++i) {
db.TestCollection0.save({ name : i.toString() });
view.properties(addLink, true); // partial update
let properties = view.properties();
assertTrue(Object === properties.links.constructor);
@ -137,7 +141,8 @@ function IResearchFeatureDDLTestSuite () {
var view = db._createView("TestView", "arangosearch", {});
db._create("TestCollection0");
var addLink = { links: { "TestCollection0": {} } };
db.TestCollection0.save({ name : 'foo' });
var addLink = { links: { "TestCollection0": { includeAllFields: true } } };
view.properties(addLink, true); // partial update
let properties = view.properties();
assertTrue(Object === properties.links.constructor);
@ -159,28 +164,40 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection2");
var view = db._createView("TestView", "arangosearch", {});
for (var i = 0; i < 1000; ++i) {
db.TestCollection0.save({ name : i.toString() });
db.TestCollection1.save({ name : i.toString() });
db.TestCollection2.save({ name : i.toString() });
}
var properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(0, Object.keys(properties.links).length);
var meta = { links: { "TestCollection0": {} } };
var meta = { links: { "TestCollection0": { includeAllFields:true } } };
view.properties(meta, true); // partial update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(1, Object.keys(properties.links).length);
meta = { links: { "TestCollection1": {} } };
meta = { links: { "TestCollection1": { includeAllFields:true } } };
view.properties(meta, true); // partial update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(2, Object.keys(properties.links).length);
meta = { links: { "TestCollection2": {} } };
meta = { links: { "TestCollection2": { includeAllFields:true } } };
view.properties(meta, false); // full update
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(1, Object.keys(properties.links).length);
// create with links
db._dropView("TestView");
view = db._createView("TestView", "arangosearch", meta);
properties = view.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(1, Object.keys(properties.links).length);
// consolidate
db._dropView("TestView");
@ -234,8 +251,14 @@ function IResearchFeatureDDLTestSuite () {
db._create("TestCollection2");
var view = db._createView("TestView", "arangosearch", {});
for (var i = 0; i < 1000; ++i) {
db.TestCollection0.save({ name : i.toString() });
db.TestCollection1.save({ name : i.toString() });
db.TestCollection2.save({ name : i.toString() });
}
var meta = { links: {
"TestCollection0": {},
"TestCollection0": { },
"TestCollection1": { analyzers: [ "text_en"], includeAllFields: true, trackListPositions: true, storeValues: "full" },
"TestCollection2": { fields: {
"b": { fields: { "b1": {} } },