From f001c4f1aa6e7a84adb11409685b193027215b27 Mon Sep 17 00:00:00 2001 From: Willi Goesgens Date: Thu, 6 Nov 2014 17:49:16 +0100 Subject: [PATCH 01/12] Treat valgrind possibly lost errors --- lib/Rest/Endpoint.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Rest/Endpoint.cpp b/lib/Rest/Endpoint.cpp index 2d41e9b182..be660f12c2 100644 --- a/lib/Rest/Endpoint.cpp +++ b/lib/Rest/Endpoint.cpp @@ -260,26 +260,26 @@ Endpoint* Endpoint::factory (const Endpoint::EndpointType type, if (found != string::npos && found > 2 && found + 2 < copy.size()) { // hostname and port (e.g. [address]:port) uint16_t port = (uint16_t) StringUtils::uint32(copy.substr(found + 2)); - + std::string portStr = copy.substr(1, found - 1); return new EndpointIpV6(type, encryption, specification, listenBacklog, reuseAddress, - copy.substr(1, found - 1), + portStr, port); } found = copy.find("]", 1); if (found != string::npos && found > 2 && found + 1 == copy.size()) { // hostname only (e.g. [address]) - + std::string portStr = copy.substr(1, found - 1); return new EndpointIpV6(type, encryption, specification, listenBacklog, reuseAddress, - copy.substr(1, found - 1), + portStr, EndpointIp::_defaultPort); } @@ -293,13 +293,13 @@ Endpoint* Endpoint::factory (const Endpoint::EndpointType type, if (found != string::npos && found + 1 < copy.size()) { // hostname and port uint16_t port = (uint16_t) StringUtils::uint32(copy.substr(found + 1)); - + std::string portStr = copy.substr(0, found); return new EndpointIpV4(type, encryption, specification, listenBacklog, reuseAddress, - copy.substr(0, found), + portStr, port); } From 8e678c50dab9d9b3a5fa88ff723523004ce3d46b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 6 Nov 2014 20:14:02 +0100 Subject: [PATCH 02/12] mini optimization: share frequently occuring values --- arangod/Aql/Ast.cpp | 33 +++++++++++++++++++++++++++++++-- arangod/Aql/Ast.h | 12 ++++++++++++ arangod/Aql/AstNode.cpp | 34 ++++++++++++++++++++++++++++++++-- arangod/Aql/AstNode.h | 16 ++++++++++++++-- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index e28bccae3e..234aac5c96 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -57,13 +57,25 @@ AstNode const Ast::NullNode{ NODE_TYPE_VALUE, VALUE_TYPE_NULL }; /// @brief initialise a singleton false node instance //////////////////////////////////////////////////////////////////////////////// -AstNode const Ast::FalseNode{ false }; +AstNode const Ast::FalseNode{ false, VALUE_TYPE_BOOL }; //////////////////////////////////////////////////////////////////////////////// /// @brief initialise a singleton true node instance //////////////////////////////////////////////////////////////////////////////// -AstNode const Ast::TrueNode{ true }; +AstNode const Ast::TrueNode{ true, VALUE_TYPE_BOOL }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialise a singleton zero node instance +//////////////////////////////////////////////////////////////////////////////// + +AstNode const Ast::ZeroNode{ static_cast(0), VALUE_TYPE_INT }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialise a singleton empty string node instance +//////////////////////////////////////////////////////////////////////////////// + +AstNode const Ast::EmptyStringNode{ "", VALUE_TYPE_STRING }; //////////////////////////////////////////////////////////////////////////////// /// @brief inverse comparison operators @@ -606,6 +618,7 @@ AstNode* Ast::createNodeIterator (char const* variableName, //////////////////////////////////////////////////////////////////////////////// AstNode* Ast::createNodeValueNull () { + // performance optimization: // return a pointer to the singleton null node // note: this node is never registered nor freed return const_cast(&NullNode); @@ -616,11 +629,13 @@ AstNode* Ast::createNodeValueNull () { //////////////////////////////////////////////////////////////////////////////// AstNode* Ast::createNodeValueBool (bool value) { + // performance optimization: // return a pointer to the singleton bool nodes // note: these nodes are never registered nor freed if (value) { return const_cast(&TrueNode); } + return const_cast(&FalseNode); } @@ -629,6 +644,13 @@ AstNode* Ast::createNodeValueBool (bool value) { //////////////////////////////////////////////////////////////////////////////// AstNode* Ast::createNodeValueInt (int64_t value) { + if (value == 0) { + // performance optimization: + // return a pointer to the singleton zero node + // note: these nodes are never registered nor freed + return const_cast(&ZeroNode); + } + AstNode* node = createNode(NODE_TYPE_VALUE); node->setValueType(VALUE_TYPE_INT); node->setIntValue(value); @@ -657,6 +679,13 @@ AstNode* Ast::createNodeValueString (char const* value) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } + if (*value == '\0') { + // performance optimization: + // return a pointer to the singleton empty string node + // note: these nodes are never registered nor freed + return const_cast(&EmptyStringNode); + } + AstNode* node = createNode(NODE_TYPE_VALUE); node->setValueType(VALUE_TYPE_STRING); node->setStringValue(value); diff --git a/arangod/Aql/Ast.h b/arangod/Aql/Ast.h index f765df291c..7d593ebd96 100644 --- a/arangod/Aql/Ast.h +++ b/arangod/Aql/Ast.h @@ -681,6 +681,18 @@ namespace triagens { static AstNode const TrueNode; +//////////////////////////////////////////////////////////////////////////////// +/// @brief a singleton zero node instance +//////////////////////////////////////////////////////////////////////////////// + + static AstNode const ZeroNode; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief a singleton empty string node instance +//////////////////////////////////////////////////////////////////////////////// + + static AstNode const EmptyStringNode; + //////////////////////////////////////////////////////////////////////////////// /// @brief inverse comparison operators //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index a657e99839..19fe49d16e 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -264,14 +264,44 @@ AstNode::AstNode (AstNodeType type, /// @brief create a boolean node, with defining a value //////////////////////////////////////////////////////////////////////////////// -AstNode::AstNode (bool v) - : AstNode(NODE_TYPE_VALUE, VALUE_TYPE_BOOL) { +AstNode::AstNode (bool v, + AstNodeValueType valueType) + : AstNode(NODE_TYPE_VALUE, valueType) { + TRI_ASSERT(valueType == VALUE_TYPE_BOOL); value.value._bool = v; TRI_ASSERT(flags == 0); TRI_ASSERT(computedJson == nullptr); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an int node, with defining a value +//////////////////////////////////////////////////////////////////////////////// + +AstNode::AstNode (int64_t v, + AstNodeValueType valueType) + : AstNode(NODE_TYPE_VALUE, valueType) { + + TRI_ASSERT(valueType == VALUE_TYPE_INT); + value.value._int = v; + TRI_ASSERT(flags == 0); + TRI_ASSERT(computedJson == nullptr); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a string node, with defining a value +//////////////////////////////////////////////////////////////////////////////// + +AstNode::AstNode (char const* v, + AstNodeValueType valueType) + : AstNode(NODE_TYPE_VALUE, valueType) { + + TRI_ASSERT(valueType == VALUE_TYPE_STRING); + value.value._string = v; + TRI_ASSERT(flags == 0); + TRI_ASSERT(computedJson == nullptr); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief create the node from JSON //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index 8e3e1f1973..aa3c81978d 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -193,10 +193,22 @@ namespace triagens { explicit AstNode (AstNodeType, AstNodeValueType); //////////////////////////////////////////////////////////////////////////////// -/// @brief create a boolean, with defining a value type +/// @brief create a boolean node, with defining a value type //////////////////////////////////////////////////////////////////////////////// - explicit AstNode (bool); + explicit AstNode (bool, AstNodeValueType); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a boolean node, with defining a value type +//////////////////////////////////////////////////////////////////////////////// + + explicit AstNode (int64_t, AstNodeValueType); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a string node, with defining a value type +//////////////////////////////////////////////////////////////////////////////// + + explicit AstNode (char const*, AstNodeValueType); //////////////////////////////////////////////////////////////////////////////// /// @brief create the node from JSON From c8c16767be9739d66545c0c63674dfb3f517055e Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 6 Nov 2014 23:18:18 +0100 Subject: [PATCH 03/12] added rule to remove filters if covered by index ranges - not yet activated --- arangod/Aql/Ast.h | 19 ++- arangod/Aql/ExecutionNode.h | 66 +++++---- arangod/Aql/Optimizer.cpp | 13 +- arangod/Aql/Optimizer.h | 5 +- arangod/Aql/OptimizerRules.cpp | 259 ++++++++++++++++++++++++++++++++- arangod/Aql/OptimizerRules.h | 8 +- arangod/Aql/RangeInfo.h | 20 ++- 7 files changed, 346 insertions(+), 44 deletions(-) diff --git a/arangod/Aql/Ast.h b/arangod/Aql/Ast.h index 7d593ebd96..9995591010 100644 --- a/arangod/Aql/Ast.h +++ b/arangod/Aql/Ast.h @@ -609,6 +609,19 @@ namespace triagens { AstNode* createNode (AstNodeType); +// ----------------------------------------------------------------------------- +// --SECTION-- public variables +// ----------------------------------------------------------------------------- + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inverse comparison operators +//////////////////////////////////////////////////////////////////////////////// + + static std::unordered_map const ReverseOperators; + + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -693,12 +706,6 @@ namespace triagens { static AstNode const EmptyStringNode; -//////////////////////////////////////////////////////////////////////////////// -/// @brief inverse comparison operators -//////////////////////////////////////////////////////////////////////////////// - - static std::unordered_map const ReverseOperators; - }; } diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index b0b655361a..02139cc7e0 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -761,7 +761,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return SINGLETON; } @@ -837,7 +837,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return ENUMERATE_COLLECTION; } @@ -990,7 +990,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return ENUMERATE_LIST; } @@ -1107,7 +1107,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return INDEX_RANGE; } @@ -1127,6 +1127,22 @@ namespace triagens { return _collection; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief return out variable +//////////////////////////////////////////////////////////////////////////////// + + Variable const* outVariable () const { + return _outVariable; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the ranges +//////////////////////////////////////////////////////////////////////////////// + + std::vector> const& ranges () const { + return _ranges; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief export to JSON //////////////////////////////////////////////////////////////////////////////// @@ -1166,7 +1182,7 @@ namespace triagens { double estimateCost () const override final; //////////////////////////////////////////////////////////////////////////////// -/// @brief check whether the pattern matches this nodes index +/// @brief check whether the pattern matches this node's index //////////////////////////////////////////////////////////////////////////////// IndexMatch MatchesIndex (IndexMatchVec const& pattern) const; @@ -1266,7 +1282,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return LIMIT; } @@ -1299,7 +1315,7 @@ namespace triagens { double estimateCost () const override final { return 1.005 * static_cast(_limit) + _dependencies.at(0)->getCost(); - //FIXME improve this estimate . . . + // FIXME: improve this estimate . . . } //////////////////////////////////////////////////////////////////////////////// @@ -1381,7 +1397,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return CALCULATION; } @@ -1517,7 +1533,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return SUBQUERY; } @@ -1650,7 +1666,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return FILTER; } @@ -1675,9 +1691,9 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// double estimateCost () const override final { - return _dependencies.at(0)->getCost() * 0.105; - //FIXME! 0.105 is the cost of doing the filter node under the - //assumption that it returns 10% of the results of its dependency + return _dependencies.at(0)->getCost() * 1.105; + // FIXME! 1.105 is the cost of doing the filter node under the + // assumption that it returns 10% of the results of its dependency } //////////////////////////////////////////////////////////////////////////////// @@ -1798,7 +1814,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return SORT; } @@ -1937,7 +1953,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return AGGREGATE; } @@ -2057,7 +2073,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return RETURN; } @@ -2244,7 +2260,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return REMOVE; } @@ -2357,7 +2373,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return INSERT; } @@ -2472,7 +2488,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return UPDATE; } @@ -2597,7 +2613,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return REPLACE; } @@ -2709,7 +2725,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return NORESULTS; } @@ -2786,7 +2802,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return REMOTE; } @@ -2969,7 +2985,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return SCATTER; } @@ -3072,7 +3088,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return DISTRIBUTE; } @@ -3180,7 +3196,7 @@ namespace triagens { /// @brief return the type of the node //////////////////////////////////////////////////////////////////////////////// - NodeType getType () const override { + NodeType getType () const override final { return GATHER; } diff --git a/arangod/Aql/Optimizer.cpp b/arangod/Aql/Optimizer.cpp index 7ee136e35a..b039a0ca07 100644 --- a/arangod/Aql/Optimizer.cpp +++ b/arangod/Aql/Optimizer.cpp @@ -453,8 +453,8 @@ void Optimizer::setupRules () { // try to replace simple OR conditions with IN registerRule("replace-OR-with-IN", - replaceORwithIN, - replaceORwithIN_pass6, + replaceOrWithIn, + replaceOrWithIn_pass6, true); // try to find a filter after an enumerate collection and find an index . . . @@ -468,6 +468,15 @@ void Optimizer::setupRules () { useIndexForSort, useIndexForSort_pass6, true); + +#if 0 + // try to remove filters which are covered by index ranges + // rule seems to work, but tests are still missing + registerRule("remove-filter-covered-by-index", + removeFiltersCoveredByIndex, + removeFiltersCoveredByIndex_pass6, + true); +#endif if (ExecutionEngine::isCoordinator()) { // distribute operations in cluster diff --git a/arangod/Aql/Optimizer.h b/arangod/Aql/Optimizer.h index 051fa18fd0..35aeec51a6 100644 --- a/arangod/Aql/Optimizer.h +++ b/arangod/Aql/Optimizer.h @@ -128,13 +128,16 @@ namespace triagens { pass6 = 800, // replace simple OR conditions with IN - replaceORwithIN_pass6 = 810, + replaceOrWithIn_pass6 = 810, // try to find a filter after an enumerate collection and find an index . . . useIndexRange_pass6 = 820, // try to find sort blocks which are superseeded by indexes useIndexForSort_pass6 = 830, + + // try to remove filters covered by index ranges + removeFiltersCoveredByIndex_pass6 = 840, ////////////////////////////////////////////////////////////////////////////// /// "Pass 10": final transformations for the cluster diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index bd1639b2f2..a53794f86d 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -1477,6 +1477,248 @@ int triagens::aql::useIndexForSort (Optimizer* opt, return TRI_ERROR_NO_ERROR; } +#if 0 +// TODO: finish rule and test it + +struct FilterCondition { + std::string variableName; + std::string attributeName; + AstNode const* lowNode = nullptr; + AstNode const* highNode = nullptr; + bool lowInclusive = false; + bool highInclusive = false; + + FilterCondition () { + } + + bool isFullyCoveredBy (RangeInfo const& other) { + if (! other.isConstant()) { + return false; + } + + if (other._var != variableName || + other._attr != attributeName) { + return false; + } + + bool const lowDefined = (lowNode != nullptr); + bool const highDefined = (highNode != nullptr); + + if (lowDefined != other._lowConst.isDefined()) { + return false; + } + + if (highDefined != other._highConst.isDefined()) { + return false; + } + + if (lowDefined) { + if (other._lowConst.inclusive() != lowInclusive) { + return false; + } + + Json json(TRI_UNKNOWN_MEM_ZONE, lowNode->toJsonValue(TRI_UNKNOWN_MEM_ZONE)); + + if (TRI_CompareValuesJson(other._lowConst.bound().json(), json.json()) != 0) { + return false; + } + } + + if (highDefined) { + if (other._highConst.inclusive() != highInclusive) { + return false; + } + + Json json(TRI_UNKNOWN_MEM_ZONE, highNode->toJsonValue(TRI_UNKNOWN_MEM_ZONE)); + + if (TRI_CompareValuesJson(other._highConst.bound().json(), json.json()) != 0) { + return false; + } + } + + return true; + } + + bool analyze (AstNode const* node) { + if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ || + node->type == NODE_TYPE_OPERATOR_BINARY_LT || + node->type == NODE_TYPE_OPERATOR_BINARY_LE || + node->type == NODE_TYPE_OPERATOR_BINARY_GT || + node->type == NODE_TYPE_OPERATOR_BINARY_GE) { + auto lhs = node->getMember(0); + auto rhs = node->getMember(1); + AstNodeType op = node->type; + bool found = false; + + if (lhs->isConstant() && + rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) { + found = true; + } + else if (rhs->isConstant() && + lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) { + found = true; + // reverse the nodes + lhs = node->getMember(1); + rhs = node->getMember(0); + + auto it = Ast::ReverseOperators.find(static_cast(node->type)); + TRI_ASSERT(it != Ast::ReverseOperators.end()); + + op = (*it).second; + } + + if (found) { + TRI_ASSERT(lhs->type == NODE_TYPE_VALUE); + TRI_ASSERT(rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS); + + std::function buildName = [&] (AstNode const* node) -> void { + if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) { + buildName(node->getMember(0)); + + if (! attributeName.empty()) { + attributeName.push_back('.'); + } + + attributeName.append(node->getStringValue()); + } + else if (node->type == NODE_TYPE_REFERENCE) { + auto variable = static_cast(node->getData()); + variableName = variable->name; + } + }; + + if (attributeName.empty()) { + buildName(rhs); + if (op == NODE_TYPE_OPERATOR_BINARY_EQ || + op == NODE_TYPE_OPERATOR_BINARY_NE) { + lowInclusive = true; + lowNode = lhs; + highInclusive = true; + highNode = lhs; + } + else if (op == NODE_TYPE_OPERATOR_BINARY_LT) { + lowInclusive = false; + lowNode = lhs; + } + else if (op == NODE_TYPE_OPERATOR_BINARY_LE) { + lowInclusive = true; + lowNode = lhs; + } + else if (op == NODE_TYPE_OPERATOR_BINARY_GT) { + highInclusive = false; + highNode = lhs; + } + else if (op == NODE_TYPE_OPERATOR_BINARY_GE) { + highInclusive = true; + highNode = lhs; + } + + return true; + } + // else if (attributeName == std::string(buffer.c_str(), buffer.length())) { + // same attribute + // TODO + // } + + // fall-through + } + + return false; + } + + if (node->type == NODE_TYPE_OPERATOR_BINARY_AND) { + auto lhs = node->getMember(0); + auto rhs = node->getMember(1); + + return (analyze(lhs) && analyze(rhs)); + } + + return false; + } + +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief try to remove filters which are covered by indexes +//////////////////////////////////////////////////////////////////////////////// + +int triagens::aql::removeFiltersCoveredByIndex (Optimizer* opt, + ExecutionPlan* plan, + Optimizer::Rule const* rule) { + std::unordered_set toUnlink; + std::vector&& nodes= plan->findNodesOfType(EN::FILTER, true); + + for (auto n : nodes) { + auto fn = static_cast(n); + // find the node with the filter expression + auto inVar = fn->getVariablesUsedHere(); + TRI_ASSERT(inVar.size() == 1); + // auto outVar = cn->getVariablesSetHere(); + + auto setter = plan->getVarSetBy(inVar[0]->id); + TRI_ASSERT(setter != nullptr); + + if (setter->getType() != EN::CALCULATION) { + continue; + } + + // check the filter condition + FilterCondition condition; + if (! condition.analyze(static_cast(setter)->expression()->node())) { + continue; + } + + bool handled = false; + auto current = n; + while (current != nullptr) { + if (current->getType() == EN::INDEX_RANGE) { + // found an index range, now check if the expression is covered by the index + auto variable = static_cast(current)->outVariable(); + TRI_ASSERT(variable != nullptr); + + auto const& ranges = static_cast(current)->ranges(); + + // TODO: this is not prepared for OR conditions + for (auto it : ranges) { + for (auto it2 : it) { + if (condition.isFullyCoveredBy(it2)) { + toUnlink.insert(setter); + toUnlink.insert(n); + break; + } + } + + if (handled) { + break; + } + } + } + + if (handled) { + break; + } + + auto deps = current->getDependencies(); + if (deps.size() != 1) { + break; + } + + current = deps[0]; + } + } + + if (! toUnlink.empty()) { + plan->unlinkNodes(toUnlink); + plan->findVarUsage(); + } + + opt->addPlan(plan, rule->level, ! toUnlink.empty()); + + return TRI_ERROR_NO_ERROR; +} +#endif + //////////////////////////////////////////////////////////////////////////////// /// @brief helper to compute lots of permutation tuples /// a permutation tuple is represented as a single vector together with @@ -1518,8 +1760,8 @@ static bool nextPermutationTuple (std::vector& data, int triagens::aql::interchangeAdjacentEnumerations (Optimizer* opt, ExecutionPlan* plan, Optimizer::Rule const* rule) { - std::vector nodes - = plan->findNodesOfType(EN::ENUMERATE_COLLECTION, + std::vector&& nodes + = plan->findNodesOfType(EN::ENUMERATE_COLLECTION, true); std::unordered_set nodesSet; for (auto n : nodes) { @@ -2274,7 +2516,7 @@ int triagens::aql::undistributeRemoveAfterEnumColl (Optimizer* opt, } //////////////////////////////////////////////////////////////////////////////// -/// @brief +/// @brief auxilliary struct for the OR-to-IN conversion //////////////////////////////////////////////////////////////////////////////// struct OrToInConverter { @@ -2425,7 +2667,16 @@ struct OrToInConverter { } }; -int triagens::aql::replaceORwithIN (Optimizer* opt, +//////////////////////////////////////////////////////////////////////////////// +/// @brief this rule replaces expressions of the type: +/// x.val == 1 || x.val == 2 || x.val == 3 +// with +// x.val IN [1,2,3] +// when the OR conditions are present in the same FILTER node, and refer to the +// same (single) attribute. +//////////////////////////////////////////////////////////////////////////////// + +int triagens::aql::replaceOrWithIn (Optimizer* opt, ExecutionPlan* plan, Optimizer::Rule const* rule) { ENTER_BLOCK; diff --git a/arangod/Aql/OptimizerRules.h b/arangod/Aql/OptimizerRules.h index 83f2b68d69..6b56260496 100644 --- a/arangod/Aql/OptimizerRules.h +++ b/arangod/Aql/OptimizerRules.h @@ -99,6 +99,12 @@ namespace triagens { int useIndexForSort (Optimizer*, ExecutionPlan*, Optimizer::Rule const*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief try to remove filters which are covered by indexes +//////////////////////////////////////////////////////////////////////////////// + + int removeFiltersCoveredByIndex (Optimizer*, ExecutionPlan*, Optimizer::Rule const*); + //////////////////////////////////////////////////////////////////////////////// /// @brief interchange adjacent EnumerateCollectionNodes in all possible ways //////////////////////////////////////////////////////////////////////////////// @@ -174,7 +180,7 @@ namespace triagens { // same (single) attribute. //////////////////////////////////////////////////////////////////////////////// - int replaceORwithIN (Optimizer*, ExecutionPlan*, Optimizer::Rule const*); + int replaceOrWithIn (Optimizer*, ExecutionPlan*, Optimizer::Rule const*); } // namespace aql } // namespace triagens diff --git a/arangod/Aql/RangeInfo.h b/arangod/Aql/RangeInfo.h index 5a937d7c75..9e238103e3 100644 --- a/arangod/Aql/RangeInfo.h +++ b/arangod/Aql/RangeInfo.h @@ -338,7 +338,10 @@ namespace triagens { RangeInfoBound low, RangeInfoBound high, bool equality) - : _var(var), _attr(attr), _valid(true), _defined(true), + : _var(var), + _attr(attr), + _valid(true), + _defined(true), _equality(equality) { if (low.isConstant()) { @@ -347,6 +350,7 @@ namespace triagens { else { _lows.emplace_back(low); } + if (high.isConstant()) { _highConst.assign(high); } @@ -373,13 +377,19 @@ namespace triagens { } } - RangeInfo ( std::string var, - std::string attr) - : _var(var), _attr(attr), _valid(true), _defined(true), + RangeInfo (std::string const& var, + std::string const& attr) + : _var(var), + _attr(attr), + _valid(true), + _defined(true), _equality(false) { } - RangeInfo () : _valid(false), _defined(false), _equality(false) { + RangeInfo () + : _valid(false), + _defined(false), + _equality(false) { } RangeInfo (basics::Json const& json); From b0de1bb2266f35722f0ad17de1baa7d00422a3f9 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 7 Nov 2014 10:53:00 +0100 Subject: [PATCH 04/12] leaner and meaner error messages --- Documentation/Books/Users/Aql/Invoke.mdpp | 15 ++- arangod/Aql/Ast.cpp | 33 +++++++ arangod/Aql/Ast.h | 8 ++ arangod/Aql/AstNode.cpp | 85 ++++++++++++++--- arangod/Aql/AstNode.h | 6 +- arangod/Aql/ExecutionBlock.cpp | 91 ++++++++++--------- arangod/Aql/ExecutionBlock.h | 20 ++++ arangod/Aql/Executor.cpp | 4 +- arangod/Aql/Expression.cpp | 2 +- arangod/Aql/OptimizerRules.cpp | 2 +- arangod/Aql/Parser.cpp | 34 ++++--- arangod/Aql/Query.cpp | 36 ++++---- arangod/Aql/Query.h | 9 ++ arangod/Utils/Exception.cpp | 43 +++++---- arangod/Utils/Exception.h | 13 ++- arangod/V8Server/v8-vocbase.cpp | 18 ++++ js/actions/api-cursor.js | 32 ++++++- js/actions/api-explain.js | 36 +++++++- ...er-rule-remove-unnecessary-calculations.js | 16 ++-- 19 files changed, 376 insertions(+), 127 deletions(-) diff --git a/Documentation/Books/Users/Aql/Invoke.mdpp b/Documentation/Books/Users/Aql/Invoke.mdpp index d058751fd9..f7073b7df0 100644 --- a/Documentation/Books/Users/Aql/Invoke.mdpp +++ b/Documentation/Books/Users/Aql/Invoke.mdpp @@ -264,7 +264,7 @@ arangosh> stmt.explain({ allPlans: true }).plans.length; 2 ``` -To see the minified version of the plan: +To see a slightly more compact version of the plan, the following transformation can be applied: ``` arangosh> stmt.explain({ allPlans: true }).plans.map(function(plan) { return formatPlan(plan); }); @@ -292,6 +292,19 @@ arangosh> stmt.explain({ allPlans: true }).plans.map(function(plan) { return for ] ``` +`explain` will also accept the following additional options: +- *maxPlans*: limits the maximum number of plans that are created by the AQL query optimizer +- *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules + can be put into this attribute, telling the optimizer to include or exclude + specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it + with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules. + +The following example disables all optimizer rules but `remove-redundant-calculations`: + +``` +arangosh> stmt.explain({ optimizer: { rules: [ "-all", "+remove-redundant-calculations" ] } }); +``` + !SECTION Parsing queries Clients can use ArangoDB to check if a given AQL query is syntactically valid. ArangoDB provides diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index 234aac5c96..db70bf0b33 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -981,6 +981,11 @@ void Ast::optimize () { if (node->type == NODE_TYPE_LET) { return optimizeLet(node); } + + // FOR + if (node->type == NODE_TYPE_FOR) { + return optimizeFor(node); + } return node; }; @@ -1643,6 +1648,34 @@ AstNode* Ast::optimizeLet (AstNode* node) { return node; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief optimizes the FOR statement +/// no real optimizations are done here, but we do an early check if the +/// FOR loop operand is actually a list +//////////////////////////////////////////////////////////////////////////////// + +AstNode* Ast::optimizeFor (AstNode* node) { + TRI_ASSERT(node != nullptr); + TRI_ASSERT(node->type == NODE_TYPE_FOR); + TRI_ASSERT(node->numMembers() == 2); + + AstNode* expression = node->getMember(1); + + if (expression == nullptr) { + return node; + } + + if (expression->isConstant() && + expression->type != NODE_TYPE_LIST) { + // right-hand operand to FOR statement is no list + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_LIST_EXPECTED, + TRI_errno_string(TRI_ERROR_QUERY_LIST_EXPECTED) + std::string(" in FOR loop")); + } + + // no real optimizations will be done here + return node; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST node from JSON //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Ast.h b/arangod/Aql/Ast.h index 9995591010..2b7cc5061b 100644 --- a/arangod/Aql/Ast.h +++ b/arangod/Aql/Ast.h @@ -575,6 +575,14 @@ namespace triagens { AstNode* optimizeLet (AstNode*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief optimizes the FOR statement +/// no real optimizations are done here, but we do an early check if the +/// FOR loop operand is actually a list +//////////////////////////////////////////////////////////////////////////////// + + AstNode* optimizeFor (AstNode*); + //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST node from JSON //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index 19fe49d16e..2516cc8646 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -826,9 +826,11 @@ AstNode* AstNode::castToString (Ast* ast) { return this; } + TRI_ASSERT(isConstant()); + // stringify node triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE); - append(&buffer, false); + stringify(&buffer, false); char const* value = ast->query()->registerString(buffer.c_str(), buffer.length(), false); TRI_ASSERT(value != nullptr); @@ -1229,16 +1231,20 @@ AstNode* AstNode::clone (Ast* ast) const { //////////////////////////////////////////////////////////////////////////////// /// @brief append a string representation of the node to a string buffer +/// the string representation does not need to be JavaScript-compatible +/// except for node types NODE_TYPE_VALUE, NODE_TYPE_LIST and NODE_TYPE_ARRAY //////////////////////////////////////////////////////////////////////////////// -void AstNode::append (triagens::basics::StringBuffer* buffer, - bool verbose) const { +void AstNode::stringify (triagens::basics::StringBuffer* buffer, + bool verbose) const { if (type == NODE_TYPE_VALUE) { + // must be JavaScript-compatible! appendValue(buffer); return; } if (type == NODE_TYPE_LIST) { + // must be JavaScript-compatible! size_t const n = numMembers(); if (verbose || n > 0) { if (verbose || n > 1) { @@ -1251,7 +1257,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, AstNode* member = getMember(i); if (member != nullptr) { - member->append(buffer, verbose); + member->stringify(buffer, verbose); } } if (verbose || n > 1) { @@ -1262,6 +1268,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, } if (type == NODE_TYPE_ARRAY) { + // must be JavaScript-compatible! if (verbose) { buffer->appendChar('{'); size_t const n = numMembers(); @@ -1279,7 +1286,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, buffer->appendJsonEncoded(member->getStringValue()); buffer->appendText("\":", 2); - member->getMember(0)->append(buffer, verbose); + member->getMember(0)->stringify(buffer, verbose); } } buffer->appendChar('}'); @@ -1290,7 +1297,8 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, return; } - if (type == NODE_TYPE_REFERENCE) { + if (type == NODE_TYPE_REFERENCE || + type == NODE_TYPE_VARIABLE) { // not used by V8 auto variable = static_cast(getData()); TRI_ASSERT(variable != nullptr); @@ -1305,9 +1313,9 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, // not used by V8 auto member = getMember(0); auto index = getMember(1); - member->append(buffer, verbose); + member->stringify(buffer, verbose); buffer->appendChar('['); - index->append(buffer, verbose); + index->stringify(buffer, verbose); buffer->appendChar(']'); return; } @@ -1315,31 +1323,68 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, if (type == NODE_TYPE_ATTRIBUTE_ACCESS) { // not used by V8 auto member = getMember(0); - member->append(buffer, verbose); + member->stringify(buffer, verbose); buffer->appendChar('.'); buffer->appendText(getStringValue()); return; } + + if (type == NODE_TYPE_BOUND_ATTRIBUTE_ACCESS) { + // not used by V8 + getMember(0)->stringify(buffer, verbose); + buffer->appendChar('.'); + getMember(1)->stringify(buffer, verbose); + return; + } + + if (type == NODE_TYPE_PARAMETER) { + // not used by V8 + buffer->appendChar('@'); + buffer->appendText(getStringValue()); + return; + } if (type == NODE_TYPE_FCALL) { + // not used by V8 auto func = static_cast(getData()); buffer->appendText(func->externalName); buffer->appendChar('('); - getMember(0)->append(buffer, verbose); + getMember(0)->stringify(buffer, verbose); buffer->appendChar(')'); return; - } + } + + if (type == NODE_TYPE_EXPAND) { + // not used by V8 + buffer->appendText("_EXPAND("); + getMember(1)->stringify(buffer, verbose); + buffer->appendChar(','); + getMember(0)->stringify(buffer, verbose); + buffer->appendChar(')'); + return; + } + + if (type == NODE_TYPE_ITERATOR) { + // not used by V8 + buffer->appendText("_ITERATOR("); + getMember(1)->stringify(buffer, verbose); + buffer->appendChar(','); + getMember(0)->stringify(buffer, verbose); + buffer->appendChar(')'); + return; + } if (type == NODE_TYPE_OPERATOR_UNARY_NOT || type == NODE_TYPE_OPERATOR_UNARY_PLUS || type == NODE_TYPE_OPERATOR_UNARY_MINUS) { + // not used by V8 TRI_ASSERT(numMembers() == 1); auto it = Operators.find(static_cast(type)); TRI_ASSERT(it != Operators.end()); buffer->appendChar(' '); buffer->appendText((*it).second); - getMember(0)->append(buffer, verbose); + getMember(0)->stringify(buffer, verbose); return; } @@ -1358,20 +1403,31 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, type == NODE_TYPE_OPERATOR_BINARY_GE || type == NODE_TYPE_OPERATOR_BINARY_IN || type == NODE_TYPE_OPERATOR_BINARY_NIN) { + // not used by V8 TRI_ASSERT(numMembers() == 2); auto it = Operators.find(type); TRI_ASSERT(it != Operators.end()); - getMember(0)->append(buffer, verbose); + getMember(0)->stringify(buffer, verbose); buffer->appendChar(' '); buffer->appendText((*it).second); buffer->appendChar(' '); - getMember(1)->append(buffer, verbose); + getMember(1)->stringify(buffer, verbose); + return; + } + + if (type == NODE_TYPE_RANGE) { + // not used by V8 + TRI_ASSERT(numMembers() == 2); + getMember(0)->stringify(buffer, verbose); + buffer->appendText("..", 2); + getMember(1)->stringify(buffer, verbose); return; } std::string message("stringification not supported for node type "); message.append(getTypeString()); + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, message); } @@ -1381,6 +1437,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer, //////////////////////////////////////////////////////////////////////////////// /// @brief stringify the value of a node into a string buffer +/// this creates an equivalent to what JSON.stringify() would do /// this method is used when generated JavaScript code for the node! //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index aa3c81978d..fc7bf5dce1 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -646,8 +646,8 @@ namespace triagens { /// @brief append a JavaScript representation of the node into a string buffer //////////////////////////////////////////////////////////////////////////////// - void append (triagens::basics::StringBuffer*, - bool) const; + void stringify (triagens::basics::StringBuffer*, + bool) const; // ----------------------------------------------------------------------------- // --SECTION-- private methods @@ -655,6 +655,8 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// /// @brief stringify the value of a node into a string buffer +/// this method is used when generated JavaScript code for the node! +/// this creates an equivalent to what JSON.stringify() would do //////////////////////////////////////////////////////////////////////////////// void appendValue (triagens::basics::StringBuffer*) const; diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 7b205b703c..f8442cb5c6 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -1649,45 +1649,42 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) { // get the size of the thing we are looping over _collection = nullptr; switch (inVarReg._type) { - case AqlValue::JSON: { - if(! inVarReg._json->isList()) { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "EnumerateListBlock: JSON is not a list"); - } - sizeInVar = inVarReg._json->size(); - break; - } - - case AqlValue::RANGE: { - sizeInVar = inVarReg._range->size(); - break; - } - - case AqlValue::DOCVEC: { - if( _index == 0) { // this is a (maybe) new DOCVEC - _DOCVECsize = 0; - //we require the total number of items - - for (size_t i = 0; i < inVarReg._vector->size(); i++) { - _DOCVECsize += inVarReg._vector->at(i)->size(); + case AqlValue::JSON: { + if (! inVarReg._json->isList()) { + throwListExpectedException(); } + sizeInVar = inVarReg._json->size(); + break; } - sizeInVar = _DOCVECsize; - if (sizeInVar > 0) { - _collection = inVarReg._vector->at(0)->getDocumentCollection(0); + + case AqlValue::RANGE: { + sizeInVar = inVarReg._range->size(); + break; } - break; - } - case AqlValue::SHAPED: { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "EnumerateListBlock: cannot iterate over shaped value"); - } + case AqlValue::DOCVEC: { + if (_index == 0) { // this is a (maybe) new DOCVEC + _DOCVECsize = 0; + // we require the total number of items - case AqlValue::EMPTY: { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "EnumerateListBlock: cannot iterate over empty value"); - } + for (size_t i = 0; i < inVarReg._vector->size(); i++) { + _DOCVECsize += inVarReg._vector->at(i)->size(); + } + } + sizeInVar = _DOCVECsize; + if (sizeInVar > 0) { + _collection = inVarReg._vector->at(0)->getDocumentCollection(0); + } + break; + } + + case AqlValue::SHAPED: { + throwListExpectedException(); + } + + case AqlValue::EMPTY: { + throwListExpectedException(); + } } if (sizeInVar == 0) { @@ -1773,17 +1770,18 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { // get the size of the thing we are looping over switch (inVarReg._type) { case AqlValue::JSON: { - if(! inVarReg._json->isList()) { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "EnumerateListBlock: JSON is not a list"); + if (! inVarReg._json->isList()) { + throwListExpectedException(); } sizeInVar = inVarReg._json->size(); break; } + case AqlValue::RANGE: { sizeInVar = inVarReg._range->size(); break; } + case AqlValue::DOCVEC: { if( _index == 0) { // this is a (maybe) new DOCVEC _DOCVECsize = 0; @@ -1795,9 +1793,10 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { sizeInVar = _DOCVECsize; break; } - default: { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, - "EnumerateListBlock: unexpected type in register"); + + case AqlValue::SHAPED: + case AqlValue::EMPTY: { + throwListExpectedException(); } } @@ -1852,7 +1851,17 @@ AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) { } } - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected value in variable to iterate over"); + throwListExpectedException(); + TRI_ASSERT(false); + + // cannot be reached. function call above will always throw an exception + return AqlValue(); +} + +void EnumerateListBlock::throwListExpectedException () { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_LIST_EXPECTED, + TRI_errno_string(TRI_ERROR_QUERY_LIST_EXPECTED) + + std::string("in FOR loop: ")); } // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 9b83a62bea..5b0ce5268e 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -681,10 +681,30 @@ namespace triagens { /// @brief create an AqlValue from the inVariable using the current _index //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an AqlValue from the inVariable using the current _index +//////////////////////////////////////////////////////////////////////////////// + AqlValue getAqlValue (AqlValue inVarReg); +//////////////////////////////////////////////////////////////////////////////// +/// @brief throws a "list expected" exception +//////////////////////////////////////////////////////////////////////////////// + + void throwListExpectedException (); + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + + private: + //////////////////////////////////////////////////////////////////////////////// /// @brief current position in the _inVariable //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Executor.cpp b/arangod/Aql/Executor.cpp index f386d62e5e..1d86ac0bf9 100644 --- a/arangod/Aql/Executor.cpp +++ b/arangod/Aql/Executor.cpp @@ -724,7 +724,7 @@ void Executor::generateCodeExpand (AstNode const* node) { auto variable = static_cast(iterator->getMember(0)->getData()); _buffer->appendText("vars[\""); _buffer->appendText(variable->name); - _buffer->appendText("\"] = v; "); + _buffer->appendText("\"]=v; "); _buffer->appendText("r.push("); generateCodeNode(node->getMember(1)); @@ -812,7 +812,7 @@ void Executor::generateCodeNode (AstNode const* node) { switch (node->type) { case NODE_TYPE_VALUE: - node->append(_buffer, true); + node->appendValue(_buffer); break; case NODE_TYPE_LIST: diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index f4100ef857..9f29ab64e6 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -744,7 +744,7 @@ std::pair Expression::getMultipleAttributes() { //////////////////////////////////////////////////////////////////////////////// void Expression::stringify (triagens::basics::StringBuffer* buffer) const { - _node->append(buffer, true); + _node->stringify(buffer, true); } // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index a53794f86d..9d6fa5f007 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -2528,7 +2528,7 @@ struct OrToInConverter { std::string getString (AstNode const* node) { triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE); - node->append(&buffer, false); + node->stringify(&buffer, false); return std::string(buffer.c_str(), buffer.length()); } diff --git a/arangod/Aql/Parser.cpp b/arangod/Aql/Parser.cpp index 3f908c0ac0..530271b5ef 100644 --- a/arangod/Aql/Parser.cpp +++ b/arangod/Aql/Parser.cpp @@ -41,7 +41,7 @@ using namespace triagens::aql; /// @brief create the parser //////////////////////////////////////////////////////////////////////////////// -Parser::Parser (Query* query) +Parser::Parser (Query* query) : _query(query), _ast(query->ast()), _scanner(nullptr), @@ -190,22 +190,26 @@ void Parser::registerParseError (int errorCode, << std::string("' at position ") << line << std::string(":") - << (column + 1) - << std::endl - << _query->queryString() - << std::endl; + << (column + 1); + + if (_query->verboseErrors()) { + errorMessage + << std::endl + << _query->queryString() + << std::endl; - // create a neat pointer to the location of the error. - size_t i; - for (i = 0; i + 1 < (size_t) column; i++) { - errorMessage << ' '; + // create a neat pointer to the location of the error. + size_t i; + for (i = 0; i + 1 < (size_t) column; i++) { + errorMessage << ' '; + } + if (i > 0) { + errorMessage << '^'; + } + errorMessage << '^' + << '^' + << std::endl; } - if (i > 0) { - errorMessage << '^'; - } - errorMessage << '^' - << '^' - << std::endl; registerError(errorCode, errorMessage.str().c_str()); } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index f6cbedb00d..f27c7db3ac 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -555,19 +555,19 @@ QueryResult Query::prepare (QueryRegistry* registry) { } catch (triagens::arango::Exception const& ex) { cleanupPlanAndEngine(ex.code()); - return QueryResult(ex.code(), getStateString() + ex.message()); + return QueryResult(ex.code(), ex.message() + getStateString()); } catch (std::bad_alloc const&) { cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY); - return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); + return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString()); } catch (std::exception const& ex) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what()); + return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString()); } catch (...) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL)); + return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString()); } } @@ -625,19 +625,19 @@ QueryResult Query::execute (QueryRegistry* registry) { } catch (triagens::arango::Exception const& ex) { cleanupPlanAndEngine(ex.code()); - return QueryResult(ex.code(), getStateString() + ex.message()); + return QueryResult(ex.code(), ex.message() + getStateString()); } catch (std::bad_alloc const&) { cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY); - return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); + return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString()); } catch (std::exception const& ex) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what()); + return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString()); } catch (...) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL)); + return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString()); } } @@ -697,19 +697,19 @@ QueryResultV8 Query::executeV8 (QueryRegistry* registry) { } catch (triagens::arango::Exception const& ex) { cleanupPlanAndEngine(ex.code()); - return QueryResultV8(ex.code(), getStateString() + ex.message()); + return QueryResultV8(ex.code(), ex.message() + getStateString()); } catch (std::bad_alloc const&) { cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY); - return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); + return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString()); } catch (std::exception const& ex) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResultV8(TRI_ERROR_INTERNAL, getStateString() + ex.what()); + return QueryResultV8(TRI_ERROR_INTERNAL, ex.what() + getStateString()); } catch (...) { cleanupPlanAndEngine(TRI_ERROR_INTERNAL); - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL)); + return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString()); } } @@ -811,16 +811,16 @@ QueryResult Query::explain () { return result; } catch (triagens::arango::Exception const& ex) { - return QueryResult(ex.code(), getStateString() + ex.message()); + return QueryResult(ex.code(), ex.message() + getStateString()); } catch (std::bad_alloc const&) { - return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); + return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString()); } catch (std::exception const& ex) { - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what()); + return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString()); } catch (...) { - return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL)); + return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString()); } } @@ -1051,7 +1051,7 @@ QueryResult Query::transactionError (int errorCode) const { err += std::string(" (") + detail + std::string(")"); } - if (_queryString != nullptr) { + if (_queryString != nullptr && verboseErrors()) { err += std::string("\nwhile executing:\n") + _queryString + std::string("\n"); } @@ -1145,7 +1145,7 @@ void Query::enterState (ExecutionState state) { //////////////////////////////////////////////////////////////////////////////// std::string Query::getStateString () const { - return "while " + StateNames[_state] + ": "; + return std::string(" (while " + StateNames[_state] + ")"); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index 15a4ba5f89..6db462a612 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -382,6 +382,14 @@ namespace triagens { return _plan; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not the query returns verbose error messages +//////////////////////////////////////////////////////////////////////////////// + + bool verboseErrors () const { + return getBooleanOption("verboseErrors", false); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief set the plan for the query //////////////////////////////////////////////////////////////////////////////// @@ -618,6 +626,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// bool const _contextOwnedByExterior; + }; } diff --git a/arangod/Utils/Exception.cpp b/arangod/Utils/Exception.cpp index 572ccdb827..a699010f61 100644 --- a/arangod/Utils/Exception.cpp +++ b/arangod/Utils/Exception.cpp @@ -33,6 +33,12 @@ using namespace std; using namespace triagens::arango; +//////////////////////////////////////////////////////////////////////////////// +/// @brief controls if backtraces are printed with exceptions +//////////////////////////////////////////////////////////////////////////////// + +static bool WithBackTrace = false; + //////////////////////////////////////////////////////////////////////////////// /// @brief constructor, without format string //////////////////////////////////////////////////////////////////////////////// @@ -46,9 +52,11 @@ Exception::Exception (int code, _code(code) { #ifdef TRI_ENABLE_MAINTAINER_MODE #if HAVE_BACKTRACE - _errorMessage += std::string("\n\n"); - TRI_GetBacktrace(_errorMessage); - _errorMessage += std::string("\n\n"); + if (WithBackTrace) { + _errorMessage += std::string("\n\n"); + TRI_GetBacktrace(_errorMessage); + _errorMessage += std::string("\n\n"); + } #endif #endif } @@ -61,28 +69,19 @@ Exception::Exception (int code, Exception::Exception (int code, string const& errorMessage, char const* file, - int line, - bool errnoStringResolved) + int line) : _errorMessage(errorMessage), _file(file), _line(line), _code(code) { - if (code != TRI_ERROR_INTERNAL) { - if (! errnoStringResolved) { - _errorMessage = std::string("("); - _errorMessage += TRI_errno_string(code); - _errorMessage += std::string(") "); - _errorMessage += errorMessage; - } - } - else { - } #ifdef TRI_ENABLE_MAINTAINER_MODE #if HAVE_BACKTRACE - _errorMessage += std::string("\n\n"); - TRI_GetBacktrace(_errorMessage); - _errorMessage += std::string("\n\n"); + if (WithBackTrace) { + _errorMessage += std::string("\n\n"); + TRI_GetBacktrace(_errorMessage); + _errorMessage += std::string("\n\n"); + } #endif #endif } @@ -137,6 +136,14 @@ std::string Exception::FillExceptionString (int code, return std::string(buffer); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief controls whether a backtrace is created for each exception +//////////////////////////////////////////////////////////////////////////////// + +void Exception::SetVerbose (bool verbose) { + WithBackTrace = verbose; +} + // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- diff --git a/arangod/Utils/Exception.h b/arangod/Utils/Exception.h index 0fd7cdb6de..e30ffbe6e8 100644 --- a/arangod/Utils/Exception.h +++ b/arangod/Utils/Exception.h @@ -52,7 +52,7 @@ //////////////////////////////////////////////////////////////////////////////// #define THROW_ARANGO_EXCEPTION_PARAMS(code, ...) \ - throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__, true) + throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__, false) //////////////////////////////////////////////////////////////////////////////// /// @brief throws an arango exception with an error code and an already-built @@ -60,7 +60,7 @@ //////////////////////////////////////////////////////////////////////////////// #define THROW_ARANGO_EXCEPTION_MESSAGE(code, message) \ - throw triagens::arango::Exception(code, message, __FILE__, __LINE__, false) + throw triagens::arango::Exception(code, message, __FILE__, __LINE__) // ----------------------------------------------------------------------------- // --SECTION-- public types @@ -84,8 +84,7 @@ namespace triagens { Exception (int code, std::string const& errorMessage, char const* file, - int line, - bool errnoStringResolved); + int line); ~Exception () throw (); @@ -107,6 +106,12 @@ namespace triagens { static std::string FillExceptionString (int, ...); +//////////////////////////////////////////////////////////////////////////////// +/// @brief controls whether a backtrace is created for each exception +//////////////////////////////////////////////////////////////////////////////// + + static void SetVerbose (bool); + protected: std::string _errorMessage; char const* _file; diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 0b6e23e72c..2728c2bc01 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -533,6 +533,22 @@ static v8::Handle JS_normalize_string (v8::Arguments const& argv) { return scope.Close(TRI_normalize_V8_Obj(argv[0])); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief enables or disables native backtrace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_EnableNativeBacktraces (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1) { + TRI_V8_EXCEPTION_USAGE(scope, "ENABLE_NATIVE_BACKTRACES()"); + } + + triagens::arango::Exception::SetVerbose(TRI_ObjectToBoolean(argv[0])); + + return scope.Close(v8::Undefined()); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief compare two UTF 16 strings //////////////////////////////////////////////////////////////////////////////// @@ -2542,6 +2558,8 @@ void TRI_InitV8VocBridge (triagens::arango::ApplicationV8* applicationV8, TRI_AddGlobalFunctionVocbase(context, "TRANSACTION", JS_Transaction, true); TRI_AddGlobalFunctionVocbase(context, "WAL_FLUSH", JS_FlushWal, true); TRI_AddGlobalFunctionVocbase(context, "WAL_PROPERTIES", JS_PropertiesWal, true); + + TRI_AddGlobalFunctionVocbase(context, "ENABLE_NATIVE_BACKTRACES", JS_EnableNativeBacktraces, true); // ............................................................................. // create global variables diff --git a/js/actions/api-cursor.js b/js/actions/api-cursor.js index 0dac06291b..d325957e76 100644 --- a/js/actions/api-cursor.js +++ b/js/actions/api-cursor.js @@ -92,6 +92,14 @@ var internal = require("internal"); /// be present in the result if the query has a LIMIT clause and the LIMIT clause is /// actually used in the query. /// +/// - *maxPlans*: limits the maximum number of plans that are created by the AQL +/// query optimizer. +/// +/// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules +/// can be put into this attribute, telling the optimizer to include or exclude +/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it +/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules. +/// /// If the result set can be created by the server, the server will respond with /// *HTTP 201*. The body of the response will contain a JSON object with the /// result set. @@ -207,7 +215,7 @@ var internal = require("internal"); /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// -/// Using a query option: +/// Using query option "fullCount": /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorOption} /// var url = "/_api/cursor"; @@ -226,6 +234,28 @@ var internal = require("internal"); /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// +/// Enabling and disabling optimizer rules: +/// +/// @EXAMPLE_ARANGOSH_RUN{RestCursorOptimizerRules} +/// var url = "/_api/cursor"; +/// var body = { +/// query: "FOR i IN 1..10 LET a = 1 LET b = 2 FILTER a + b == 3 RETURN i", +/// count: true, +/// options: { +/// maxPlans: 1, +/// optimizer: { +/// rules: [ "-all", "+remove-unnecessary-filters" ] +/// } +/// } +/// }; +/// +/// var response = logCurlRequest('POST', url, JSON.stringify(body)); +/// +/// assert(response.code === 201); +/// +/// logJsonResponse(response); +/// @END_EXAMPLE_ARANGOSH_RUN +/// /// Executes a data-modification query and retrieves the number of /// modified documents: /// diff --git a/js/actions/api-explain.js b/js/actions/api-explain.js index cb4afd2681..50d6cbb93a 100644 --- a/js/actions/api-explain.js +++ b/js/actions/api-explain.js @@ -51,12 +51,15 @@ var ERRORS = require("internal").errors; /// The currently supported options are: /// - *allPlans*: if set to *true*, all possible execution plans will be returned. /// The default is *false*, meaning only the optimal plan will be returned. +/// /// - *maxPlans*: an optional maximum number of plans that the optimizer is /// allowed to generate. Setting this attribute to a low value allows to put a /// cap on the amount of work the optimizer does. +/// /// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules /// can be put into this attribute, telling the optimizer to include or exclude -/// specific rules. +/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it +/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules. /// /// @RESTDESCRIPTION /// @@ -84,11 +87,15 @@ var ERRORS = require("internal").errors; /// Each plan in the result is a JSON object with the following attributes: /// - *nodes*: the list of execution nodes of the plan. The list of available node types /// can be found [here](.../Aql/Optimizer.html) +/// /// - *estimatedCost*: the total estimated cost for the plan. If there are multiple /// plans, the optimizer will choose the plan with the lowest total cost. +/// /// - *collections*: a list of collections used in the query +/// /// - *rules*: a list of rules the optimizer applied. The list of rules can be /// found [here](../Aql/Optimizer.html) +/// /// - *variables*: list of variables used in the query (note: this may contain /// internal variables created by the optimizer) /// @@ -152,6 +159,33 @@ var ERRORS = require("internal").errors; /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// +/// Using some options: +/// +/// @EXAMPLE_ARANGOSH_RUN{RestExplainOptions} +/// var url = "/_api/explain"; +/// var cn = "products"; +/// db._drop(cn); +/// db._create(cn); +/// db.products.ensureSkiplist("id"); +/// for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); } +/// body = { +/// query : "FOR p IN products LET a = p.id FILTER a == 4 LET name = p.name SORT p.id LIMIT 1 RETURN name", +/// options : { +/// maxPlans : 2, +/// allPlans : true, +/// optimizer : { +/// rules: [ "-all", "+use-index-for-sort", "+use-index-range" ] +/// } +/// } +/// }; +/// +/// var response = logCurlRequest('POST', url, JSON.stringify(body)); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// @END_EXAMPLE_ARANGOSH_RUN +/// /// Returning all plans: /// /// @EXAMPLE_ARANGOSH_RUN{RestExplainAllPlans} diff --git a/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js b/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js index 34af5f4293..77a8b294f6 100644 --- a/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js +++ b/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js @@ -101,14 +101,14 @@ function optimizerRuleTestSuite () { "FOR a IN [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)", "FOR a IN [17.33] RETURN FLOOR(a)", "FOR a IN ['-12'] RETURN TO_LIST(a)", - "FOR a IN { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)", - "FOR a IN -12 RETURN ABS(a)", - "FOR a IN -12 RETURN ABS(a + 17)", - "FOR a IN 17.33 RETURN ROUND(a)", - "FOR a IN 17.33 RETURN SQRT(a)", - "FOR a IN -17.33 RETURN SQRT(a)", - "FOR a IN CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1", - "FOR a IN 7 return a..12", + "FOR a IN [{ \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } }] RETURN TO_LIST(a)", + "FOR a IN [-12] RETURN ABS(a)", + "FOR a IN [-12] RETURN ABS(a + 17)", + "FOR a IN [17.33] RETURN ROUND(a)", + "FOR a IN [17.33] RETURN SQRT(a)", + "FOR a IN [-17.33] RETURN SQRT(a)", + "FOR a IN [CHAR_LENGTH('äöボカド名üÄÖÜß')] return a + 1", + "FOR a IN [7] return a..12", "FOR a IN [1, 7, 3, 12] RETURN AVERAGE(a)", "FOR a IN [1, 7, 3, 12, null] RETURN NOT_NULL(a)", "FOR a IN [1, 7, 3, 12, null] RETURN FIRST_LIST(a)", From f96a3d3f983dc1ad31c002515504b650458e129e Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 7 Nov 2014 11:04:21 +0100 Subject: [PATCH 05/12] forgot to commit --- arangod/Utils/Exception.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arangod/Utils/Exception.h b/arangod/Utils/Exception.h index e30ffbe6e8..2c1430097c 100644 --- a/arangod/Utils/Exception.h +++ b/arangod/Utils/Exception.h @@ -52,7 +52,7 @@ //////////////////////////////////////////////////////////////////////////////// #define THROW_ARANGO_EXCEPTION_PARAMS(code, ...) \ - throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__, false) + throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__) //////////////////////////////////////////////////////////////////////////////// /// @brief throws an arango exception with an error code and an already-built From 0d01e6c7b6700595e6a4befcacfd0e9f14e384fd Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 7 Nov 2014 11:16:25 +0100 Subject: [PATCH 06/12] less verbose error messages --- arangod/Aql/Expression.cpp | 12 +++++++----- arangod/Aql/Query.cpp | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 9f29ab64e6..919fa28368 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -142,11 +142,13 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx, return _func->execute(_ast->query(), trx, docColls, argv, startPos, vars, regs); } catch (triagens::arango::Exception& ex) { - ex.addToMessage(" while evaluating expression "); - auto json = _node->toJson(TRI_UNKNOWN_MEM_ZONE, false); - if (json != nullptr) { - ex.addToMessage(triagens::basics::JsonHelper::toString(json)); - TRI_Free(TRI_UNKNOWN_MEM_ZONE, json); + if (_ast->query()->verboseErrors()) { + ex.addToMessage(" while evaluating expression "); + auto json = _node->toJson(TRI_UNKNOWN_MEM_ZONE, false); + if (json != nullptr) { + ex.addToMessage(triagens::basics::JsonHelper::toString(json)); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, json); + } } throw; } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index f27c7db3ac..368080cd4b 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -713,7 +713,6 @@ QueryResultV8 Query::executeV8 (QueryRegistry* registry) { } } - //////////////////////////////////////////////////////////////////////////////// /// @brief parse an AQL query //////////////////////////////////////////////////////////////////////////////// From e408fd98a364f2bd304a2e76c38543129333af14 Mon Sep 17 00:00:00 2001 From: Thomas Schmidts Date: Fri, 7 Nov 2014 11:33:00 +0100 Subject: [PATCH 07/12] Changed http to https --- .../Books/Users/localtheme/templates/book/includes/summary.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Books/Users/localtheme/templates/book/includes/summary.html b/Documentation/Books/Users/localtheme/templates/book/includes/summary.html index 81a55ec183..eac01df05c 100644 --- a/Documentation/Books/Users/localtheme/templates/book/includes/summary.html +++ b/Documentation/Books/Users/localtheme/templates/book/includes/summary.html @@ -65,7 +65,7 @@ {{ articles(summary.chapters) }} - +