diff --git a/3rdParty/V8/CMakeLists.txt b/3rdParty/V8/CMakeLists.txt index c539776bd2..a67bcf321f 100644 --- a/3rdParty/V8/CMakeLists.txt +++ b/3rdParty/V8/CMakeLists.txt @@ -61,10 +61,11 @@ list(APPEND V8_GYP_ARGS ) if (CROSS_COMPILING) - list(APPEND V8_GYP_ARGS -DGYP_CROSSCOMPILE=1) + list(APPEND V8_GYP_ARGS + -Dhost_arch=${V8_PROC_ARCH} + -DGYP_CROSSCOMPILE=1) endif() - ################################################################################ ## ICU EXPORTS ################################################################################ diff --git a/CHANGELOG b/CHANGELOG index 13aefd5a82..d6fea469da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -48,6 +48,8 @@ devel v3.0.6 (XXXX-XX-XX) ------------------- +* fixed issue #2026 + * slightly better error diagnostics for AQL query compilation and replication * fixed issue #2018 diff --git a/Documentation/Books/Manual/GettingStarted/Installing/Linux.mdpp b/Documentation/Books/Manual/GettingStarted/Installing/Linux.mdpp index e3efd91baf..a012dccb6e 100644 --- a/Documentation/Books/Manual/GettingStarted/Installing/Linux.mdpp +++ b/Documentation/Books/Manual/GettingStarted/Installing/Linux.mdpp @@ -39,8 +39,8 @@ For unattended installation, you can set the password using the [debconf helpers](http://www.microhowto.info/howto/perform_an_unattended_installation_of_a_debian_package.html). ``` -echo arangodb3 arangodb/password password NEWPASSWORD | debconf-set-selections -echo arangodb3 arangodb/password_again password NEWPASSWORD | debconf-set-selections +echo arangodb3 arangodb3/password password NEWPASSWORD | debconf-set-selections +echo arangodb3 arangodb3/password_again password NEWPASSWORD | debconf-set-selections ``` The commands should be executed prior to the installation. diff --git a/Installation/Jenkins/build.sh b/Installation/Jenkins/build.sh index 17e66d48d4..ae69a96b75 100755 --- a/Installation/Jenkins/build.sh +++ b/Installation/Jenkins/build.sh @@ -300,6 +300,16 @@ while [ $# -gt 0 ]; do CLEAN_IT=1 shift ;; + --cxArmV8) + ARMV8=1 + CXGCC=1 + shift + ;; + --cxArmV7) + ARMV7=1 + CXGCC=1 + shift + ;; *) echo "Unknown option: $1" exit 1 @@ -330,9 +340,31 @@ elif [ "$CLANG36" == 1 ]; then CC=/usr/bin/clang-3.6 CXX=/usr/bin/clang++-3.6 CXXFLAGS="${CXXFLAGS} -std=c++11" +elif [ "${CXGCC}" = 1 ]; then + if [ "${ARMV8}" = 1 ]; then + export TOOL_PREFIX=aarch64-linux-gnu + BUILD_DIR="${BUILD_DIR}-ARMV8" + elif [ "${ARMV7}" = 1 ]; then + export TOOL_PREFIX=aarch64-linux-gnu + BUILD_DIR="${BUILD_DIR}-ARMV7" + else + echo "Unknown CX-Compiler!" + exit 1; + fi + + export CXX=$TOOL_PREFIX-g++ + export AR=$TOOL_PREFIX-ar + export RANLIB=$TOOL_PREFIX-ranlib + export CC=$TOOL_PREFIX-gcc + export LD=$TOOL_PREFIX-g++ + export LINK=$TOOL_PREFIX-g++ + export STRIP=$TOOL_PREFIX-strip + CONFIGURE_OPTIONS="${CONFIGURE_OPTIONS} -DCROSS_COMPILING=true" fi + + if [ "$SANITIZE" == 1 ]; then if [ "$GCC5" == 1 ]; then CFLAGS="${CFLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fno-sanitize=vptr" @@ -415,7 +447,7 @@ SOURCE_DIR=`compute_relative ${DST}/ ${SRC}/` if [ ! -f Makefile -o ! -f CMakeCache.txt ]; then CFLAGS="${CFLAGS}" CXXFLAGS="${CXXFLAGS}" LDFLAGS="${LDFLAGS}" LIBS="${LIBS}" \ - cmake ${SOURCE_DIR} ${CONFIGURE_OPTIONS} -G "${GENERATOR}" + cmake ${SOURCE_DIR} ${CONFIGURE_OPTIONS} -G "${GENERATOR}" || exit 1 fi ${MAKE_CMD_PREFIX} ${MAKE} ${MAKE_PARAMS} diff --git a/Installation/Windows/Templates/NSIS.template.in b/Installation/Windows/Templates/NSIS.template.in index a5fbf25afe..79068497c8 100755 --- a/Installation/Windows/Templates/NSIS.template.in +++ b/Installation/Windows/Templates/NSIS.template.in @@ -946,7 +946,7 @@ Function default_installation_directory Return FunctionEnd -Function assign_proper_access_rigths +Function assign_proper_access_rights StrCpy $0 "0" AccessControl::GrantOnFile \ "$INSTDIR" "(BU)" "GenericRead + GenericWrite + GenericExecute" @@ -963,7 +963,7 @@ Function is_writable ; is does not matter if we do some errors here ${If} $TRI_INSTALL_ALL_USERS == '1' CreateDirectory $INSTDIR - Call assign_proper_access_rigths + Call assign_proper_access_rights ${EndIf} FunctionEnd diff --git a/Installation/Windows/client/Templates/NSIS.template.in b/Installation/Windows/client/Templates/NSIS.template.in index 8e93494170..b17eb90723 100755 --- a/Installation/Windows/client/Templates/NSIS.template.in +++ b/Installation/Windows/client/Templates/NSIS.template.in @@ -1,4 +1,5 @@ ; CPack install script designed for a nmake build +; TODO !addplugindir '@CPACK_PLUGIN_PATH@/AccessControl/Plugins' ;-------------------------------- ; Include LogicLib for more readable code @@ -484,14 +485,10 @@ FunctionEnd ;-------------------------------- ;Pages - !define MUI_PAGE_CUSTOMFUNCTION_PRE skip_page !insertmacro MUI_PAGE_WELCOME - !define MUI_PAGE_CUSTOMFUNCTION_PRE skip_page !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" - Page custom InstallOptionsPage skip_page - !define MUI_PAGE_CUSTOMFUNCTION_PRE default_installation_directory !define MUI_PAGE_CUSTOMFUNCTION_LEAVE check_installation_directory !insertmacro MUI_PAGE_DIRECTORY @@ -709,7 +706,6 @@ displayAgain: StrCmp $PASSWORD $PASSWORD_AGAIN +3 0 MessageBox MB_OK|MB_ICONSTOP "Passwords don't match, try again" Goto displayAgain - done: Pop ${TEMP1} Return @@ -725,20 +721,17 @@ FunctionEnd Function default_installation_directory ; Read variables which defines if arango should be installed as Service - !insertmacro MUI_INSTALLOPTIONS_READ $R2 "NSIS.InstallOptions.ini" "Field 2" "State" - !insertmacro MUI_INSTALLOPTIONS_READ $R3 "NSIS.InstallOptions.ini" "Field 3" "State" - !insertmacro MUI_INSTALLOPTIONS_READ $R4 "NSIS.InstallOptions.ini" "Field 4" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $R2 "NSIS.InstallOptions.ini" "Field 2" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $R3 "NSIS.InstallOptions.ini" "Field 3" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $R4 "NSIS.InstallOptions.ini" "Field 4" "State" - ${If} $R3 == '1' - StrCpy $TRI_INSTALL_TYPE 'AllUsers' - ${EndIf} - - ${If} $R4 == '1' - StrCpy $TRI_INSTALL_TYPE 'SingleUser' - ${EndIf} - Call read_options + ${If} $R3 == '1' + StrCpy $TRI_INSTALL_TYPE 'AllUsers' ${EndIf} + ${If} $R4 == '1' + StrCpy $TRI_INSTALL_TYPE 'SingleUser' + ${EndIf} ${Switch} $TRI_INSTALL_TYPE ${Case} 'SingleUser' @@ -750,25 +743,25 @@ Function default_installation_directory Return FunctionEnd -Function assign_proper_access_rigths - StrCpy $0 "0" - AccessControl::GrantOnFile \ - "$INSTDIR" "(BU)" "GenericRead + GenericWrite + GenericExecute" - Pop $R0 - ${If} $R0 == error - Pop $R0 - StrCpy $0 "1" - DetailPrint `AccessControl error: $R0` - ; MessageBox MB_OK "target directory $INSTDIR can not get cannot get correct access rigths" - ${EndIf} -FunctionEnd +; TODO Function assign_proper_access_rights +; TODO StrCpy $0 "0" +; TODO AccessControl::GrantOnFile \ +; TODO "$INSTDIR" "(BU)" "GenericRead + GenericWrite + GenericExecute" +; TODO Pop $R0 +; TODO ${If} $R0 == error +; TODO Pop $R0 +; TODO StrCpy $0 "1" +; TODO DetailPrint `AccessControl error: $R0` +; TODO ; MessageBox MB_OK "target directory $INSTDIR can not get cannot get correct access rigths" +; TODO ${EndIf} +; TODO FunctionEnd Function is_writable ; is does not matter if we do some errors here ${If} $TRI_INSTALL_ALL_USERS == '1' CreateDirectory $INSTDIR - Call assign_proper_access_rigths - ${EndIf} + ; TODO Call assign_proper_access_rights + ${EndIf} FunctionEnd Function check_installation_directory diff --git a/Installation/debian/preinst b/Installation/debian/preinst index 69db01c070..2c882e4b20 100755 --- a/Installation/debian/preinst +++ b/Installation/debian/preinst @@ -2,7 +2,7 @@ set -e getent group arangodb >/dev/null || groupadd -r arangodb -getent passwd arangodb >/dev/null || useradd -r -g arangodb -d /usr/share/arangodb -s /bin/false -c "ArangoDB Application User" arangodb +getent passwd arangodb >/dev/null || useradd -r -g arangodb -d /usr/share/arangodb3 -s /bin/false -c "ArangoDB Application User" arangodb install -o arangodb -g arangodb -m 755 -d /var/lib/arangodb3 install -o arangodb -g arangodb -m 755 -d /var/lib/arangodb3-apps diff --git a/arangod/Aql/CollectNode.cpp b/arangod/Aql/CollectNode.cpp index a6c800925e..293ce028f6 100644 --- a/arangod/Aql/CollectNode.cpp +++ b/arangod/Aql/CollectNode.cpp @@ -192,6 +192,7 @@ struct UserVarFinder final : public WalkerWorker { en->getType() == ExecutionNode::INDEX || en->getType() == ExecutionNode::ENUMERATE_LIST || en->getType() == ExecutionNode::TRAVERSAL || + en->getType() == ExecutionNode::SHORTEST_PATH || en->getType() == ExecutionNode::COLLECT) { depth += 1; } diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 066bd6d695..fa8069b441 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -581,7 +581,7 @@ ExecutionNode const* ExecutionNode::getLoop() const { auto type = node->getType(); if (type == ENUMERATE_COLLECTION || type == INDEX || type == TRAVERSAL || - type == ENUMERATE_LIST) { + type == ENUMERATE_LIST || type == SHORTEST_PATH) { return node; } } @@ -1147,7 +1147,7 @@ void ExecutionNode::RegisterPlan::after(ExecutionNode* en) { en->getVarsUsedLater(); std::vector const& varsUsedHere = en->getVariablesUsedHere(); - + // We need to delete those variables that have been used here but are not // used any more later: std::unordered_set regsToClear; @@ -1160,7 +1160,7 @@ void ExecutionNode::RegisterPlan::after(ExecutionNode* en) { if (it2 == varInfo.end()) { // report an error here to prevent crashing - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "missing variable #" + std::to_string(v->id) + " for node " + en->getTypeString() + " while planning registers"); + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "missing variable #" + std::to_string(v->id) + " (" + v->name + ") for node " + en->getTypeString() + " while planning registers"); } // finally adjust the variable inside the IN calculation diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index e16ef5a97f..4a3a38b457 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -749,7 +749,7 @@ ExecutionNode* ExecutionPlan::fromNodeTraversal(ExecutionNode* previous, return addDependency(previous, en); } -AstNode const* ExecutionPlan::parseTraversalVertexNode(ExecutionNode* previous, +AstNode const* ExecutionPlan::parseTraversalVertexNode(ExecutionNode*& previous, AstNode const* vertex) { if (vertex->type == NODE_TYPE_OBJECT && vertex->isConstant()) { size_t n = vertex->numMembers(); @@ -767,6 +767,8 @@ AstNode const* ExecutionPlan::parseTraversalVertexNode(ExecutionNode* previous, // operand is some misc expression auto calc = createTemporaryCalculation(vertex, previous); vertex = _ast->createNodeReference(getOutVariable(calc)); + // update previous so the caller has an updated value + previous = calc; } return vertex; @@ -1960,6 +1962,7 @@ bool ExecutionPlan::isDeadSimple() const { nodeType == ExecutionNode::ENUMERATE_COLLECTION || nodeType == ExecutionNode::ENUMERATE_LIST || nodeType == ExecutionNode::TRAVERSAL || + nodeType == ExecutionNode::SHORTEST_PATH || nodeType == ExecutionNode::INDEX) { // these node types are not simple return false; diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index f6902dbeb0..e36f04dc88 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -286,7 +286,7 @@ class ExecutionPlan { ExecutionNode* fromJson(arangodb::basics::Json const& Json); /// @brief create an vertex element for graph nodes - AstNode const* parseTraversalVertexNode(ExecutionNode*, AstNode const*); + AstNode const* parseTraversalVertexNode(ExecutionNode*&, AstNode const*); private: /// @brief map from node id to the actual node diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index 8f3699a386..3a0dc08335 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -32,6 +32,7 @@ #include "Aql/Function.h" #include "Aql/IndexNode.h" #include "Aql/ModificationNodes.h" +#include "Aql/ShortestPathNode.h" #include "Aql/SortCondition.h" #include "Aql/SortNode.h" #include "Aql/TraversalConditionFinder.h" @@ -324,7 +325,8 @@ void arangodb::aql::removeRedundantSortsRule(Optimizer* opt, } } else if (current->getType() == EN::ENUMERATE_LIST || current->getType() == EN::ENUMERATE_COLLECTION || - current->getType() == EN::TRAVERSAL) { + current->getType() == EN::TRAVERSAL || + current->getType() == EN::SHORTEST_PATH) { // ok, but we cannot remove two different sorts if one of these node // types is between them // example: in the following query, the one sort will be optimized @@ -764,10 +766,10 @@ void arangodb::aql::removeSortRandRule(Optimizer* opt, ExecutionPlan* plan, case EN::SUBQUERY: case EN::ENUMERATE_LIST: case EN::TRAVERSAL: + case EN::SHORTEST_PATH: case EN::INDEX: { - // if we found another SortNode, an CollectNode, FilterNode, a - // SubqueryNode, - // an EnumerateListNode, a TraversalNode or an IndexNode + // if we found another SortNode, a CollectNode, FilterNode, a + // SubqueryNode, an EnumerateListNode, a TraversalNode or an IndexNode // this means we cannot apply our optimization collectionNode = nullptr; current = nullptr; @@ -949,7 +951,9 @@ void arangodb::aql::moveCalculationsDownRule(Optimizer* opt, } else if (currentType == EN::INDEX || currentType == EN::ENUMERATE_COLLECTION || currentType == EN::ENUMERATE_LIST || - currentType == EN::TRAVERSAL || currentType == EN::COLLECT || + currentType == EN::TRAVERSAL || + currentType == EN::SHORTEST_PATH || + currentType == EN::COLLECT || currentType == EN::NORESULTS) { // we will not push further down than such nodes shouldMove = false; @@ -1241,6 +1245,17 @@ class arangodb::aql::RedundantCalculationsReplacer final std::unordered_map const& replacements) : _replacements(replacements) { } + + template + void replaceStartTargetVariables(ExecutionNode* en) { + auto node = static_cast(en); + if (node->_inStartVariable != nullptr) { + node->_inStartVariable = Variable::replace(node->_inStartVariable, _replacements); + } + if (node->_inTargetVariable != nullptr) { + node->_inTargetVariable = Variable::replace(node->_inTargetVariable, _replacements); + } + } template void replaceInVariable(ExecutionNode* en) { @@ -1290,6 +1305,11 @@ class arangodb::aql::RedundantCalculationsReplacer final replaceInVariable(en); break; } + + case EN::SHORTEST_PATH: { + replaceStartTargetVariables(en); + break; + } case EN::COLLECT: { auto node = static_cast(en); @@ -3589,7 +3609,7 @@ void arangodb::aql::patchUpdateStatementsRule(Optimizer* opt, } } - if (type == EN::TRAVERSAL) { + if (type == EN::TRAVERSAL || type == EN::SHORTEST_PATH) { // unclear what will be read by the traversal modified = false; break; diff --git a/arangod/Aql/ShortestPathNode.h b/arangod/Aql/ShortestPathNode.h index c3680586d1..aa78f647e9 100644 --- a/arangod/Aql/ShortestPathNode.h +++ b/arangod/Aql/ShortestPathNode.h @@ -38,6 +38,7 @@ namespace aql { /// @brief class ShortestPathNode class ShortestPathNode : public ExecutionNode { friend class ExecutionBlock; + friend class RedundantCalculationsReplacer; friend class ShortestPathBlock; /// @brief constructor with a vocbase and a collection name diff --git a/arangod/Cluster/ClusterComm.cpp b/arangod/Cluster/ClusterComm.cpp index 1e6b562a51..1997d87699 100644 --- a/arangod/Cluster/ClusterComm.cpp +++ b/arangod/Cluster/ClusterComm.cpp @@ -1356,6 +1356,11 @@ size_t ClusterComm::performSingleRequest( if (req.result.status == CL_COMM_BACKEND_UNAVAILABLE) { THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE); } + + if (req.result.status == CL_COMM_ERROR && req.result.result != nullptr + && req.result.result->getHttpReturnCode() == 503) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE); + } // Add correct recognition of content type later. basics::StringBuffer& buffer = req.result.result->getBody(); diff --git a/arangod/Cluster/ClusterMethods.cpp b/arangod/Cluster/ClusterMethods.cpp index c6b08e7eec..b86a144792 100644 --- a/arangod/Cluster/ClusterMethods.cpp +++ b/arangod/Cluster/ClusterMethods.cpp @@ -123,6 +123,11 @@ static void mergeResults( resultBody->openArray(); for (auto const& pair : reverseMapping) { VPackSlice arr = resultMap.find(pair.first)->second->slice(); + if (arr.isObject() && arr.hasKey("error") && arr.get("error").isBoolean() && arr.get("error").getBoolean()) { + // an error occurred, now rethrow the error + int res = arr.get("errorNum").getNumericValue(); + THROW_ARANGO_EXCEPTION(res); + } resultBody->add(arr.at(pair.second)); } resultBody->close(); @@ -736,6 +741,7 @@ int createDocumentOnCoordinator( bool useMultiple = slice.isArray(); int res = TRI_ERROR_NO_ERROR; + if (useMultiple) { VPackValueLength length = slice.length(); for (VPackValueLength idx = 0; idx < length; ++idx) { @@ -766,6 +772,7 @@ int createDocumentOnCoordinator( // Now prepare the requests: std::vector requests; auto body = std::make_shared(); + for (auto const& it : shardMap) { if (!useMultiple) { TRI_ASSERT(it.second.size() == 1); @@ -801,7 +808,7 @@ int createDocumentOnCoordinator( "shard:" + it.first, arangodb::GeneralRequest::RequestType::POST, baseUrl + StringUtils::urlEncode(it.first) + optsUrlPart, body); } - + // Perform the requests size_t nrDone = 0; cc->performRequests(requests, CL_DEFAULT_TIMEOUT, nrDone, Logger::REQUESTS); diff --git a/arangod/GeneralServer/GeneralServer.cpp b/arangod/GeneralServer/GeneralServer.cpp index a4728b15a3..a322c2762d 100644 --- a/arangod/GeneralServer/GeneralServer.cpp +++ b/arangod/GeneralServer/GeneralServer.cpp @@ -159,6 +159,9 @@ bool GeneralServer::handleRequestAsync(GeneralCommTask* task, if (res != TRI_ERROR_DISPATCHER_IS_STOPPING) { LOG(WARN) << "unable to add job to the job queue: " << TRI_errno_string(res); + } else { + task->handleSimpleError(GeneralResponse::ResponseCode::SERVICE_UNAVAILABLE); + return true; } // todo send info to async work manager? return false; @@ -194,6 +197,11 @@ bool GeneralServer::handleRequest(GeneralCommTask* task, // add the job to the dispatcher int res = DispatcherFeature::DISPATCHER->addJob(job, startThread); + if (res == TRI_ERROR_DISPATCHER_IS_STOPPING) { + task->handleSimpleError(GeneralResponse::ResponseCode::SERVICE_UNAVAILABLE); + return true; + } + // job is in queue now return res == TRI_ERROR_NO_ERROR; } diff --git a/arangod/RestServer/arangod.cpp b/arangod/RestServer/arangod.cpp index 48b1d16e7a..7943f0e8e4 100644 --- a/arangod/RestServer/arangod.cpp +++ b/arangod/RestServer/arangod.cpp @@ -164,6 +164,10 @@ static int runServer(int argc, char** argv) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangod terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/Benchmark/arangobench.cpp b/arangosh/Benchmark/arangobench.cpp index 89e6fb5e9a..8580580e13 100644 --- a/arangosh/Benchmark/arangobench.cpp +++ b/arangosh/Benchmark/arangobench.cpp @@ -64,6 +64,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangobench terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/Dump/arangodump.cpp b/arangosh/Dump/arangodump.cpp index f8d550139b..ca2a40d76e 100644 --- a/arangosh/Dump/arangodump.cpp +++ b/arangosh/Dump/arangodump.cpp @@ -60,6 +60,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangodump terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/Import/arangoimp.cpp b/arangosh/Import/arangoimp.cpp index 02ede9d4e9..e5bd8ae1cc 100644 --- a/arangosh/Import/arangoimp.cpp +++ b/arangosh/Import/arangoimp.cpp @@ -62,6 +62,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangoimp terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/Restore/arangorestore.cpp b/arangosh/Restore/arangorestore.cpp index eb4fe4d147..a059fc58f9 100644 --- a/arangosh/Restore/arangorestore.cpp +++ b/arangosh/Restore/arangorestore.cpp @@ -62,6 +62,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangorestore terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/Shell/arangosh.cpp b/arangosh/Shell/arangosh.cpp index 7876b1c67e..e31491b92f 100644 --- a/arangosh/Shell/arangosh.cpp +++ b/arangosh/Shell/arangosh.cpp @@ -72,6 +72,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangosh terminated because of an unhandled exception: " << ex.what(); diff --git a/arangosh/VPack/arangovpack.cpp b/arangosh/VPack/arangovpack.cpp index e78f866624..177858b729 100644 --- a/arangosh/VPack/arangovpack.cpp +++ b/arangosh/VPack/arangovpack.cpp @@ -56,6 +56,10 @@ int main(int argc, char* argv[]) { try { server.run(argc, argv); + if (server.helpShown()) { + // --help was displayed + ret = EXIT_SUCCESS; + } } catch (std::exception const& ex) { LOG(ERR) << "arangovpack terminated because of an unhandled exception: " << ex.what(); diff --git a/lib/ApplicationFeatures/ApplicationServer.cpp b/lib/ApplicationFeatures/ApplicationServer.cpp index 1b1691f2ae..87d33d6b17 100644 --- a/lib/ApplicationFeatures/ApplicationServer.cpp +++ b/lib/ApplicationFeatures/ApplicationServer.cpp @@ -166,6 +166,11 @@ void ApplicationServer::run(int argc, char* argv[]) { // file(s) parseOptions(argc, argv); + if (!_helpSection.empty()) { + // help shown. we can exit early + return; + } + // seal the options _options->seal(); @@ -285,17 +290,17 @@ void ApplicationServer::collectOptions() { void ApplicationServer::parseOptions(int argc, char* argv[]) { ArgumentParser parser(_options.get()); - std::string helpSection = parser.helpSection(argc, argv); + _helpSection = parser.helpSection(argc, argv); - if (!helpSection.empty()) { + if (!_helpSection.empty()) { // user asked for "--help" // translate "all" to "*" - if (helpSection == "all") { - helpSection = "*"; + if (_helpSection == "all") { + _helpSection = "*"; } - _options->printHelp(helpSection); - exit(EXIT_SUCCESS); + _options->printHelp(_helpSection); + return; } if (!parser.parse(argc, argv)) { diff --git a/lib/ApplicationFeatures/ApplicationServer.h b/lib/ApplicationFeatures/ApplicationServer.h index 65bbe79ce2..4166a9f3d1 100644 --- a/lib/ApplicationFeatures/ApplicationServer.h +++ b/lib/ApplicationFeatures/ApplicationServer.h @@ -166,6 +166,9 @@ class ApplicationServer { ~ApplicationServer(); + std::string helpSection() const { return _helpSection; } + bool helpShown() const { return !_helpSection.empty(); } + // adds a feature to the application server. the application server // will take ownership of the feature object and destroy it in its // destructor @@ -297,6 +300,9 @@ class ApplicationServer { // reporter for progress std::vector _progressReports; + + // help section displayed + std::string _helpSection; }; } } diff --git a/lib/Rest/FakeRequest.cpp b/lib/Rest/FakeRequest.cpp index 0088dba135..bee7bf4bcf 100644 --- a/lib/Rest/FakeRequest.cpp +++ b/lib/Rest/FakeRequest.cpp @@ -49,7 +49,9 @@ VPackSlice FakeRequest::payload(arangodb::velocypack::Options const* options) { if( _contentType == ContentType::JSON) { VPackParser parser(options); - parser.parse(_body, static_cast(_contentLength)); + if (_contentLength > 0) { + parser.parse(_body, static_cast(_contentLength)); + } _vpackBuilder = parser.steal(); return VPackSlice(_vpackBuilder->slice()); } else /*VPACK*/{