From 802180e2f2b6f99f158ca6a3437d6a578a1ec3ff Mon Sep 17 00:00:00 2001 From: jsteemann Date: Mon, 22 May 2017 18:00:20 +0200 Subject: [PATCH 01/12] better error messages --- arangod/Aql/Query.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 42fadebaf6..ce4b13d4e2 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -378,8 +378,10 @@ void Query::registerWarning(int code, char const* details) { if (DoFailOnWarning) { // make an error from each warning if requested - // note: this will throw! - registerErrorCustom(code, details); + if (details == nullptr) { + THROW_ARANGO_EXCEPTION(code); + } + THROW_ARANGO_EXCEPTION_MESSAGE(code, details); } if (_warnings.size() > _maxWarningCount) { From d8dc3c85e77ebea43be954e17b6035ba561149c8 Mon Sep 17 00:00:00 2001 From: Andreas Streichardt Date: Mon, 22 May 2017 19:06:54 +0200 Subject: [PATCH 02/12] Call version instead of foxx status to save a v8 context --- js/server/modules/@arangodb/foxx/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/server/modules/@arangodb/foxx/manager.js b/js/server/modules/@arangodb/foxx/manager.js index 51dcddc526..9b562af5a4 100644 --- a/js/server/modules/@arangodb/foxx/manager.js +++ b/js/server/modules/@arangodb/foxx/manager.js @@ -109,7 +109,7 @@ function isClusterReadyForBusiness () { const coordIds = getPeerCoordinatorIds(); return parallelClusterRequests(function * () { for (const coordId of coordIds) { - yield [coordId, 'GET', '/_api/foxx/_local/status']; + yield [coordId, 'GET', '/_api/version']; } }()).every(response => response.statusCode === 200); } From ddfa15b8cc12bed4a8354d2468f69e7a15533da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Gra=CC=88tzer?= Date: Mon, 22 May 2017 19:09:32 +0200 Subject: [PATCH 03/12] Adding bound checking back --- arangod/RocksDBEngine/RocksDBCollection.cpp | 1 - arangod/RocksDBEngine/RocksDBIndex.cpp | 13 +++++-- arangod/RocksDBEngine/RocksDBIndex.h | 7 ++-- arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp | 35 +++++++++++++++---- arangod/RocksDBEngine/RocksDBPrimaryIndex.h | 12 +++++-- arangod/RocksDBEngine/RocksDBVPackIndex.cpp | 14 ++++---- arangod/RocksDBEngine/RocksDBVPackIndex.h | 3 +- 7 files changed, 63 insertions(+), 22 deletions(-) diff --git a/arangod/RocksDBEngine/RocksDBCollection.cpp b/arangod/RocksDBEngine/RocksDBCollection.cpp index 86326a7b27..e60f48cbe8 100644 --- a/arangod/RocksDBEngine/RocksDBCollection.cpp +++ b/arangod/RocksDBEngine/RocksDBCollection.cpp @@ -628,7 +628,6 @@ void RocksDBCollection::invokeOnAllElements( void RocksDBCollection::truncate(transaction::Methods* trx, OperationOptions& options) { // TODO FIXME -- improve transaction size - // TODO FIXME -- intermediate commit TRI_ASSERT(_objectId != 0); rocksdb::Comparator const* cmp = globalRocksEngine()->cmp(); diff --git a/arangod/RocksDBEngine/RocksDBIndex.cpp b/arangod/RocksDBEngine/RocksDBIndex.cpp index 61a09dfa62..e6669833da 100644 --- a/arangod/RocksDBEngine/RocksDBIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBIndex.cpp @@ -35,6 +35,8 @@ #include "VocBase/LogicalCollection.h" #include "VocBase/ticks.h" +#include + using namespace arangodb; using namespace arangodb::rocksutils; @@ -86,6 +88,10 @@ RocksDBIndex::~RocksDBIndex() { } } +rocksdb::Comparator const* RocksDBIndex::comparator() const { + return _cmp; +} + void RocksDBIndex::toVelocyPackFigures(VPackBuilder& builder) const { TRI_ASSERT(builder.isOpenObject()); Index::toVelocyPackFigures(builder); @@ -191,13 +197,14 @@ void RocksDBIndex::truncate(transaction::Methods* trx) { RocksDBKeyBounds indexBounds = getBounds(); rocksdb::ReadOptions options = mthds->readOptions(); - rocksdb::Slice upperBound = indexBounds.end(); - options.iterate_upper_bound = &upperBound; + rocksdb::Slice end = indexBounds.end(); + options.iterate_upper_bound = &end; + std::unique_ptr iter = mthds->NewIterator(options); iter->Seek(indexBounds.start()); - while (iter->Valid()) { + while (iter->Valid() && _cmp->Compare(iter->key(), end) < 0) { Result r = mthds->Delete(iter->key()); if (!r.ok()) { THROW_ARANGO_EXCEPTION(r); diff --git a/arangod/RocksDBEngine/RocksDBIndex.h b/arangod/RocksDBEngine/RocksDBIndex.h index 4068ac270b..e42699fe8a 100644 --- a/arangod/RocksDBEngine/RocksDBIndex.h +++ b/arangod/RocksDBEngine/RocksDBIndex.h @@ -30,12 +30,13 @@ #include "RocksDBEngine/RocksDBKeyBounds.h" #include +namespace rocksdb {class Comparator;} + namespace arangodb { namespace cache { class Cache; } class LogicalCollection; -class RocksDBComparator; class RocksDBCounterManager; class RocksDBMethods; @@ -98,6 +99,8 @@ class RocksDBIndex : public Index { virtual bool deserializeEstimate(RocksDBCounterManager* mgr); virtual void recalculateEstimates(); + + rocksdb::Comparator const* comparator() const; protected: // Will be called during truncate to allow the index to update selectivity @@ -115,7 +118,7 @@ class RocksDBIndex : public Index { protected: uint64_t _objectId; - RocksDBComparator* _cmp; + rocksdb::Comparator* _cmp; mutable std::shared_ptr _cache; // we use this boolean for testing whether _cache is set. diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp index 6d6d73b15c..510334a97e 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp @@ -119,7 +119,13 @@ RocksDBAllIndexIterator::RocksDBAllIndexIterator( : IndexIterator(collection, trx, mmdr, index), _reverse(reverse), _iterator(), - _bounds(RocksDBKeyBounds::PrimaryIndex(index->objectId())) { + _bounds(RocksDBKeyBounds::PrimaryIndex(index->objectId())), + _cmp(index->comparator()) +#ifdef ARANGODB_ENABLE_MAINTAINER_MODE + , _index(index) +#endif + { + // acquire rocksdb transaction RocksDBTransactionState* state = rocksutils::toRocksTransactionState(trx); TRI_ASSERT(state != nullptr); @@ -140,6 +146,15 @@ RocksDBAllIndexIterator::RocksDBAllIndexIterator( } } +bool RocksDBAllIndexIterator::outOfRange() const { + TRI_ASSERT(_trx->state()->isRunning()); + if (_reverse) { + return _cmp->Compare(_iterator->key(), _bounds.start()) < 0; + } else { + return _cmp->Compare(_iterator->key(), _bounds.end()) > 0; + } +} + bool RocksDBAllIndexIterator::next(TokenCallback const& cb, size_t limit) { TRI_ASSERT(_trx->state()->isRunning()); @@ -175,7 +190,7 @@ bool RocksDBAllIndexIterator::nextWithKey(TokenKeyCallback const& cb, size_t limit) { TRI_ASSERT(_trx->state()->isRunning()); - if (limit == 0 || !_iterator->Valid()) { + if (limit == 0 || !_iterator->Valid() || outOfRange()) { // No limit no data, or we are actually done. The last call should have // returned false TRI_ASSERT(limit > 0); // Someone called with limit == 0. Api broken @@ -183,6 +198,9 @@ bool RocksDBAllIndexIterator::nextWithKey(TokenKeyCallback const& cb, } while (limit > 0) { +#ifdef ARANGODB_ENABLE_MAINTAINER_MODE + TRI_ASSERT(_index->objectId() == RocksDBKey::objectId(_iterator->key())); +#endif RocksDBToken token(RocksDBValue::revisionId(_iterator->value())); StringRef key = RocksDBKey::primaryKey(_iterator->key()); cb(token, key); @@ -193,7 +211,7 @@ bool RocksDBAllIndexIterator::nextWithKey(TokenKeyCallback const& cb, } else { _iterator->Next(); } - if (!_iterator->Valid()) { + if (!_iterator->Valid() || outOfRange()) { return false; } } @@ -228,7 +246,7 @@ RocksDBAnyIndexIterator::RocksDBAnyIndexIterator( LogicalCollection* collection, transaction::Methods* trx, ManagedDocumentResult* mmdr, RocksDBPrimaryIndex const* index) : IndexIterator(collection, trx, mmdr, index), - _cmp(index->_cmp), + _cmp(index->comparator()), _iterator(), _bounds(RocksDBKeyBounds::PrimaryIndex(index->objectId())), _total(0), @@ -241,6 +259,7 @@ RocksDBAnyIndexIterator::RocksDBAnyIndexIterator( RocksDBMethods* mthds = rocksutils::toRocksMethods(trx); auto options = mthds->readOptions(); TRI_ASSERT(options.snapshot != nullptr); + TRI_ASSERT(options.prefix_same_as_start); options.fill_cache = false; _iterator = mthds->NewIterator(options); @@ -259,7 +278,7 @@ RocksDBAnyIndexIterator::RocksDBAnyIndexIterator( _iterator->Prev(); } } - if (!_iterator->Valid()) { + if (!_iterator->Valid() || outOfRange()) { _iterator->Seek(_bounds.start()); } } @@ -282,7 +301,7 @@ bool RocksDBAnyIndexIterator::next(TokenCallback const& cb, size_t limit) { --limit; _returned++; _iterator->Next(); - if (!_iterator->Valid()) { + if (!_iterator->Valid() || outOfRange()) { if (_returned < _total) { _iterator->Seek(_bounds.start()); continue; @@ -295,6 +314,10 @@ bool RocksDBAnyIndexIterator::next(TokenCallback const& cb, size_t limit) { void RocksDBAnyIndexIterator::reset() { _iterator->Seek(_bounds.start()); } +bool RocksDBAnyIndexIterator::outOfRange() const { + return _cmp->Compare(_iterator->key(), _bounds.end()) > 0; +} + // ================ PrimaryIndex ================ RocksDBPrimaryIndex::RocksDBPrimaryIndex( diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h index 0be14e681e..4d44068a0e 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h @@ -40,6 +40,7 @@ namespace rocksdb { class Iterator; +class Comparator; } namespace arangodb { @@ -93,9 +94,16 @@ class RocksDBAllIndexIterator final : public IndexIterator { void seek(StringRef const& key); private: + bool outOfRange() const; + bool const _reverse; - std::unique_ptr _iterator; RocksDBKeyBounds const _bounds; + std::unique_ptr _iterator; + rocksdb::Comparator const* _cmp; +#ifdef ARANGODB_ENABLE_MAINTAINER_MODE + RocksDBPrimaryIndex const* _index; +#endif + }; class RocksDBAnyIndexIterator final : public IndexIterator { @@ -118,7 +126,7 @@ class RocksDBAnyIndexIterator final : public IndexIterator { static uint64_t newOffset(LogicalCollection* collection, transaction::Methods* trx); - RocksDBComparator const* _cmp; + rocksdb::Comparator const* _cmp; std::unique_ptr _iterator; RocksDBKeyBounds const _bounds; uint64_t _total; diff --git a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp index 364b7bca38..43771e20e3 100644 --- a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp @@ -84,7 +84,7 @@ RocksDBVPackIndexIterator::RocksDBVPackIndexIterator( : IndexIterator(collection, trx, mmdr, index), _index(index), _primaryIndex(primaryIndex), - _cmp(index->_cmp), + _cmp(index->comparator()), _reverse(reverse), _bounds(index->_unique ? RocksDBKeyBounds::UniqueIndexRange( index->objectId(), left, right) @@ -120,15 +120,17 @@ void RocksDBVPackIndexIterator::reset() { bool RocksDBVPackIndexIterator::outOfRange() const { TRI_ASSERT(_trx->state()->isRunning()); - TRI_ASSERT(_reverse); - - return (_cmp->Compare(_iterator->key(), _bounds.start()) < 0); + if (_reverse) { + return (_cmp->Compare(_iterator->key(), _bounds.start()) < 0); + } else { + return (_cmp->Compare(_iterator->key(), _bounds.end()) > 0); + } } bool RocksDBVPackIndexIterator::next(TokenCallback const& cb, size_t limit) { TRI_ASSERT(_trx->state()->isRunning()); - if (limit == 0 || !_iterator->Valid() || (_reverse && outOfRange())) { + if (limit == 0 || !_iterator->Valid() || outOfRange()) { // No limit no data, or we are actually done. The last call should have // returned false TRI_ASSERT(limit > 0); // Someone called with limit == 0. Api broken @@ -150,7 +152,7 @@ bool RocksDBVPackIndexIterator::next(TokenCallback const& cb, size_t limit) { _iterator->Next(); } - if (!_iterator->Valid() || (_reverse && outOfRange())) { + if (!_iterator->Valid() || outOfRange()) { return false; } } diff --git a/arangod/RocksDBEngine/RocksDBVPackIndex.h b/arangod/RocksDBEngine/RocksDBVPackIndex.h index bb037465b5..e9553ffea9 100644 --- a/arangod/RocksDBEngine/RocksDBVPackIndex.h +++ b/arangod/RocksDBEngine/RocksDBVPackIndex.h @@ -50,7 +50,6 @@ class SortCondition; struct Variable; } // namespace aql class LogicalCollection; -class RocksDBComparator; class RocksDBPrimaryIndex; class RocksDBVPackIndex; namespace transaction { @@ -90,7 +89,7 @@ class RocksDBVPackIndexIterator final : public IndexIterator { arangodb::RocksDBVPackIndex const* _index; arangodb::RocksDBPrimaryIndex* _primaryIndex; - arangodb::RocksDBComparator const* _cmp; + rocksdb::Comparator const* _cmp; std::unique_ptr _iterator; bool const _reverse; RocksDBKeyBounds _bounds; From a7b3839e6c3bf132ff5e2e543556578702012704 Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 08:53:13 +0200 Subject: [PATCH 04/12] renamed "getMore" to "next" --- arangod/Aql/EnumerateCollectionBlock.cpp | 2 +- arangod/Aql/IndexBlock.cpp | 7 ++++--- arangod/Aql/IndexBlock.h | 4 ++-- arangod/Graph/SingleServerEdgeCursor.cpp | 5 ++--- arangod/Indexes/IndexIterator.h | 6 ++---- arangod/Pregel/GraphStore.cpp | 5 ++--- arangod/RestHandler/RestEdgesHandler.cpp | 3 +-- arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp | 3 +-- arangod/Transaction/Methods.cpp | 5 ++--- arangod/Utils/OperationCursor.cpp | 10 +--------- arangod/Utils/OperationCursor.h | 20 +++---------------- arangod/V8Server/v8-query.cpp | 2 +- 12 files changed, 22 insertions(+), 50 deletions(-) diff --git a/arangod/Aql/EnumerateCollectionBlock.cpp b/arangod/Aql/EnumerateCollectionBlock.cpp index 5bb1e663cd..3f8b0d3df5 100644 --- a/arangod/Aql/EnumerateCollectionBlock.cpp +++ b/arangod/Aql/EnumerateCollectionBlock.cpp @@ -230,7 +230,7 @@ AqlItemBlock* EnumerateCollectionBlock::getSome(size_t, // atLeast, THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } - bool tmp = _cursor->getMore(cb, atMost); + bool tmp = _cursor->next(cb, atMost); if (!tmp) { TRI_ASSERT(!_cursor->hasMore()); } diff --git a/arangod/Aql/IndexBlock.cpp b/arangod/Aql/IndexBlock.cpp index e2807d0018..948e216f25 100644 --- a/arangod/Aql/IndexBlock.cpp +++ b/arangod/Aql/IndexBlock.cpp @@ -32,6 +32,7 @@ #include "Basics/Exceptions.h" #include "Basics/ScopeGuard.h" #include "Basics/StaticStrings.h" +#include "Cluster/ServerState.h" #include "StorageEngine/DocumentIdentifierToken.h" #include "Utils/OperationCursor.h" #include "V8/v8-globals.h" @@ -417,7 +418,7 @@ bool IndexBlock::skipIndex(size_t atMost) { // this is called every time we need to fetch data from the indexes bool IndexBlock::readIndex( size_t atMost, - std::function& callback) { + IndexIterator::TokenCallback const& callback) { DEBUG_BEGIN_BLOCK(); // this is called every time we want to read the index. // For the primary key index, this only reads the index once, and never @@ -451,7 +452,7 @@ bool IndexBlock::readIndex( TRI_ASSERT(atMost >= _returned); - if (_cursor->getMore(callback, atMost - _returned)) { + if (_cursor->next(callback, atMost - _returned)) { // We have returned enough. // And this index could return more. // We are good. @@ -498,7 +499,7 @@ AqlItemBlock* IndexBlock::getSome(size_t atLeast, size_t atMost) { std::unique_ptr res; - std::function callback; + IndexIterator::TokenCallback callback; if (_indexes.size() > 1) { // Activate uniqueness checks callback = [&](DocumentIdentifierToken const& token) { diff --git a/arangod/Aql/IndexBlock.h b/arangod/Aql/IndexBlock.h index 9508e598ab..2d0bcd2153 100644 --- a/arangod/Aql/IndexBlock.h +++ b/arangod/Aql/IndexBlock.h @@ -29,7 +29,7 @@ #include "Aql/ExecutionBlock.h" #include "Aql/ExecutionNode.h" #include "Aql/IndexNode.h" - +#include "Indexes/IndexIterator.h" #include "StorageEngine/DocumentIdentifierToken.h" namespace arangodb { @@ -102,7 +102,7 @@ class IndexBlock final : public ExecutionBlock { bool skipIndex(size_t atMost); /// @brief continue fetching of documents - bool readIndex(size_t atMost, std::function&); + bool readIndex(size_t atMost, IndexIterator::TokenCallback const&); /// @brief frees the memory for all non-constant expressions void cleanupNonConstExpressions(); diff --git a/arangod/Graph/SingleServerEdgeCursor.cpp b/arangod/Graph/SingleServerEdgeCursor.cpp index 4ea835d994..b9b7024e05 100644 --- a/arangod/Graph/SingleServerEdgeCursor.cpp +++ b/arangod/Graph/SingleServerEdgeCursor.cpp @@ -128,7 +128,7 @@ bool SingleServerEdgeCursor::next( auto cb = [&](DocumentIdentifierToken const& token) { _cache.emplace_back(token); }; - bool tmp = cursor->getMore(cb, 1000); + bool tmp = cursor->next(cb, 1000); TRI_ASSERT(tmp == cursor->hasMore()); } } while (_cache.empty()); @@ -174,8 +174,7 @@ void SingleServerEdgeCursor::readAll( callback(edgeId, doc, cursorId); } }; - while (cursor->getMore(cb, 1000)) { - } + cursor->all(cb); } } } diff --git a/arangod/Indexes/IndexIterator.h b/arangod/Indexes/IndexIterator.h index 46517efdfa..56db08048a 100644 --- a/arangod/Indexes/IndexIterator.h +++ b/arangod/Indexes/IndexIterator.h @@ -46,15 +46,13 @@ #define ARANGOD_INDEXES_INDEX_ITERATOR_H 1 #include "Basics/Common.h" -#include "Cluster/ServerState.h" #include "Indexes/IndexLookupContext.h" -#include "StorageEngine/StorageEngine.h" -#include "VocBase/ManagedDocumentResult.h" #include "VocBase/vocbase.h" namespace arangodb { class Index; class LogicalCollection; +class ManagedDocumentResult; namespace transaction { class Methods; } @@ -62,7 +60,7 @@ class Methods; /// @brief a base class to iterate over the index. An iterator is requested /// at the index itself class IndexIterator { - protected: + public: typedef std::function TokenCallback; typedef std::function diff --git a/arangod/Pregel/GraphStore.cpp b/arangod/Pregel/GraphStore.cpp index a59c390dca..6e2261f65a 100644 --- a/arangod/Pregel/GraphStore.cpp +++ b/arangod/Pregel/GraphStore.cpp @@ -391,7 +391,7 @@ void GraphStore::_loadVertices(ShardID const& vertexShard, edgeOffset += ventry._edgeCount; } }; - while (cursor->getMore(cb, 1000)) { + while (cursor->next(cb, 1000)) { if (_destroyed) { LOG_TOPIC(WARN, Logger::PREGEL) << "Aborted loading graph"; break; @@ -474,8 +474,7 @@ void GraphStore::_loadEdges(transaction::Methods* trx, } } }; - while (cursor->getMore(cb, 1000)) { - } + cursor->all(cb); // Add up all added elements vertexEntry._edgeCount += added; diff --git a/arangod/RestHandler/RestEdgesHandler.cpp b/arangod/RestHandler/RestEdgesHandler.cpp index 20a5913d4d..65593e6241 100644 --- a/arangod/RestHandler/RestEdgesHandler.cpp +++ b/arangod/RestHandler/RestEdgesHandler.cpp @@ -87,8 +87,7 @@ void RestEdgesHandler::readCursor( THROW_ARANGO_EXCEPTION(cursor->code); } - while (cursor->getMore(cb, 1000)) { - } + cursor->all(cb); } diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp index 6d6d73b15c..cab6c87070 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp @@ -535,8 +535,7 @@ void RocksDBPrimaryIndex::invokeOnAllElements( cnt = callback(token); } }; - while (cursor->next(cb, 1000) && cnt) { - } + while (cursor->next(cb, 1000) && cnt) {} } Result RocksDBPrimaryIndex::postprocessRemove(transaction::Methods* trx, diff --git a/arangod/Transaction/Methods.cpp b/arangod/Transaction/Methods.cpp index 840586279a..c670ffcff3 100644 --- a/arangod/Transaction/Methods.cpp +++ b/arangod/Transaction/Methods.cpp @@ -843,7 +843,7 @@ OperationResult transaction::Methods::anyLocal( skip, limit, 1000, false); LogicalCollection* collection = cursor->collection(); - cursor->getAll([&](DocumentIdentifierToken const& token) { + cursor->all([&](DocumentIdentifierToken const& token) { if (collection->readDocument(this, token, mmdr)) { mmdr.addToBuilder(resultBuilder, false); } @@ -2139,8 +2139,7 @@ OperationResult transaction::Methods::allLocal( } }; - while (cursor->getMore(cb, 1000)) { - } + cursor->all(cb); resultBuilder.close(); diff --git a/arangod/Utils/OperationCursor.cpp b/arangod/Utils/OperationCursor.cpp index 7bb5909cca..a848b616aa 100644 --- a/arangod/Utils/OperationCursor.cpp +++ b/arangod/Utils/OperationCursor.cpp @@ -52,14 +52,9 @@ void OperationCursor::reset() { } } -////////////////////////////////////////////////////////////////////////////// /// @brief Calls cb for the next batchSize many elements /// NOTE: This will throw on OUT_OF_MEMORY -////////////////////////////////////////////////////////////////////////////// - -bool OperationCursor::getMore( - std::function const& callback, - uint64_t batchSize) { +bool OperationCursor::next(IndexIterator::TokenCallback const& callback, uint64_t batchSize) { if (!hasMore()) { return false; } @@ -97,13 +92,10 @@ bool OperationCursor::getMore( return _hasMore; } -////////////////////////////////////////////////////////////////////////////// /// @brief Skip the next toSkip many elements. /// skipped will be increased by the amount of skipped elements afterwards /// Check hasMore()==true before using this /// NOTE: This will throw on OUT_OF_MEMORY -////////////////////////////////////////////////////////////////////////////// - int OperationCursor::skip(uint64_t toSkip, uint64_t& skipped) { if (!hasMore()) { TRI_ASSERT(false); diff --git a/arangod/Utils/OperationCursor.h b/arangod/Utils/OperationCursor.h index 6efd915e6c..3a6c1a95cc 100644 --- a/arangod/Utils/OperationCursor.h +++ b/arangod/Utils/OperationCursor.h @@ -91,36 +91,22 @@ struct OperationCursor { return !successful(); } -////////////////////////////////////////////////////////////////////////////// /// @brief Reset the cursor -////////////////////////////////////////////////////////////////////////////// - void reset(); -////////////////////////////////////////////////////////////////////////////// /// @brief Calls cb for the next batchSize many elements -////////////////////////////////////////////////////////////////////////////// - - bool getMore( - std::function const& callback, + bool next(IndexIterator::TokenCallback const& callback, uint64_t batchSize); -////////////////////////////////////////////////////////////////////////////// /// @brief convenience function to retrieve all results -////////////////////////////////////////////////////////////////////////////// - - void getAll( - std::function const& callback) { - while (getMore(callback, 1000)) {} + void all(IndexIterator::TokenCallback const& callback) { + while (next(callback, 1000)) {} } -////////////////////////////////////////////////////////////////////////////// /// @brief Skip the next toSkip many elements. /// skipped will be increased by the amount of skipped elements afterwards /// Check hasMore()==true before using this /// NOTE: This will throw on OUT_OF_MEMORY -////////////////////////////////////////////////////////////////////////////// - int skip(uint64_t, uint64_t&); }; diff --git a/arangod/V8Server/v8-query.cpp b/arangod/V8Server/v8-query.cpp index 0af189f401..7e10503152 100644 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -255,7 +255,7 @@ static void JS_AllQuery(v8::FunctionCallbackInfo const& args) { VPackBuilder resultBuilder; resultBuilder.openArray(); - opCursor->getAll([&resultBuilder, &mmdr, &trx, &collection](DocumentIdentifierToken const& tkn) { + opCursor->all([&resultBuilder, &mmdr, &trx, &collection](DocumentIdentifierToken const& tkn) { if (collection->readDocument(&trx, tkn, mmdr)) { resultBuilder.add(VPackSlice(mmdr.vpack())); } From 2f3e4d0ba0875294952dd8933e73b03e4c4ac9e8 Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 08:57:39 +0200 Subject: [PATCH 05/12] fix init order --- arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp | 7 +++---- arangod/RocksDBEngine/RocksDBPrimaryIndex.h | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp index 3d301808db..dbb1330d0b 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp @@ -118,13 +118,12 @@ RocksDBAllIndexIterator::RocksDBAllIndexIterator( ManagedDocumentResult* mmdr, RocksDBPrimaryIndex const* index, bool reverse) : IndexIterator(collection, trx, mmdr, index), _reverse(reverse), - _iterator(), _bounds(RocksDBKeyBounds::PrimaryIndex(index->objectId())), - _cmp(index->comparator()) + _iterator(), #ifdef ARANGODB_ENABLE_MAINTAINER_MODE - , _index(index) + _index(index), #endif - { + _cmp(index->comparator()) { // acquire rocksdb transaction RocksDBTransactionState* state = rocksutils::toRocksTransactionState(trx); diff --git a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h index 4d44068a0e..484dd7845e 100644 --- a/arangod/RocksDBEngine/RocksDBPrimaryIndex.h +++ b/arangod/RocksDBEngine/RocksDBPrimaryIndex.h @@ -99,11 +99,10 @@ class RocksDBAllIndexIterator final : public IndexIterator { bool const _reverse; RocksDBKeyBounds const _bounds; std::unique_ptr _iterator; - rocksdb::Comparator const* _cmp; #ifdef ARANGODB_ENABLE_MAINTAINER_MODE RocksDBPrimaryIndex const* _index; #endif - + rocksdb::Comparator const* _cmp; }; class RocksDBAnyIndexIterator final : public IndexIterator { From 1ee149d1ea957c6b477ca22456d3e94af0e40ac1 Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Tue, 23 May 2017 09:47:39 +0200 Subject: [PATCH 06/12] fix build script - it doesn't work properly with quotes. --- Installation/Jenkins/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Installation/Jenkins/build.sh b/Installation/Jenkins/build.sh index 5ca81acc66..56bb831e50 100755 --- a/Installation/Jenkins/build.sh +++ b/Installation/Jenkins/build.sh @@ -668,7 +668,7 @@ set -e if test "${PARTIAL_STATE}" == 0; then rm -rf CMakeFiles CMakeCache.txt CMakeCPackOptions.cmake cmake_install.cmake CPackConfig.cmake CPackSourceConfig.cmake CFLAGS="${CFLAGS}" CXXFLAGS="${CXXFLAGS}" LDFLAGS="${LDFLAGS}" LIBS="${LIBS}" \ - cmake "${SOURCE_DIR}" "${CONFIGURE_OPTIONS[@]}" -G "${GENERATOR}" || exit 1 + cmake "${SOURCE_DIR}" ${CONFIGURE_OPTIONS[@]} -G "${GENERATOR}" || exit 1 fi if [ -n "$CPACK" ] && [ -n "${TARGET_DIR}" ] && [ -z "${MSVC}" ]; then @@ -681,7 +681,7 @@ fi TRIES=0; set +e while /bin/true; do - ${MAKE_CMD_PREFIX} ${MAKE} "${MAKE_PARAMS[@]}" + ${MAKE_CMD_PREFIX} ${MAKE} ${MAKE_PARAMS[@]} RC=$? if test "${isCygwin}" == 1 -a "${RC}" != 0 -a "${TRIES}" == 0; then # sometimes windows will fail on a messed up working copy, From 6c065cd8894e0ffb6dc0ec50ffaaa8449732b073 Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Tue, 23 May 2017 10:16:45 +0200 Subject: [PATCH 07/12] fix windows executable version information --- CMakeLists.txt | 9 ++++++++- Installation/Windows/version/VersionInfo.in | 14 +++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) mode change 100644 => 100755 Installation/Windows/version/VersionInfo.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 797efedead..9be801175f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,13 @@ set(ARANGODB_VERSION_MINOR "2") set(ARANGODB_VERSION_REVISION "devel") set(ARANGODB_PACKAGE_REVISION "1") +# version for the windows rc file needs to be numeric: +if (ARANGODB_VERSION_REVISION GREATER -1) + set(ARANGODB_NUMERIC_VERSION_REVISION "${ARANGODB_VERSION_REVISION}") +else() + set(ARANGODB_NUMERIC_VERSION_REVISION 1337) +endif() + set(ARANGODB_VERSION "${ARANGODB_VERSION_MAJOR}.${ARANGODB_VERSION_MINOR}.${ARANGODB_VERSION_REVISION}") @@ -94,7 +101,7 @@ set(ARANGODB_FRIENDLY_STRING "ArangoDB - the native multi-model NoSQL database") set(ARANGO_BENCH_FRIENDLY_STRING "arangobench - stress test program") set(ARANGO_DUMP_FRIENDLY_STRING "arangodump - export") set(ARANGO_RESTORE_FRIENDLY_STRING "arangrestore - importer") -set(ARANGO_EXPORT_FRIENDLY_STRING "arangoexport - datae xporter") +set(ARANGO_EXPORT_FRIENDLY_STRING "arangoexport - datae xporter") set(ARANGO_IMP_FRIENDLY_STRING "arangoimp - TSV/CSV/JSON importer") set(ARANGOSH_FRIENDLY_STRING "arangosh - commandline client") set(ARANGO_VPACK_FRIENDLY_STRING "arangovpack - vpack printer") diff --git a/Installation/Windows/version/VersionInfo.in b/Installation/Windows/version/VersionInfo.in old mode 100644 new mode 100755 index 67d976b32d..11e6ee561a --- a/Installation/Windows/version/VersionInfo.in +++ b/Installation/Windows/version/VersionInfo.in @@ -1,11 +1,11 @@ #pragma once #ifndef PRODUCT_VERSION_MAJOR -#define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ +#define PRODUCT_VERSION_MAJOR @ARANGODB_VERSION_MAJOR@ #endif #ifndef PRODUCT_VERSION_MINOR -#define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ +#define PRODUCT_VERSION_MINOR @ARANGODB_VERSION_MINOR@ #endif #ifndef PRODUCT_VERSION_PATCH @@ -13,15 +13,15 @@ #endif #ifndef PRODUCT_VERSION_BUILD -#define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ +#define PRODUCT_VERSION_BUILD @ARANGODB_NUMERIC_VERSION_REVISION@ #endif #ifndef FILE_VERSION_MAJOR -#define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ +#define FILE_VERSION_MAJOR @ARANGODB_VERSION_MAJOR@ #endif #ifndef FILE_VERSION_MINOR -#define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ +#define FILE_VERSION_MINOR @ARANGODB_VERSION_MINOR@ #endif #ifndef FILE_VERSION_PATCH @@ -29,7 +29,7 @@ #endif #ifndef FILE_VERSION_BUILD -#define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ +#define FILE_VERSION_BUILD @ARANGODB_NUMERIC_VERSION_REVISION@ #endif #ifndef __TO_STRING @@ -49,7 +49,7 @@ #define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD #define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" -#ifndef PRODUCT_INON +#ifndef PRODUCT_ICON #define PRODUCT_ICON "@PRODUCT_ICON@" #endif From 53fa66e60d789b15e94bc5fcb1626727e68a87df Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 10:30:36 +0200 Subject: [PATCH 08/12] move parts from ProgramOptions from headers into cpp files --- arangod/Aql/BindParameters.h | 2 +- arangod/Aql/Query.cpp | 53 +-- arangod/Aql/Query.h | 67 +-- arangod/Aql/QueryList.cpp | 19 +- arangod/Aql/QueryList.h | 15 +- arangod/RestServer/ConsoleFeature.cpp | 2 + arangod/RestServer/InitDatabaseFeature.cpp | 3 + arangod/RestServer/QueryRegistryFeature.cpp | 18 +- arangod/RestServer/QueryRegistryFeature.h | 6 +- arangod/RestServer/UnitTestsFeature.cpp | 2 + arangosh/Export/ExportFeature.cpp | 1 + arangosh/Import/ImportFeature.cpp | 1 + arangosh/Shell/ClientFeature.cpp | 2 + arangosh/Shell/ConsoleFeature.cpp | 2 + lib/ApplicationFeatures/ApplicationServer.cpp | 2 + lib/ApplicationFeatures/ConfigFeature.cpp | 3 +- lib/CMakeLists.txt | 3 + lib/ProgramOptions/IniFileParser.h | 1 - lib/ProgramOptions/Option.cpp | 177 +++++++ lib/ProgramOptions/Option.h | 140 +----- lib/ProgramOptions/ProgramOptions.cpp | 444 ++++++++++++++++++ lib/ProgramOptions/ProgramOptions.h | 397 ++-------------- lib/ProgramOptions/Section.cpp | 81 ++++ lib/ProgramOptions/Section.h | 48 +- 24 files changed, 818 insertions(+), 671 deletions(-) create mode 100644 lib/ProgramOptions/Option.cpp create mode 100644 lib/ProgramOptions/ProgramOptions.cpp create mode 100644 lib/ProgramOptions/Section.cpp diff --git a/arangod/Aql/BindParameters.h b/arangod/Aql/BindParameters.h index c10382c0fd..d69fcf1fe4 100644 --- a/arangod/Aql/BindParameters.h +++ b/arangod/Aql/BindParameters.h @@ -44,7 +44,7 @@ class BindParameters { : _builder(nullptr), _parameters(), _processed(false) {} /// @brief create the parameters - explicit BindParameters(std::shared_ptr builder) + explicit BindParameters(std::shared_ptr const& builder) : _builder(builder), _parameters(), _processed(false) {} /// @brief destroy the parameters diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index ce4b13d4e2..60dde69a7e 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -23,6 +23,7 @@ #include "Query.h" +#include "ApplicationFeatures/ApplicationServer.h" #include "Aql/AqlItemBlock.h" #include "Aql/AqlTransaction.h" #include "Aql/ExecutionBlock.h" @@ -42,6 +43,7 @@ #include "Cluster/ServerState.h" #include "Logger/Logger.h" #include "RestServer/AqlFeature.h" +#include "RestServer/QueryRegistryFeature.h" #include "StorageEngine/TransactionState.h" #include "Transaction/Methods.h" #include "Transaction/StandaloneContext.h" @@ -69,23 +71,11 @@ static std::atomic NextQueryId(1); constexpr uint64_t DontCache = 0; } -/// @brief global memory limit for AQL queries -uint64_t Query::MemoryLimitValue = 0; - -/// @brief global threshold value for slow queries -double Query::SlowQueryThresholdValue = 10.0; - -/// @brief whether or not query tracking is disabled globally -bool Query::DoDisableQueryTracking = false; - -/// @brief whether a warning in an AQL query should raise an error -bool Query::DoFailOnWarning = false; - /// @brief creates a query Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase, char const* queryString, size_t queryLength, - std::shared_ptr bindParameters, - std::shared_ptr options, QueryPart part) + std::shared_ptr const& bindParameters, + std::shared_ptr const& options, QueryPart part) : _id(0), _resourceMonitor(), _resources(&_resourceMonitor), @@ -102,6 +92,7 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase, _maxWarningCount(10), _warnings(), _startTime(TRI_microtime()), + _queryRegistry(application_features::ApplicationServer::getFeature("QueryRegistry")), _part(part), _contextOwnedByExterior(contextOwnedByExterior), _killed(false), @@ -152,8 +143,8 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase, /// @brief creates a query from VelocyPack Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase, - std::shared_ptr const queryStruct, - std::shared_ptr options, QueryPart part) + std::shared_ptr const& queryStruct, + std::shared_ptr const& options, QueryPart part) : _id(0), _resourceMonitor(), _resources(&_resourceMonitor), @@ -169,6 +160,7 @@ Query::Query(bool contextOwnedByExterior, TRI_vocbase_t* vocbase, _maxWarningCount(10), _warnings(), _startTime(TRI_microtime()), + _queryRegistry(application_features::ApplicationServer::getFeature("QueryRegistry")), _part(part), _contextOwnedByExterior(contextOwnedByExterior), _killed(false), @@ -211,21 +203,7 @@ Query::~Query() { _executor.reset(); - if (_context != nullptr) { - TRI_ASSERT(!_contextOwnedByExterior); - - // unregister transaction and resolver in context - ISOLATE; - TRI_GET_GLOBALS(); - auto ctx = - static_cast(v8g->_transactionContext); - if (ctx != nullptr) { - ctx->unregisterTransaction(); - } - - V8DealerFeature::DEALER->exitContext(_context); - _context = nullptr; - } + exitContext(); _ast.reset(); @@ -376,7 +354,7 @@ void Query::registerErrorCustom(int code, char const* details) { void Query::registerWarning(int code, char const* details) { TRI_ASSERT(code != TRI_ERROR_NO_ERROR); - if (DoFailOnWarning) { + if (_queryRegistry->failOnWarning()) { // make an error from each warning if requested if (details == nullptr) { THROW_ARANGO_EXCEPTION(code); @@ -1139,7 +1117,6 @@ void Query::exitContext() { V8DealerFeature::DEALER->exitContext(_context); _context = nullptr; } - TRI_ASSERT(_context == nullptr); } } @@ -1473,8 +1450,18 @@ Graph const* Query::lookupGraphByName(std::string const& name) { return g.release(); } + +size_t Query::memoryLimit() const { + uint64_t globalLimit = _queryRegistry->queryMemoryLimit(); + uint64_t value = getNumericOption("memoryLimit", globalLimit); + if (value > 0) { + return static_cast(value); + } + return 0; +} /// @brief returns the next query id TRI_voc_tick_t Query::NextId() { return NextQueryId.fetch_add(1, std::memory_order_seq_cst); } + diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index 87c3275107..6084dab9cb 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -53,6 +53,8 @@ namespace velocypack { class Builder; } +class QueryRegistryFeature; + namespace aql { struct AstNode; @@ -75,12 +77,12 @@ class Query { public: Query(bool, TRI_vocbase_t*, char const*, size_t, - std::shared_ptr, - std::shared_ptr, QueryPart); + std::shared_ptr const& bindParameters, + std::shared_ptr const& options, QueryPart); - Query(bool, TRI_vocbase_t*, - std::shared_ptr const, - std::shared_ptr, QueryPart); + Query(bool contextOwnedByExterior, TRI_vocbase_t*, + std::shared_ptr const& queryStruct, + std::shared_ptr const& options, QueryPart); ~Query(); @@ -185,13 +187,7 @@ class Query { } /// @brief memory limit for query - size_t memoryLimit() const { - uint64_t value = getNumericOption("memoryLimit", MemoryLimitValue); - if (value > 0) { - return static_cast(value); - } - return 0; - } + size_t memoryLimit() const; /// @brief maximum number of plans to produce int64_t literalSizeThreshold() const { @@ -274,37 +270,6 @@ class Query { /// NOTE: returns nullptr if there are no warnings. std::shared_ptr warningsToVelocyPack() const; - /// @brief fetch the query memory limit - static uint64_t MemoryLimit() { return MemoryLimitValue; } - - /// @brief set the query memory limit - static void MemoryLimit(uint64_t value) { - MemoryLimitValue = value; - } - - /// @brief fetch the global query tracking value - static bool DisableQueryTracking() { return DoDisableQueryTracking; } - - /// @brief turn off tracking globally - static void DisableQueryTracking(bool value) { - DoDisableQueryTracking = value; - } - - /// @brief whether a warning in an AQL query should raise an error - static bool FailOnWarning() { return DoFailOnWarning; } - - static void FailOnWarning(bool value) { - DoFailOnWarning = value; - } - - /// @brief fetch the global slow query threshold value - static double SlowQueryThreshold() { return SlowQueryThresholdValue; } - - /// @brief set global slow query threshold value - static void SlowQueryThreshold(double value) { - SlowQueryThresholdValue = value; - } - /// @brief get a description of the query's current state std::string getStateString() const; @@ -400,7 +365,7 @@ class Query { /// @brief the currently used V8 context V8Context* _context; - /// @brief warnings collected during execution + /// @brief graphs used in query, identified by name std::unordered_map _graphs; /// @brief the actual query string @@ -452,6 +417,8 @@ class Query { /// @brief query start time double _startTime; + QueryRegistryFeature const* _queryRegistry; + /// @brief the query part QueryPart const _part; @@ -463,18 +430,6 @@ class Query { /// @brief whether or not the query is a data modification query bool _isModificationQuery; - - /// @brief global memory limit for AQL queries - static uint64_t MemoryLimitValue; - - /// @brief global threshold value for slow queries - static double SlowQueryThresholdValue; - - /// @brief whether or not query tracking is disabled globally - static bool DoDisableQueryTracking; - - /// @brief whether a warning in an AQL query should raise an error - static bool DoFailOnWarning; }; } } diff --git a/arangod/Aql/QueryList.cpp b/arangod/Aql/QueryList.cpp index 1c112ed2a0..4cbfcd1d25 100644 --- a/arangod/Aql/QueryList.cpp +++ b/arangod/Aql/QueryList.cpp @@ -21,7 +21,8 @@ /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// -#include "Aql/QueryList.h" +#include "QueryList.h" +#include "ApplicationFeatures/ApplicationServer.h" #include "Aql/Query.h" #include "Aql/QueryProfile.h" #include "Basics/ReadLocker.h" @@ -29,8 +30,10 @@ #include "Basics/WriteLocker.h" #include "Basics/Exceptions.h" #include "Logger/Logger.h" +#include "RestServer/QueryRegistryFeature.h" #include "VocBase/vocbase.h" +using namespace arangodb; using namespace arangodb::aql; QueryEntryCopy::QueryEntryCopy(TRI_voc_tick_t id, @@ -41,21 +44,17 @@ QueryEntryCopy::QueryEntryCopy(TRI_voc_tick_t id, : id(id), queryString(std::move(queryString)), bindParameters(bindParameters), started(started), runTime(runTime), state(state) {} -double const QueryList::DefaultSlowQueryThreshold = 10.0; -size_t const QueryList::DefaultMaxSlowQueries = 64; -size_t const QueryList::DefaultMaxQueryStringLength = 4096; - /// @brief create a query list QueryList::QueryList(TRI_vocbase_t*) : _lock(), _current(), _slow(), _slowCount(0), - _enabled(!Query::DisableQueryTracking()), - _trackSlowQueries(!Query::DisableQueryTracking()), - _slowQueryThreshold(Query::SlowQueryThreshold()), - _maxSlowQueries(QueryList::DefaultMaxSlowQueries), - _maxQueryStringLength(QueryList::DefaultMaxQueryStringLength) { + _enabled(application_features::ApplicationServer::getFeature("QueryRegistry")->queryTracking()), + _trackSlowQueries(application_features::ApplicationServer::getFeature("QueryRegistry")->queryTracking()), + _slowQueryThreshold(application_features::ApplicationServer::getFeature("QueryRegistry")->slowThreshold()), + _maxSlowQueries(defaultMaxSlowQueries), + _maxQueryStringLength(defaultMaxQueryStringLength) { _current.reserve(64); } diff --git a/arangod/Aql/QueryList.h b/arangod/Aql/QueryList.h index 3d51e7014d..30215afbcb 100644 --- a/arangod/Aql/QueryList.h +++ b/arangod/Aql/QueryList.h @@ -162,6 +162,12 @@ class QueryList { private: std::string extractQueryString(Query const* query, size_t maxLength) const; + /// @brief default maximum number of slow queries to keep in list + static constexpr size_t defaultMaxSlowQueries = 64; + + /// @brief default max length of a query when returning it + static constexpr size_t defaultMaxQueryStringLength = 4096; + private: /// @brief r/w lock for the list arangodb::basics::ReadWriteLock _lock; @@ -189,15 +195,6 @@ class QueryList { /// @brief max length of query strings to return size_t _maxQueryStringLength; - - /// @brief default threshold for slow queries - static double const DefaultSlowQueryThreshold; - - /// @brief default maximum number of slow queries to keep in list - static size_t const DefaultMaxSlowQueries; - - /// @brief default max length of a query when returning it - static size_t const DefaultMaxQueryStringLength; }; } } diff --git a/arangod/RestServer/ConsoleFeature.cpp b/arangod/RestServer/ConsoleFeature.cpp index 953115ed61..de1e8e96d3 100644 --- a/arangod/RestServer/ConsoleFeature.cpp +++ b/arangod/RestServer/ConsoleFeature.cpp @@ -30,6 +30,8 @@ #include "RestServer/DatabaseFeature.h" #include "RestServer/ServerFeature.h" +#include + using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::options; diff --git a/arangod/RestServer/InitDatabaseFeature.cpp b/arangod/RestServer/InitDatabaseFeature.cpp index 37cdf301ab..89a4762ed4 100644 --- a/arangod/RestServer/InitDatabaseFeature.cpp +++ b/arangod/RestServer/InitDatabaseFeature.cpp @@ -23,12 +23,15 @@ #include "InitDatabaseFeature.h" #include "Basics/FileUtils.h" +#include "Basics/terminal-utils.h" #include "Logger/Logger.h" #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "RestServer/DatabasePathFeature.h" +#include + using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::basics; diff --git a/arangod/RestServer/QueryRegistryFeature.cpp b/arangod/RestServer/QueryRegistryFeature.cpp index 420aad5320..6f06711f14 100644 --- a/arangod/RestServer/QueryRegistryFeature.cpp +++ b/arangod/RestServer/QueryRegistryFeature.cpp @@ -80,22 +80,7 @@ void QueryRegistryFeature::collectOptions( new UInt64Parameter(&_queryCacheEntries)); } -void QueryRegistryFeature::validateOptions( - std::shared_ptr options) { -} - void QueryRegistryFeature::prepare() { - // set query memory limit - arangodb::aql::Query::MemoryLimit(_queryMemoryLimit); - - // set global query tracking flag - arangodb::aql::Query::DisableQueryTracking(!_queryTracking); - - arangodb::aql::Query::FailOnWarning(_failOnWarning); - - // set global threshold value for slow queries - arangodb::aql::Query::SlowQueryThreshold(_slowThreshold); - // configure the query cache std::pair cacheProperties{_queryCacheMode, _queryCacheEntries}; @@ -106,8 +91,7 @@ void QueryRegistryFeature::prepare() { QUERY_REGISTRY = _queryRegistry.get(); } -void QueryRegistryFeature::start() { -} +void QueryRegistryFeature::start() {} void QueryRegistryFeature::unprepare() { // clear the query registery diff --git a/arangod/RestServer/QueryRegistryFeature.h b/arangod/RestServer/QueryRegistryFeature.h index 17bda9acf9..afd15708d9 100644 --- a/arangod/RestServer/QueryRegistryFeature.h +++ b/arangod/RestServer/QueryRegistryFeature.h @@ -39,11 +39,15 @@ class QueryRegistryFeature final : public application_features::ApplicationFeatu public: void collectOptions(std::shared_ptr) override final; - void validateOptions(std::shared_ptr) override final; void prepare() override final; void start() override final; void unprepare() override final; + bool queryTracking() const { return _queryTracking; } + bool failOnWarning() const { return _failOnWarning; } + uint64_t queryMemoryLimit() const { return _queryMemoryLimit; } + double slowThreshold() const { return _slowThreshold; } + private: bool _queryTracking; bool _failOnWarning; diff --git a/arangod/RestServer/UnitTestsFeature.cpp b/arangod/RestServer/UnitTestsFeature.cpp index e1f7bf68c8..8c4763a98a 100644 --- a/arangod/RestServer/UnitTestsFeature.cpp +++ b/arangod/RestServer/UnitTestsFeature.cpp @@ -33,6 +33,8 @@ #include "V8Server/V8Context.h" #include "V8Server/V8DealerFeature.h" +#include + using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::options; diff --git a/arangosh/Export/ExportFeature.cpp b/arangosh/Export/ExportFeature.cpp index b912cffca4..724978da77 100644 --- a/arangosh/Export/ExportFeature.cpp +++ b/arangosh/Export/ExportFeature.cpp @@ -36,6 +36,7 @@ #include #include #include +#include using namespace arangodb; using namespace arangodb::basics; diff --git a/arangosh/Import/ImportFeature.cpp b/arangosh/Import/ImportFeature.cpp index 0ca2930c9f..48b0a7e7d0 100644 --- a/arangosh/Import/ImportFeature.cpp +++ b/arangosh/Import/ImportFeature.cpp @@ -33,6 +33,7 @@ #include "SimpleHttpClient/SimpleHttpClient.h" #include +#include using namespace arangodb; using namespace arangodb::basics; diff --git a/arangosh/Shell/ClientFeature.cpp b/arangosh/Shell/ClientFeature.cpp index af3dcfe80e..095185492f 100644 --- a/arangosh/Shell/ClientFeature.cpp +++ b/arangosh/Shell/ClientFeature.cpp @@ -32,6 +32,8 @@ #include "SimpleHttpClient/SimpleHttpClient.h" #include "Ssl/ssl-helper.h" +#include + using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::httpclient; diff --git a/arangosh/Shell/ConsoleFeature.cpp b/arangosh/Shell/ConsoleFeature.cpp index 0ae48dc0b4..0c054cf473 100644 --- a/arangosh/Shell/ConsoleFeature.cpp +++ b/arangosh/Shell/ConsoleFeature.cpp @@ -31,6 +31,8 @@ #include "ProgramOptions/Section.h" #include "Shell/ClientFeature.h" +#include + using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::options; diff --git a/lib/ApplicationFeatures/ApplicationServer.cpp b/lib/ApplicationFeatures/ApplicationServer.cpp index 81b345f83a..e02724c035 100644 --- a/lib/ApplicationFeatures/ApplicationServer.cpp +++ b/lib/ApplicationFeatures/ApplicationServer.cpp @@ -29,6 +29,8 @@ #include "Logger/Logger.h" #include "ProgramOptions/ArgumentParser.h" +#include + using namespace arangodb::application_features; using namespace arangodb::basics; using namespace arangodb::options; diff --git a/lib/ApplicationFeatures/ConfigFeature.cpp b/lib/ApplicationFeatures/ConfigFeature.cpp index eea0303f92..5d15e11a77 100644 --- a/lib/ApplicationFeatures/ConfigFeature.cpp +++ b/lib/ApplicationFeatures/ConfigFeature.cpp @@ -33,6 +33,7 @@ #include "ProgramOptions/IniFileParser.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" +#include "ProgramOptions/Translator.h" using namespace arangodb; using namespace arangodb::basics; @@ -71,7 +72,7 @@ void ConfigFeature::collectOptions(std::shared_ptr options) { void ConfigFeature::loadOptions(std::shared_ptr options, char const* binaryPath) { for (auto const& def : _defines) { - DefineEnvironment(def); + arangodb::options::DefineEnvironment(def); } loadConfigFile(options, _progname, binaryPath); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7ff02e2515..224daa0b7f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -188,6 +188,9 @@ add_library(${LIB_ARANGO} STATIC Logger/LoggerBufferFeature.cpp Logger/LoggerFeature.cpp Logger/LoggerStream.cpp + ProgramOptions/Option.cpp + ProgramOptions/ProgramOptions.cpp + ProgramOptions/Section.cpp ProgramOptions/Translator.cpp Random/RandomFeature.cpp Random/RandomGenerator.cpp diff --git a/lib/ProgramOptions/IniFileParser.h b/lib/ProgramOptions/IniFileParser.h index f8c9678ea5..25a0cbce12 100644 --- a/lib/ProgramOptions/IniFileParser.h +++ b/lib/ProgramOptions/IniFileParser.h @@ -26,7 +26,6 @@ #include "Basics/FileUtils.h" #include -#include #include #include "ProgramOptions/ProgramOptions.h" diff --git a/lib/ProgramOptions/Option.cpp b/lib/ProgramOptions/Option.cpp new file mode 100644 index 0000000000..99390a5b79 --- /dev/null +++ b/lib/ProgramOptions/Option.cpp @@ -0,0 +1,177 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2016 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +//////////////////////////////////////////////////////////////////////////////// + +#include "Option.h" + +#include "ProgramOptions/Parameters.h" + +#include +#include + +#include + +using namespace arangodb::options; + +// create an option, consisting of single string +Option::Option(std::string const& value, std::string const& description, + Parameter* parameter, bool hidden, bool obsolete) + : section(), + name(), + description(description), + shorthand(), + parameter(parameter), + hidden(hidden), + obsolete(obsolete) { + auto parts = splitName(value); + section = parts.first; + name = parts.second; + + size_t const pos = name.find(','); + if (pos != std::string::npos) { + shorthand = stripShorthand(name.substr(pos + 1)); + name = name.substr(0, pos); + } +} + +void Option::toVPack(VPackBuilder& builder) const { + parameter->toVPack(builder); +} + +// print help for an option +// the special search string "." will show help for all sections, even if hidden +void Option::printHelp(std::string const& search, size_t tw, size_t ow, bool) const { + if (search == "." || !hidden) { + std::cout << " " << pad(nameWithType(), ow) << " "; + + std::string value = description; + if (obsolete) { + value += " (obsolete option)"; + } else { + std::string description = parameter->description(); + if (!description.empty()) { + value.append(". "); + value.append(description); + } + value += " (default: " + parameter->valueString() + ")"; + } + auto parts = wordwrap(value, tw - ow - 6); + size_t const n = parts.size(); + for (size_t i = 0; i < n; ++i) { + std::cout << trim(parts[i]) << std::endl; + if (i < n - 1) { + std::cout << " " << pad("", ow) << " "; + } + } + } +} + +// determine the width of an option help string +size_t Option::optionsWidth() const { + if (hidden) { + return 0; + } + + return nameWithType().size(); +} + +// strip the "--" from a string +std::string Option::stripPrefix(std::string const& name) { + size_t pos = name.find("--"); + if (pos == 0) { + // strip initial "--" + return name.substr(2); + } + return name; +} + +// strip the "-" from a string +std::string Option::stripShorthand(std::string const& name) { + size_t pos = name.find("-"); + if (pos == 0) { + // strip initial "-" + return name.substr(1); + } + return name; +} + +// split an option name at the ".", if it exists +std::pair Option::splitName(std::string name) { + std::string section; + name = stripPrefix(name); + // split at "." + size_t pos = name.find("."); + if (pos == std::string::npos) { + // global option + section = ""; + } else { + // section-specific option + section = name.substr(0, pos); + name = name.substr(pos + 1); + } + + return std::make_pair(section, name); +} + +std::vector Option::wordwrap(std::string const& value, + size_t size) { + std::vector result; + std::string next = value; + + if (size > 0) { + while (next.size() > size) { + size_t m = next.find_last_of("., ", size - 1); + + if (m == std::string::npos || m < size / 2) { + m = size; + } else { + m += 1; + } + + result.emplace_back(next.substr(0, m)); + next = next.substr(m); + } + } + + result.emplace_back(next); + + return result; +} + +// right-pad a string +std::string Option::pad(std::string const& value, size_t length) { + size_t const valueLength = value.size(); + if (valueLength > length) { + return value.substr(0, length); + } + if (valueLength == length) { + return value; + } + return value + std::string(length - valueLength, ' '); +} + +std::string Option::trim(std::string const& value) { + size_t const pos = value.find_first_not_of(" \t\n\r"); + if (pos == std::string::npos) { + return ""; + } + return value.substr(pos); +} diff --git a/lib/ProgramOptions/Option.h b/lib/ProgramOptions/Option.h index 3fe0de726a..0953c3497b 100644 --- a/lib/ProgramOptions/Option.h +++ b/lib/ProgramOptions/Option.h @@ -27,41 +27,21 @@ #include "ProgramOptions/Parameters.h" #include -#include - -#include namespace arangodb { namespace options { +struct Parameter; + // a single program option container struct Option { // options are default copy-constructible and default movable // create an option, consisting of single string Option(std::string const& value, std::string const& description, - Parameter* parameter, bool hidden, bool obsolete) - : section(), - name(), - description(description), - shorthand(), - parameter(parameter), - hidden(hidden), - obsolete(obsolete) { - auto parts = splitName(value); - section = parts.first; - name = parts.second; + Parameter* parameter, bool hidden, bool obsolete); - size_t const pos = name.find(','); - if (pos != std::string::npos) { - shorthand = stripShorthand(name.substr(pos + 1)); - name = name.substr(0, pos); - } - } - - void toVPack(VPackBuilder& builder) const { - parameter->toVPack(builder); - } + void toVPack(arangodb::velocypack::Builder& builder) const; // get display name for the option std::string displayName() const { return "--" + fullName(); } @@ -76,127 +56,31 @@ struct Option { // print help for an option // the special search string "." will show help for all sections, even if hidden - void printHelp(std::string const& search, size_t tw, size_t ow, bool) const { - if (search == "." || !hidden) { - std::cout << " " << pad(nameWithType(), ow) << " "; - - std::string value = description; - if (obsolete) { - value += " (obsolete option)"; - } else { - std::string description = parameter->description(); - if (!description.empty()) { - value.append(". "); - value.append(description); - } - value += " (default: " + parameter->valueString() + ")"; - } - auto parts = wordwrap(value, tw - ow - 6); - size_t const n = parts.size(); - for (size_t i = 0; i < n; ++i) { - std::cout << trim(parts[i]) << std::endl; - if (i < n - 1) { - std::cout << " " << pad("", ow) << " "; - } - } - } - } + void printHelp(std::string const& search, size_t tw, size_t ow, bool) const; std::string nameWithType() const { return displayName() + " " + parameter->typeDescription(); } // determine the width of an option help string - size_t optionsWidth() const { - if (hidden) { - return 0; - } - - return nameWithType().size(); - } + size_t optionsWidth() const; // strip the "--" from a string - static std::string stripPrefix(std::string const& name) { - size_t pos = name.find("--"); - if (pos == 0) { - // strip initial "--" - return name.substr(2); - } - return name; - } + static std::string stripPrefix(std::string const& name); // strip the "-" from a string - static std::string stripShorthand(std::string const& name) { - size_t pos = name.find("-"); - if (pos == 0) { - // strip initial "-" - return name.substr(1); - } - return name; - } + static std::string stripShorthand(std::string const& name); // split an option name at the ".", if it exists - static std::pair splitName(std::string name) { - std::string section; - name = stripPrefix(name); - // split at "." - size_t pos = name.find("."); - if (pos == std::string::npos) { - // global option - section = ""; - } else { - // section-specific option - section = name.substr(0, pos); - name = name.substr(pos + 1); - } - - return std::make_pair(section, name); - } + static std::pair splitName(std::string name); static std::vector wordwrap(std::string const& value, - size_t size) { - std::vector result; - std::string next = value; - - if (size > 0) { - while (next.size() > size) { - size_t m = next.find_last_of("., ", size - 1); - - if (m == std::string::npos || m < size / 2) { - m = size; - } else { - m += 1; - } - - result.emplace_back(next.substr(0, m)); - next = next.substr(m); - } - } - - result.emplace_back(next); - - return result; - } + size_t size); // right-pad a string - static std::string pad(std::string const& value, size_t length) { - size_t const valueLength = value.size(); - if (valueLength > length) { - return value.substr(0, length); - } - if (valueLength == length) { - return value; - } - return value + std::string(length - valueLength, ' '); - } + static std::string pad(std::string const& value, size_t length); - static std::string trim(std::string const& value) { - size_t const pos = value.find_first_not_of(" \t\n\r"); - if (pos == std::string::npos) { - return ""; - } - return value.substr(pos); - } + static std::string trim(std::string const& value); std::string section; std::string name; diff --git a/lib/ProgramOptions/ProgramOptions.cpp b/lib/ProgramOptions/ProgramOptions.cpp new file mode 100644 index 0000000000..f36b2340a0 --- /dev/null +++ b/lib/ProgramOptions/ProgramOptions.cpp @@ -0,0 +1,444 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2016 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +//////////////////////////////////////////////////////////////////////////////// + +#include "ProgramOptions.h" +#include "Basics/levenshtein.h" +#include "Basics/shell-colors.h" +#include "Basics/terminal-utils.h" +#include "ProgramOptions/Option.h" +#include "ProgramOptions/Section.h" +#include "ProgramOptions/Translator.h" + +#include +#include + +#include + +#define ARANGODB_PROGRAM_OPTIONS_PROGNAME "#progname#" + +using namespace arangodb::options; + +ProgramOptions::ProgramOptions(char const* progname, std::string const& usage, + std::string const& more, + char const* binaryPath) + : _progname(progname), + _usage(usage), + _more(more), + _terminalWidth(TRI_ColumnsWidth), + _similarity(TRI_Levenshtein), + _processingResult(), + _sealed(false), + _overrideOptions(false), + _binaryPath(binaryPath){ + // find progname wildcard in string + size_t const pos = _usage.find(ARANGODB_PROGRAM_OPTIONS_PROGNAME); + + if (pos != std::string::npos) { + // and replace it with actual program name + _usage = usage.substr(0, pos) + _progname + + _usage.substr(pos + strlen(ARANGODB_PROGRAM_OPTIONS_PROGNAME)); + } + + _translator = EnvironmentTranslator; +} + +// sets a value translator +void ProgramOptions::setTranslator( + std::function translator) { + _translator = translator; +} + +// prints usage information +void ProgramOptions::printUsage() const { std::cout << _usage << std::endl << std::endl; } + +// prints a help for all options, or the options of a section +// the special search string "*" will show help for all sections +// the special search string "." will show help for all sections, even if +// hidden +void ProgramOptions::printHelp(std::string const& search) const { + bool const colors = (isatty(STDOUT_FILENO) != 0); + printUsage(); + + size_t const tw = _terminalWidth(); + size_t const ow = optionsWidth(); + + for (auto const& it : _sections) { + if (search == "*" || search == "." || search == it.second.name) { + it.second.printHelp(search, tw, ow, colors); + } + } + + if (search == "*") { + printSectionsHelp(); + } +} + +// prints the names for all section help options +void ProgramOptions::printSectionsHelp() const { + char const* colorStart; + char const* colorEnd; + + if (isatty(STDOUT_FILENO)) { + colorStart = TRI_SHELL_COLOR_BRIGHT; + colorEnd = TRI_SHELL_COLOR_RESET; + } else { + colorStart = colorEnd = ""; + } + + // print names of sections + std::cout << _more; + for (auto const& it : _sections) { + if (!it.second.name.empty() && it.second.hasOptions()) { + std::cout << " " << colorStart << "--help-" << it.second.name + << colorEnd; + } + } + std::cout << std::endl; +} + +// returns a VPack representation of the option values +VPackBuilder ProgramOptions::toVPack(bool onlyTouched, + std::unordered_set const& exclude) const { + VPackBuilder builder; + builder.openObject(); + + walk( + [&builder, &exclude](Section const&, Option const& option) { + std::string full(option.fullName()); + if (exclude.find(full) != exclude.end()) { + // excluded option + return; + } + + // add key + builder.add(VPackValue(full)); + + // add value + option.toVPack(builder); + }, + onlyTouched); + + builder.close(); + return builder; +} + +// translate a shorthand option +std::string ProgramOptions::translateShorthand(std::string const& name) const { + auto it = _shorthands.find(name); + + if (it == _shorthands.end()) { + return name; + } + return (*it).second; +} + +void ProgramOptions::walk(std::function const& callback, + bool onlyTouched) const { + for (auto const& it : _sections) { + if (it.second.obsolete) { + // obsolete section. ignore it + continue; + } + for (auto const& it2 : it.second.options) { + if (it2.second.obsolete) { + // obsolete option. ignore it + continue; + } + if (onlyTouched && !_processingResult.touched(it2.second.fullName())) { + // option not touched. skip over it + continue; + } + callback(it.second, it2.second); + } + } +} + +// checks whether a specific option exists +// if the option does not exist, this will flag an error +bool ProgramOptions::require(std::string const& name) { + auto parts = Option::splitName(name); + auto it = _sections.find(parts.first); + + if (it == _sections.end()) { + return unknownOption(name); + } + + auto it2 = (*it).second.options.find(parts.second); + + if (it2 == (*it).second.options.end()) { + return unknownOption(name); + } + + return true; +} + +// sets a value for an option +bool ProgramOptions::setValue(std::string const& name, std::string const& value) { + if (!_overrideOptions && _processingResult.frozen(name)) { + // option already frozen. don't override it + return true; + } + + auto parts = Option::splitName(name); + auto it = _sections.find(parts.first); + + if (it == _sections.end()) { + return unknownOption(name); + } + + if ((*it).second.obsolete) { + // section is obsolete. ignore it + return true; + } + + auto it2 = (*it).second.options.find(parts.second); + + if (it2 == (*it).second.options.end()) { + return unknownOption(name); + } + + auto& option = (*it2).second; + if (option.obsolete) { + // option is obsolete. ignore it + _processingResult.touch(name); + return true; + } + + std::string result = option.parameter->set(_translator(value, _binaryPath)); + + if (!result.empty()) { + // parameter validation failed + return fail("error setting value for option '--" + name + "': " + result); + } + + _processingResult.touch(name); + + return true; +} + +// finalizes a pass, copying touched into frozen +void ProgramOptions::endPass() { + if (_overrideOptions) { + return; + } + for (auto const& it : _processingResult._touched) { + _processingResult.freeze(it); + } +} + +// check whether or not an option requires a value +bool ProgramOptions::requiresValue(std::string const& name) const { + auto parts = Option::splitName(name); + auto it = _sections.find(parts.first); + + if (it == _sections.end()) { + return false; + } + + auto it2 = (*it).second.options.find(parts.second); + + if (it2 == (*it).second.options.end()) { + return false; + } + + return (*it2).second.parameter->requiresValue(); +} + +// returns an option description +std::string ProgramOptions::getDescription(std::string const& name) { + auto parts = Option::splitName(name); + auto it = _sections.find(parts.first); + + if (it == _sections.end()) { + return ""; + } + + auto it2 = (*it).second.options.find(parts.second); + + if (it2 == (*it).second.options.end()) { + return ""; + } + + return (*it2).second.description; +} + +// handle an unknown option +bool ProgramOptions::unknownOption(std::string const& name) { + char const* colorStart; + char const* colorEnd; + + if (isatty(STDERR_FILENO)) { + colorStart = TRI_SHELL_COLOR_BRIGHT; + colorEnd = TRI_SHELL_COLOR_RESET; + } else { + colorStart = colorEnd = ""; + } + + fail(std::string("unknown option '") + colorStart + "--" + name + colorEnd + + "'"); + + auto similarOptions = similar(name, 8, 4); + if (!similarOptions.empty()) { + if (similarOptions.size() == 1) { + std::cerr << "Did you mean this?" << std::endl; + } else { + std::cerr << "Did you mean one of these?" << std::endl; + } + // determine maximum width + size_t maxWidth = 0; + for (auto const& it : similarOptions) { + maxWidth = (std::max)(maxWidth, it.size()); + } + + for (auto const& it : similarOptions) { + std::cerr << " " << colorStart << Option::pad(it, maxWidth) << colorEnd + << " " << getDescription(it) << std::endl; + } + std::cerr << std::endl; + } + + auto it = _oldOptions.find(name); + if (it != _oldOptions.end()) { + // a now removed or renamed option was specified... + auto& now = (*it).second; + if (now.empty()) { + std::cerr << "Please note that the specified option '" << colorStart + << "--" << name << colorEnd + << "' has been removed in this ArangoDB version"; + } else { + std::cerr << "Please note that the specified option '" << colorStart + << "--" << name << colorEnd << "' has been renamed to '--" + << colorStart << now << colorEnd + << "' in this ArangoDB version"; + } + + std::cerr + << std::endl + << "Please be sure to read the manual section about changed options" + << std::endl + << std::endl; + } + + std::cerr << "Use " << colorStart << "--help" << colorEnd << " or " + << colorStart << "--help-all" << colorEnd + << " to get an overview of available options" << std::endl + << std::endl; + + return false; +} + +// report an error (callback from parser) +bool ProgramOptions::fail(std::string const& message) { + _processingResult.failed(true); + std::cerr << "Error while processing " << _context << ":" << std::endl; + failNotice(message); + std::cerr << std::endl; + return false; +} + +void ProgramOptions::failNotice(std::string const& message) { + _processingResult.failed(true); + std::cerr << " " << message << std::endl; +} + +// add a positional argument (callback from parser) +void ProgramOptions::addPositional(std::string const& value) { + _processingResult._positionals.emplace_back(value); +} + +// adds an option to the list of options +void ProgramOptions::addOption(Option const& option) { + checkIfSealed(); + auto it = _sections.find(option.section); + + if (it == _sections.end()) { + throw std::logic_error( + std::string("no section defined for program option ") + + option.displayName()); + } + + if (!option.shorthand.empty()) { + if (!_shorthands.emplace(option.shorthand, option.fullName()).second) { + throw std::logic_error( + std::string("shorthand option already defined for option ") + + option.displayName()); + } + } + + (*it).second.options.emplace(option.name, option); +} + +// determine maximum width of all options labels +size_t ProgramOptions::optionsWidth() const { + size_t width = 0; + for (auto const& it : _sections) { + width = (std::max)(width, it.second.optionsWidth()); + } + return width; +} + +// check if the options are already sealed and throw if yes +void ProgramOptions::checkIfSealed() const { + if (_sealed) { + throw std::logic_error("program options are already sealed"); + } +} + +// get a list of similar options +std::vector ProgramOptions::similar(std::string const& value, int cutOff, + size_t maxResults) { + std::vector result; + + if (_similarity != nullptr) { + // build a sorted map of similar values first + std::multimap distances; + // walk over all options + walk( + [this, &value, &distances](Section const&, Option const& option) { + if (option.fullName() != value) { + distances.emplace(_similarity(value, option.fullName()), + option.displayName()); + } + }, + false); + + // now return the ones that have an edit distance not higher than the + // cutOff value + int last = 0; + for (auto const& it : distances) { + if (last > 1 && it.first > 2 * last) { + break; + } + if (it.first > cutOff) { + continue; + } + result.emplace_back(it.second); + if (result.size() >= maxResults) { + break; + } + last = it.first; + } + } + + return result; +} + diff --git a/lib/ProgramOptions/ProgramOptions.h b/lib/ProgramOptions/ProgramOptions.h index 2b0596de73..071f7e19d9 100644 --- a/lib/ProgramOptions/ProgramOptions.h +++ b/lib/ProgramOptions/ProgramOptions.h @@ -25,16 +25,10 @@ #include "Basics/Common.h" -#include -#include - -#include "Basics/levenshtein.h" -#include "Basics/terminal-utils.h" #include "ProgramOptions/Option.h" #include "ProgramOptions/Section.h" -#include "ProgramOptions/Translator.h" -#define ARANGODB_PROGRAM_OPTIONS_PROGNAME "#progname#" +#include namespace arangodb { namespace options { @@ -102,35 +96,11 @@ class ProgramOptions { ProgramOptions(char const* progname, std::string const& usage, std::string const& more, - const char *binaryPath, - TerminalWidthFuncType const& terminalWidth = TRI_ColumnsWidth, - SimilarityFuncType const& similarity = TRI_Levenshtein) - : _progname(progname), - _usage(usage), - _more(more), - _terminalWidth(terminalWidth), - _similarity(similarity), - _processingResult(), - _sealed(false), - _overrideOptions(false), - _binaryPath(binaryPath){ - // find progname wildcard in string - size_t const pos = _usage.find(ARANGODB_PROGRAM_OPTIONS_PROGNAME); - - if (pos != std::string::npos) { - // and replace it with actual program name - _usage = usage.substr(0, pos) + _progname + - _usage.substr(pos + strlen(ARANGODB_PROGRAM_OPTIONS_PROGNAME)); - } - - _translator = EnvironmentTranslator; - } + char const* binaryPath); // sets a value translator void setTranslator( - std::function translator) { - _translator = translator; - } + std::function translator); // return a const reference to the processing result ProcessingResult const& processingResult() const { return _processingResult; } @@ -205,201 +175,40 @@ class ProgramOptions { } // prints usage information - void printUsage() const { std::cout << _usage << std::endl << std::endl; } + void printUsage() const; // prints a help for all options, or the options of a section // the special search string "*" will show help for all sections // the special search string "." will show help for all sections, even if // hidden - void printHelp(std::string const& search) const { - bool const colors = (isatty(STDOUT_FILENO) != 0); - printUsage(); - - size_t const tw = _terminalWidth(); - size_t const ow = optionsWidth(); - - for (auto const& it : _sections) { - if (search == "*" || search == "." || search == it.second.name) { - it.second.printHelp(search, tw, ow, colors); - } - } - - if (search == "*") { - printSectionsHelp(); - } - } + void printHelp(std::string const& search) const; // prints the names for all section help options - void printSectionsHelp() const { - char const* colorStart; - char const* colorEnd; - - if (isatty(STDOUT_FILENO)) { - colorStart = TRI_SHELL_COLOR_BRIGHT; - colorEnd = TRI_SHELL_COLOR_RESET; - } else { - colorStart = colorEnd = ""; - } - - // print names of sections - std::cout << _more; - for (auto const& it : _sections) { - if (!it.second.name.empty() && it.second.hasOptions()) { - std::cout << " " << colorStart << "--help-" << it.second.name - << colorEnd; - } - } - std::cout << std::endl; - } - + void printSectionsHelp() const; + // returns a VPack representation of the option values - VPackBuilder toVPack(bool onlyTouched, - std::unordered_set const& exclude) const { - VPackBuilder builder; - builder.openObject(); - - walk( - [&builder, &exclude](Section const&, Option const& option) { - std::string full(option.fullName()); - if (exclude.find(full) != exclude.end()) { - // excluded option - return; - } - - // add key - builder.add(VPackValue(full)); - - // add value - option.toVPack(builder); - }, - onlyTouched); - - builder.close(); - return builder; - } + arangodb::velocypack::Builder toVPack(bool onlyTouched, + std::unordered_set const& exclude) const; // translate a shorthand option - std::string translateShorthand(std::string const& name) const { - auto it = _shorthands.find(name); - - if (it == _shorthands.end()) { - return name; - } - return (*it).second; - } - + std::string translateShorthand(std::string const& name) const; + void walk(std::function const& callback, - bool onlyTouched) const { - for (auto const& it : _sections) { - if (it.second.obsolete) { - // obsolete section. ignore it - continue; - } - for (auto const& it2 : it.second.options) { - if (it2.second.obsolete) { - // obsolete option. ignore it - continue; - } - if (onlyTouched && !_processingResult.touched(it2.second.fullName())) { - // option not touched. skip over it - continue; - } - callback(it.second, it2.second); - } - } - } + bool onlyTouched) const; // checks whether a specific option exists // if the option does not exist, this will flag an error - bool require(std::string const& name) { - auto parts = Option::splitName(name); - auto it = _sections.find(parts.first); - - if (it == _sections.end()) { - return unknownOption(name); - } - - auto it2 = (*it).second.options.find(parts.second); - - if (it2 == (*it).second.options.end()) { - return unknownOption(name); - } - - return true; - } + bool require(std::string const& name); // sets a value for an option - bool setValue(std::string const& name, std::string const& value) { - if (!_overrideOptions && _processingResult.frozen(name)) { - // option already frozen. don't override it - return true; - } - - auto parts = Option::splitName(name); - auto it = _sections.find(parts.first); - - if (it == _sections.end()) { - return unknownOption(name); - } - - if ((*it).second.obsolete) { - // section is obsolete. ignore it - return true; - } - - auto it2 = (*it).second.options.find(parts.second); - - if (it2 == (*it).second.options.end()) { - return unknownOption(name); - } - - auto& option = (*it2).second; - if (option.obsolete) { - // option is obsolete. ignore it - _processingResult.touch(name); - return true; - } - - std::string result = option.parameter->set(_translator(value, _binaryPath)); - - if (!result.empty()) { - // parameter validation failed - return fail("error setting value for option '--" + name + "': " + result); - } - - _processingResult.touch(name); - - return true; - } - + bool setValue(std::string const& name, std::string const& value); + // finalizes a pass, copying touched into frozen - void endPass() { - if (_overrideOptions) { - return; - } - for (auto const& it : _processingResult._touched) { - _processingResult.freeze(it); - } - } + void endPass(); // check whether or not an option requires a value - bool requiresValue(std::string const& name) const { - auto parts = Option::splitName(name); - auto it = _sections.find(parts.first); - - if (it == _sections.end()) { - return false; - } - - auto it2 = (*it).second.options.find(parts.second); - - if (it2 == (*it).second.options.end()) { - return false; - } - - return (*it2).second.parameter->requiresValue(); - } - + bool requiresValue(std::string const& name) const; + // returns a pointer to an option value, specified by option name // returns a nullptr if the option is unknown template @@ -423,184 +232,32 @@ class ProgramOptions { } // returns an option description - std::string getDescription(std::string const& name) { - auto parts = Option::splitName(name); - auto it = _sections.find(parts.first); - - if (it == _sections.end()) { - return ""; - } - - auto it2 = (*it).second.options.find(parts.second); - - if (it2 == (*it).second.options.end()) { - return ""; - } - - return (*it2).second.description; - } + std::string getDescription(std::string const& name); // handle an unknown option - bool unknownOption(std::string const& name) { - char const* colorStart; - char const* colorEnd; - - if (isatty(STDERR_FILENO)) { - colorStart = TRI_SHELL_COLOR_BRIGHT; - colorEnd = TRI_SHELL_COLOR_RESET; - } else { - colorStart = colorEnd = ""; - } - - fail(std::string("unknown option '") + colorStart + "--" + name + colorEnd + - "'"); - - auto similarOptions = similar(name, 8, 4); - if (!similarOptions.empty()) { - if (similarOptions.size() == 1) { - std::cerr << "Did you mean this?" << std::endl; - } else { - std::cerr << "Did you mean one of these?" << std::endl; - } - // determine maximum width - size_t maxWidth = 0; - for (auto const& it : similarOptions) { - maxWidth = (std::max)(maxWidth, it.size()); - } - - for (auto const& it : similarOptions) { - std::cerr << " " << colorStart << Option::pad(it, maxWidth) << colorEnd - << " " << getDescription(it) << std::endl; - } - std::cerr << std::endl; - } - - auto it = _oldOptions.find(name); - if (it != _oldOptions.end()) { - // a now removed or renamed option was specified... - auto& now = (*it).second; - if (now.empty()) { - std::cerr << "Please note that the specified option '" << colorStart - << "--" << name << colorEnd - << "' has been removed in this ArangoDB version"; - } else { - std::cerr << "Please note that the specified option '" << colorStart - << "--" << name << colorEnd << "' has been renamed to '--" - << colorStart << now << colorEnd - << "' in this ArangoDB version"; - } - - std::cerr - << std::endl - << "Please be sure to read the manual section about changed options" - << std::endl - << std::endl; - } - - std::cerr << "Use " << colorStart << "--help" << colorEnd << " or " - << colorStart << "--help-all" << colorEnd - << " to get an overview of available options" << std::endl - << std::endl; - - return false; - } + bool unknownOption(std::string const& name); // report an error (callback from parser) - bool fail(std::string const& message) { - _processingResult.failed(true); - std::cerr << "Error while processing " << _context << ":" << std::endl; - failNotice(message); - std::cerr << std::endl; - return false; - } + bool fail(std::string const& message); - void failNotice(std::string const& message) { - _processingResult.failed(true); - std::cerr << " " << message << std::endl; - } + void failNotice(std::string const& message); // add a positional argument (callback from parser) - void addPositional(std::string const& value) { - _processingResult._positionals.emplace_back(value); - } + void addPositional(std::string const& value); private: // adds an option to the list of options - void addOption(Option const& option) { - checkIfSealed(); - auto it = _sections.find(option.section); - - if (it == _sections.end()) { - throw std::logic_error( - std::string("no section defined for program option ") + - option.displayName()); - } - - if (!option.shorthand.empty()) { - if (!_shorthands.emplace(option.shorthand, option.fullName()).second) { - throw std::logic_error( - std::string("shorthand option already defined for option ") + - option.displayName()); - } - } - - (*it).second.options.emplace(option.name, option); - } + void addOption(Option const& option); // determine maximum width of all options labels - size_t optionsWidth() const { - size_t width = 0; - for (auto const& it : _sections) { - width = (std::max)(width, it.second.optionsWidth()); - } - return width; - } + size_t optionsWidth() const; // check if the options are already sealed and throw if yes - void checkIfSealed() const { - if (_sealed) { - throw std::logic_error("program options are already sealed"); - } - } + void checkIfSealed() const; // get a list of similar options std::vector similar(std::string const& value, int cutOff, - size_t maxResults) { - std::vector result; - - if (_similarity != nullptr) { - // build a sorted map of similar values first - std::multimap distances; - // walk over all options - walk( - [this, &value, &distances](Section const&, Option const& option) { - if (option.fullName() != value) { - distances.emplace(_similarity(value, option.fullName()), - option.displayName()); - } - }, - false); - - // now return the ones that have an edit distance not higher than the - // cutOff value - int last = 0; - for (auto const& it : distances) { - if (last > 1 && it.first > 2 * last) { - break; - } - if (it.first > cutOff) { - continue; - } - result.emplace_back(it.second); - if (result.size() >= maxResults) { - break; - } - last = it.first; - } - } - - return result; - } + size_t maxResults); private: // name of binary (i.e. argv[0]) diff --git a/lib/ProgramOptions/Section.cpp b/lib/ProgramOptions/Section.cpp new file mode 100644 index 0000000000..9bcb6f14c2 --- /dev/null +++ b/lib/ProgramOptions/Section.cpp @@ -0,0 +1,81 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2016 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Jan Steemann +//////////////////////////////////////////////////////////////////////////////// + +#include "Section.h" +#include "Basics/shell-colors.h" +#include "ProgramOptions/Option.h" + +#include + +using namespace arangodb::options; + +// adds a program option to the section +void Section::addOption(Option const& option) { options.emplace(option.name, option); } + +// whether or not the section has (displayable) options +bool Section::hasOptions() const { + if (!hidden) { + for (auto const& it : options) { + if (!it.second.hidden) { + return true; + } + } + } + return false; +} + +// print help for a section +// the special search string "." will show help for all sections, even if hidden +void Section::printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const { + if (search != "." && (hidden || !hasOptions())) { + return; + } + + if (colors) { + std::cout << "Section '" << TRI_SHELL_COLOR_BRIGHT << displayName() << TRI_SHELL_COLOR_RESET << "' (" << description << ")" + << std::endl; + } else { + std::cout << "Section '" << displayName() << "' (" << description << ")" + << std::endl; + } + + // propagate print command to options + for (auto const& it : options) { + it.second.printHelp(search, tw, ow, colors); + } + + std::cout << std::endl; +} + +// determine display width for a section +size_t Section::optionsWidth() const { + size_t width = 0; + + if (!hidden) { + for (auto const& it : options) { + width = (std::max)(width, it.second.optionsWidth()); + } + } + + return width; +} + diff --git a/lib/ProgramOptions/Section.h b/lib/ProgramOptions/Section.h index 88953f4a8c..60b4c4ef63 100644 --- a/lib/ProgramOptions/Section.h +++ b/lib/ProgramOptions/Section.h @@ -24,8 +24,6 @@ #define ARANGODB_PROGRAM_OPTIONS_SECTION_H 1 #include "Basics/Common.h" -#include "Basics/shell-colors.h" - #include "ProgramOptions/Option.h" namespace arangodb { @@ -44,58 +42,20 @@ struct Section { obsolete(obsolete) {} // adds a program option to the section - void addOption(Option const& option) { options.emplace(option.name, option); } + void addOption(Option const& option); // get display name for the section std::string displayName() const { return alias.empty() ? name : alias; } // whether or not the section has (displayable) options - bool hasOptions() const { - if (!hidden) { - for (auto const& it : options) { - if (!it.second.hidden) { - return true; - } - } - } - return false; - } + bool hasOptions() const; // print help for a section // the special search string "." will show help for all sections, even if hidden - void printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const { - if (search != "." && (hidden || !hasOptions())) { - return; - } - - if (colors) { - std::cout << "Section '" << TRI_SHELL_COLOR_BRIGHT << displayName() << TRI_SHELL_COLOR_RESET << "' (" << description << ")" - << std::endl; - } else { - std::cout << "Section '" << displayName() << "' (" << description << ")" - << std::endl; - } - - // propagate print command to options - for (auto const& it : options) { - it.second.printHelp(search, tw, ow, colors); - } - - std::cout << std::endl; - } + void printHelp(std::string const& search, size_t tw, size_t ow, bool colors) const; // determine display width for a section - size_t optionsWidth() const { - size_t width = 0; - - if (!hidden) { - for (auto const& it : options) { - width = (std::max)(width, it.second.optionsWidth()); - } - } - - return width; - } + size_t optionsWidth() const; std::string name; std::string description; From 47e2648e44b289e54036a4963e2c85736b415aca Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 10:40:45 +0200 Subject: [PATCH 09/12] updated manual --- .../Manual/ReleaseNotes/NewFeatures32.md | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md b/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md index fb34b438f8..275108aefe 100644 --- a/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md +++ b/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md @@ -91,11 +91,6 @@ engine. The RocksDB storage engine in this release has a few known issues and missing features. These will be resolved in the following releases: -* index selectivity estimates are missing. All indexes will report their selectivity - estimate as `0.2`. This may lead to non-optimal indexes being used in a query. - -* the geo index is not yet implemented - * the number of documents reported for collections (`db..count()`) may be slightly wrong during transactions @@ -106,6 +101,19 @@ These will be resolved in the following releases: * the datafile debugger (arango-dfdb) cannot be used with this storage engine +* APIs that return collection properties or figures will return slightly different + attributes for the RocksDB engine than for the MMFiles engine. For example, the + attributes `journalSize`, `doCompact`, `indexBuckets` and `isVolatile` are present + in the MMFiles engine but not in the RocksDB engine. The memory usage figures reported + for collections in the RocksDB engine are estimate values, whereas they are + exact in the MMFiles engine. + +* the RocksDB engine does not support some operations which only make sense in the + context of the MMFiles engine. These are: + + - the `rotate` method on collections + - the `flush()` method for WAL files + ### RocksDB storage engine: supported index types @@ -123,6 +131,8 @@ supported there: "hash", "skiplist" and "persistent" are only used for compatibility with the MMFiles engine where these indexes existed in previous and the current version of ArangoDB. +* geo: user-defined index for proximity searches + * fulltext: user-defined sorted reverted index on words occurring in documents From d1f466327c59ed83bb33f428658c914a979e02c6 Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 10:42:51 +0200 Subject: [PATCH 10/12] updated manual --- Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md b/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md index 275108aefe..fc5a1dbe12 100644 --- a/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md +++ b/Documentation/Books/Manual/ReleaseNotes/NewFeatures32.md @@ -112,7 +112,12 @@ These will be resolved in the following releases: context of the MMFiles engine. These are: - the `rotate` method on collections - - the `flush()` method for WAL files + - the `flush` method for WAL files + +* the `any` operation to provide a random document from a collection is supported + by the RocksDB engine but the operation has much higher complexity than in the + MMFiles engine. It is therefore discouraged to call it for cases other than manual + inspection of a few documents in a collection. ### RocksDB storage engine: supported index types From 244e380f6357cb67ca0c1894f4700b692939ef08 Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 10:57:31 +0200 Subject: [PATCH 11/12] unintentionally broke the export --- arangod/RocksDBEngine/RocksDBCollectionExport.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arangod/RocksDBEngine/RocksDBCollectionExport.cpp b/arangod/RocksDBEngine/RocksDBCollectionExport.cpp index dd37e295bd..3188d64d4a 100644 --- a/arangod/RocksDBEngine/RocksDBCollectionExport.cpp +++ b/arangod/RocksDBEngine/RocksDBCollectionExport.cpp @@ -65,7 +65,14 @@ void RocksDBCollectionExport::run(size_t limit) { THROW_ARANGO_EXCEPTION(res); } - _vpack.reserve(limit); + size_t maxDocuments = _collection->numberDocuments(&trx); + if (limit > 0 && limit < maxDocuments) { + maxDocuments = limit; + } else { + limit = maxDocuments; + } + + _vpack.reserve(maxDocuments); ManagedDocumentResult mmdr; trx.invokeOnAllElements( From b2cd86ad8b8ef12784583d289c4469a7e5cae3ac Mon Sep 17 00:00:00 2001 From: jsteemann Date: Tue, 23 May 2017 11:01:57 +0200 Subject: [PATCH 12/12] fix for win32 --- lib/Logger/LoggerFeature.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Logger/LoggerFeature.cpp b/lib/Logger/LoggerFeature.cpp index 47073f05be..688d4993bc 100644 --- a/lib/Logger/LoggerFeature.cpp +++ b/lib/Logger/LoggerFeature.cpp @@ -28,6 +28,10 @@ #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" +#if _WIN32 +#include +#endif + using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::options;