mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/arangodb/arangodb into column-family
# Conflicts: # arangod/RocksDBEngine/RocksDBIndex.cpp # arangod/RocksDBEngine/RocksDBIndex.h # arangod/RocksDBEngine/RocksDBPrimaryIndex.cpp # arangod/RocksDBEngine/RocksDBPrimaryIndex.h
This commit is contained in:
commit
88977d204a
|
@ -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")
|
||||
|
|
|
@ -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.<collection>.count()`) may be
|
||||
slightly wrong during transactions
|
||||
|
||||
|
@ -106,6 +101,24 @@ 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
|
||||
|
||||
* 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
|
||||
|
||||
|
@ -123,6 +136,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
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class BindParameters {
|
|||
: _builder(nullptr), _parameters(), _processed(false) {}
|
||||
|
||||
/// @brief create the parameters
|
||||
explicit BindParameters(std::shared_ptr<arangodb::velocypack::Builder> builder)
|
||||
explicit BindParameters(std::shared_ptr<arangodb::velocypack::Builder> const& builder)
|
||||
: _builder(builder), _parameters(), _processed(false) {}
|
||||
|
||||
/// @brief destroy the parameters
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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<void(DocumentIdentifierToken const&)>& 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<AqlItemBlock> res;
|
||||
|
||||
std::function<void(DocumentIdentifierToken const& token)> callback;
|
||||
IndexIterator::TokenCallback callback;
|
||||
if (_indexes.size() > 1) {
|
||||
// Activate uniqueness checks
|
||||
callback = [&](DocumentIdentifierToken const& token) {
|
||||
|
|
|
@ -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<void(DocumentIdentifierToken const&)>&);
|
||||
bool readIndex(size_t atMost, IndexIterator::TokenCallback const&);
|
||||
|
||||
/// @brief frees the memory for all non-constant expressions
|
||||
void cleanupNonConstExpressions();
|
||||
|
|
|
@ -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<TRI_voc_tick_t> 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<VPackBuilder> bindParameters,
|
||||
std::shared_ptr<VPackBuilder> options, QueryPart part)
|
||||
std::shared_ptr<VPackBuilder> const& bindParameters,
|
||||
std::shared_ptr<VPackBuilder> 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<arangodb::QueryRegistryFeature>("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<VPackBuilder> const queryStruct,
|
||||
std::shared_ptr<VPackBuilder> options, QueryPart part)
|
||||
std::shared_ptr<VPackBuilder> const& queryStruct,
|
||||
std::shared_ptr<VPackBuilder> 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<arangodb::QueryRegistryFeature>("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<arangodb::transaction::V8Context*>(v8g->_transactionContext);
|
||||
if (ctx != nullptr) {
|
||||
ctx->unregisterTransaction();
|
||||
}
|
||||
|
||||
V8DealerFeature::DEALER->exitContext(_context);
|
||||
_context = nullptr;
|
||||
}
|
||||
exitContext();
|
||||
|
||||
_ast.reset();
|
||||
|
||||
|
@ -376,10 +354,12 @@ 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
|
||||
// note: this will throw!
|
||||
registerErrorCustom(code, details);
|
||||
if (details == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(code);
|
||||
}
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(code, details);
|
||||
}
|
||||
|
||||
if (_warnings.size() > _maxWarningCount) {
|
||||
|
@ -1137,7 +1117,6 @@ void Query::exitContext() {
|
|||
V8DealerFeature::DEALER->exitContext(_context);
|
||||
_context = nullptr;
|
||||
}
|
||||
TRI_ASSERT(_context == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1471,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<uint64_t>("memoryLimit", globalLimit);
|
||||
if (value > 0) {
|
||||
return static_cast<size_t>(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<arangodb::velocypack::Builder>,
|
||||
std::shared_ptr<arangodb::velocypack::Builder>, QueryPart);
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& bindParameters,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& options, QueryPart);
|
||||
|
||||
Query(bool, TRI_vocbase_t*,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const,
|
||||
std::shared_ptr<arangodb::velocypack::Builder>, QueryPart);
|
||||
Query(bool contextOwnedByExterior, TRI_vocbase_t*,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& queryStruct,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> const& options, QueryPart);
|
||||
|
||||
~Query();
|
||||
|
||||
|
@ -185,13 +187,7 @@ class Query {
|
|||
}
|
||||
|
||||
/// @brief memory limit for query
|
||||
size_t memoryLimit() const {
|
||||
uint64_t value = getNumericOption<decltype(MemoryLimitValue)>("memoryLimit", MemoryLimitValue);
|
||||
if (value > 0) {
|
||||
return static_cast<size_t>(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<arangodb::velocypack::Builder> 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<std::string, Graph*> _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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<arangodb::QueryRegistryFeature>("QueryRegistry")->queryTracking()),
|
||||
_trackSlowQueries(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")->queryTracking()),
|
||||
_slowQueryThreshold(application_features::ApplicationServer::getFeature<arangodb::QueryRegistryFeature>("QueryRegistry")->slowThreshold()),
|
||||
_maxSlowQueries(defaultMaxSlowQueries),
|
||||
_maxQueryStringLength(defaultMaxQueryStringLength) {
|
||||
_current.reserve(64);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<void(DocumentIdentifierToken const& token)> TokenCallback;
|
||||
typedef std::function<void(DocumentIdentifierToken const& token,
|
||||
arangodb::velocypack::Slice extra)>
|
||||
|
|
|
@ -391,7 +391,7 @@ void GraphStore<V, E>::_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<V, E>::_loadEdges(transaction::Methods* trx,
|
|||
}
|
||||
}
|
||||
};
|
||||
while (cursor->getMore(cb, 1000)) {
|
||||
}
|
||||
cursor->all(cb);
|
||||
|
||||
// Add up all added elements
|
||||
vertexEntry._edgeCount += added;
|
||||
|
|
|
@ -87,8 +87,7 @@ void RestEdgesHandler::readCursor(
|
|||
THROW_ARANGO_EXCEPTION(cursor->code);
|
||||
}
|
||||
|
||||
while (cursor->getMore(cb, 1000)) {
|
||||
}
|
||||
cursor->all(cb);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "RestServer/DatabaseFeature.h"
|
||||
#include "RestServer/ServerFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -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 <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -80,22 +80,7 @@ void QueryRegistryFeature::collectOptions(
|
|||
new UInt64Parameter(&_queryCacheEntries));
|
||||
}
|
||||
|
||||
void QueryRegistryFeature::validateOptions(
|
||||
std::shared_ptr<ProgramOptions> 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<std::string, size_t> 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
|
||||
|
|
|
@ -39,11 +39,15 @@ class QueryRegistryFeature final : public application_features::ApplicationFeatu
|
|||
|
||||
public:
|
||||
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
|
||||
void validateOptions(std::shared_ptr<options::ProgramOptions>) 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;
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "V8Server/V8Context.h"
|
||||
#include "V8Server/V8DealerFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -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);
|
||||
TRI_voc_cid_t cid = _logicalCollection->cid();
|
||||
RocksDBTransactionState* state = rocksutils::toRocksTransactionState(trx);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include "VocBase/LogicalCollection.h"
|
||||
#include "VocBase/ticks.h"
|
||||
|
||||
#include <rocksdb/comparator.h>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::rocksutils;
|
||||
|
||||
|
@ -196,16 +198,26 @@ void RocksDBIndex::truncate(transaction::Methods* trx) {
|
|||
|
||||
rocksdb::ReadOptions options = mthds->readOptions();
|
||||
rocksdb::Slice end = indexBounds.end();
|
||||
<<<<<<< HEAD
|
||||
rocksdb::Comparator const* cmp = this->comparator();
|
||||
options.iterate_upper_bound = &end;
|
||||
=======
|
||||
options.iterate_upper_bound = &end;
|
||||
|
||||
>>>>>>> b2cd86ad8b8ef12784583d289c4469a7e5cae3ac
|
||||
|
||||
std::unique_ptr<rocksdb::Iterator> iter = mthds->NewIterator(options, _cf);
|
||||
iter->Seek(indexBounds.start());
|
||||
|
||||
<<<<<<< HEAD
|
||||
while (iter->Valid() && cmp->Compare(iter->key(), end) < 0) {
|
||||
TRI_ASSERT(_objectId == RocksDBKey::objectId(iter->key()));
|
||||
|
||||
Result r = mthds->Delete(_cf, iter->key());
|
||||
=======
|
||||
while (iter->Valid() && _cmp->Compare(iter->key(), end) < 0) {
|
||||
Result r = mthds->Delete(iter->key());
|
||||
>>>>>>> b2cd86ad8b8ef12784583d289c4469a7e5cae3ac
|
||||
if (!r.ok()) {
|
||||
THROW_ARANGO_EXCEPTION(r);
|
||||
}
|
||||
|
|
|
@ -31,13 +31,11 @@
|
|||
#include <rocksdb/status.h>
|
||||
|
||||
namespace rocksdb {class Comparator; class ColumnFamilyHandle;}
|
||||
|
||||
namespace arangodb {
|
||||
namespace cache {
|
||||
class Cache;
|
||||
}
|
||||
class LogicalCollection;
|
||||
class RocksDBComparator;
|
||||
class RocksDBCounterManager;
|
||||
class RocksDBMethods;
|
||||
|
||||
|
@ -105,9 +103,12 @@ class RocksDBIndex : public Index {
|
|||
|
||||
virtual void recalculateEstimates();
|
||||
|
||||
<<<<<<< HEAD
|
||||
rocksdb::ColumnFamilyHandle* columnFamily() const{
|
||||
return _cf;
|
||||
}
|
||||
=======
|
||||
>>>>>>> b2cd86ad8b8ef12784583d289c4469a7e5cae3ac
|
||||
rocksdb::Comparator const* comparator() const;
|
||||
|
||||
protected:
|
||||
|
@ -126,7 +127,11 @@ class RocksDBIndex : public Index {
|
|||
|
||||
protected:
|
||||
uint64_t _objectId;
|
||||
<<<<<<< HEAD
|
||||
rocksdb::ColumnFamilyHandle* _cf;
|
||||
=======
|
||||
rocksdb::Comparator* _cmp;
|
||||
>>>>>>> b2cd86ad8b8ef12784583d289c4469a7e5cae3ac
|
||||
|
||||
mutable std::shared_ptr<cache::Cache> _cache;
|
||||
// we use this boolean for testing whether _cache is set.
|
||||
|
|
|
@ -197,7 +197,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
|
||||
|
@ -205,6 +205,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);
|
||||
|
@ -282,7 +285,7 @@ RocksDBAnyIndexIterator::RocksDBAnyIndexIterator(
|
|||
_iterator->Prev();
|
||||
}
|
||||
}
|
||||
if (!_iterator->Valid()) {
|
||||
if (!_iterator->Valid() || outOfRange()) {
|
||||
_iterator->Seek(_bounds.start());
|
||||
}
|
||||
}
|
||||
|
@ -562,8 +565,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,
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
namespace rocksdb {
|
||||
class Iterator;
|
||||
class Comparator;
|
||||
}
|
||||
|
||||
namespace arangodb {
|
||||
|
|
|
@ -50,7 +50,6 @@ class SortCondition;
|
|||
struct Variable;
|
||||
} // namespace aql
|
||||
class LogicalCollection;
|
||||
class RocksDBComparator;
|
||||
class RocksDBPrimaryIndex;
|
||||
class RocksDBVPackIndex;
|
||||
namespace transaction {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<void(DocumentIdentifierToken const& token)> 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);
|
||||
|
|
|
@ -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<void(DocumentIdentifierToken const& token)> const& callback,
|
||||
bool next(IndexIterator::TokenCallback const& callback,
|
||||
uint64_t batchSize);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief convenience function to retrieve all results
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void getAll(
|
||||
std::function<void(DocumentIdentifierToken const& token)> 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&);
|
||||
|
||||
};
|
||||
|
|
|
@ -255,7 +255,7 @@ static void JS_AllQuery(v8::FunctionCallbackInfo<v8::Value> 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()));
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/property_tree/detail/xml_parser_utils.hpp>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "SimpleHttpClient/SimpleHttpClient.h"
|
||||
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "SimpleHttpClient/SimpleHttpClient.h"
|
||||
#include "Ssl/ssl-helper.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::httpclient;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "ProgramOptions/Section.h"
|
||||
#include "Shell/ClientFeature.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "Logger/Logger.h"
|
||||
#include "ProgramOptions/ArgumentParser.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -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<ProgramOptions> options) {
|
|||
void ConfigFeature::loadOptions(std::shared_ptr<ProgramOptions> options,
|
||||
char const* binaryPath) {
|
||||
for (auto const& def : _defines) {
|
||||
DefineEnvironment(def);
|
||||
arangodb::options::DefineEnvironment(def);
|
||||
}
|
||||
|
||||
loadConfigFile(options, _progname, binaryPath);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#include "ProgramOptions/ProgramOptions.h"
|
||||
#include "ProgramOptions/Section.h"
|
||||
|
||||
#if _WIN32
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "Basics/FileUtils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
|
|
|
@ -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 <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<std::string, std::string> 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<std::string> Option::wordwrap(std::string const& value,
|
||||
size_t size) {
|
||||
std::vector<std::string> 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);
|
||||
}
|
|
@ -27,41 +27,21 @@
|
|||
#include "ProgramOptions/Parameters.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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<std::string, std::string> 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<std::string, std::string> splitName(std::string name);
|
||||
|
||||
static std::vector<std::string> wordwrap(std::string const& value,
|
||||
size_t size) {
|
||||
std::vector<std::string> 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;
|
||||
|
|
|
@ -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 <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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<std::string(std::string const&, char const*)> 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<std::string> 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<void(Section const&, Option const&)> 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<std::string> ProgramOptions::similar(std::string const& value, int cutOff,
|
||||
size_t maxResults) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (_similarity != nullptr) {
|
||||
// build a sorted map of similar values first
|
||||
std::multimap<int, std::string> 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;
|
||||
}
|
||||
|
|
@ -25,16 +25,10 @@
|
|||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#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 <velocypack/Builder.h>
|
||||
|
||||
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<std::string(std::string const&, char const*)> translator) {
|
||||
_translator = translator;
|
||||
}
|
||||
std::function<std::string(std::string const&, char const*)> 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<std::string> 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<std::string> 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<void(Section const&, Option const&)> 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 <typename T>
|
||||
|
@ -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<std::string> similar(std::string const& value, int cutOff,
|
||||
size_t maxResults) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (_similarity != nullptr) {
|
||||
// build a sorted map of similar values first
|
||||
std::multimap<int, std::string> 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])
|
||||
|
|
|
@ -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 <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue