//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// #ifndef ARANGOD_AQL_CONDITION_H #define ARANGOD_AQL_CONDITION_H 1 #include "Basics/Common.h" #include "Aql/AstNode.h" #include "Basics/AttributeNameParser.h" #include "Basics/JsonHelper.h" namespace arangodb { namespace aql { class Ast; class EnumerateCollectionNode; class ExecutionPlan; struct Index; class SortCondition; struct Variable; enum ConditionPartCompareResult { IMPOSSIBLE = 0, SELF_CONTAINED_IN_OTHER = 1, OTHER_CONTAINED_IN_SELF = 2, DISJOINT = 3, CONVERT_EQUAL = 4, UNKNOWN = 5 }; //////////////////////////////////////////////////////////////////////////////// /// @brief side on which an attribute occurs in a condition //////////////////////////////////////////////////////////////////////////////// enum AttributeSideType { ATTRIBUTE_LEFT, ATTRIBUTE_RIGHT }; struct ConditionPart { static ConditionPartCompareResult const ResultsTable[3][7][7]; ConditionPart() = delete; ConditionPart(Variable const*, std::string const&, AstNode const*, AttributeSideType, void*); ConditionPart(Variable const*, std::vector const&, AstNode const*, AttributeSideType, void*); ~ConditionPart(); inline int whichCompareOperation() const { switch (operatorType) { case NODE_TYPE_OPERATOR_BINARY_EQ: return 0; case NODE_TYPE_OPERATOR_BINARY_NE: return 1; case NODE_TYPE_OPERATOR_BINARY_LT: return 2; case NODE_TYPE_OPERATOR_BINARY_LE: return 3; case NODE_TYPE_OPERATOR_BINARY_GE: return 4; case NODE_TYPE_OPERATOR_BINARY_GT: return 5; default: return 6; // not a compare operator. } } ////////////////////////////////////////////////////////////////////////////// /// @brief returns the lower bound ////////////////////////////////////////////////////////////////////////////// inline AstNode const* lowerBound() const { if (operatorType == NODE_TYPE_OPERATOR_BINARY_GT || operatorType == NODE_TYPE_OPERATOR_BINARY_GE || operatorType == NODE_TYPE_OPERATOR_BINARY_EQ) { return valueNode; } if (operatorType == NODE_TYPE_OPERATOR_BINARY_IN && valueNode->isConstant() && valueNode->isArray() && valueNode->numMembers() > 0) { // return first item from IN array. // this requires IN arrays to be sorted, which they should be when // we get here return valueNode->getMember(0); } return nullptr; } ////////////////////////////////////////////////////////////////////////////// /// @brief returns if the lower bound is inclusive ////////////////////////////////////////////////////////////////////////////// inline bool isLowerInclusive() const { if (operatorType == NODE_TYPE_OPERATOR_BINARY_GE || operatorType == NODE_TYPE_OPERATOR_BINARY_EQ || operatorType == NODE_TYPE_OPERATOR_BINARY_IN) { return true; } return false; } ////////////////////////////////////////////////////////////////////////////// /// @brief returns the upper bound ////////////////////////////////////////////////////////////////////////////// inline AstNode const* upperBound() const { if (operatorType == NODE_TYPE_OPERATOR_BINARY_LT || operatorType == NODE_TYPE_OPERATOR_BINARY_LE || operatorType == NODE_TYPE_OPERATOR_BINARY_EQ) { return valueNode; } if (operatorType == NODE_TYPE_OPERATOR_BINARY_IN && valueNode->isConstant() && valueNode->isArray() && valueNode->numMembers() > 0) { // return last item from IN array. // this requires IN arrays to be sorted, which they should be when // we get here return valueNode->getMember(valueNode->numMembers() - 1); } return nullptr; } ////////////////////////////////////////////////////////////////////////////// /// @brief returns if the upper bound is inclusive ////////////////////////////////////////////////////////////////////////////// inline bool isUpperInclusive() const { if (operatorType == NODE_TYPE_OPERATOR_BINARY_LE || operatorType == NODE_TYPE_OPERATOR_BINARY_EQ || operatorType == NODE_TYPE_OPERATOR_BINARY_IN) { return true; } return false; } ////////////////////////////////////////////////////////////////////////////// /// @brief true if the condition is completely covered by the other condition ////////////////////////////////////////////////////////////////////////////// bool isCoveredBy(ConditionPart const&) const; Variable const* variable; std::string attributeName; AstNodeType operatorType; AstNode const* operatorNode; AstNode const* valueNode; void* data; bool isExpanded; }; class Condition { private: typedef std::vector> UsagePositionType; typedef std::unordered_map AttributeUsageType; typedef std::unordered_map VariableUsageType; public: Condition(Condition const&) = delete; Condition& operator=(Condition const&) = delete; Condition() = delete; ////////////////////////////////////////////////////////////////////////////// /// @brief create the condition ////////////////////////////////////////////////////////////////////////////// explicit Condition(Ast*); ////////////////////////////////////////////////////////////////////////////// /// @brief destroy the condition ////////////////////////////////////////////////////////////////////////////// ~Condition(); public: ////////////////////////////////////////////////////////////////////////////// /// @brief return the condition root ////////////////////////////////////////////////////////////////////////////// inline AstNode* root() const { return _root; } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the condition is empty ////////////////////////////////////////////////////////////////////////////// inline bool isEmpty() const { if (_root == nullptr) { return true; } return (_root->numMembers() == 0); } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the condition results will be sorted (this is only /// relevant if the condition consists of multiple ORs) ////////////////////////////////////////////////////////////////////////////// inline bool isSorted() const { return _isSorted; } ////////////////////////////////////////////////////////////////////////////// /// @brief return the condition as a Json object ////////////////////////////////////////////////////////////////////////////// arangodb::basics::Json toJson(TRI_memory_zone_t* zone, bool verbose) const { if (_root == nullptr) { return arangodb::basics::Json(arangodb::basics::Json::Object); } return arangodb::basics::Json(zone, _root->toJson(zone, verbose)); } ////////////////////////////////////////////////////////////////////////////// /// @brief export the condition as VelocyPack ////////////////////////////////////////////////////////////////////////////// void toVelocyPack(arangodb::velocypack::Builder&, bool) const; ////////////////////////////////////////////////////////////////////////////// /// @brief create a condition from JSON ////////////////////////////////////////////////////////////////////////////// static Condition* fromJson(ExecutionPlan*, arangodb::basics::Json const&); ////////////////////////////////////////////////////////////////////////////// /// @brief clone the condition ////////////////////////////////////////////////////////////////////////////// Condition* clone() const; ////////////////////////////////////////////////////////////////////////////// /// @brief add a sub-condition to the condition /// the sub-condition will be AND-combined with the existing condition(s) ////////////////////////////////////////////////////////////////////////////// void andCombine(AstNode const*); ////////////////////////////////////////////////////////////////////////////// /// @brief normalize the condition /// this will convert the condition into its disjunctive normal form ////////////////////////////////////////////////////////////////////////////// void normalize(ExecutionPlan*); ////////////////////////////////////////////////////////////////////////////// /// @brief normalize the condition /// this will convert the condition into its disjunctive normal form /// in this case we don't re-run the optimizer. Its expected that you /// don't want to remove eventually unneccessary filters. ////////////////////////////////////////////////////////////////////////////// void normalize(); ////////////////////////////////////////////////////////////////////////////// /// @brief removes condition parts from another ////////////////////////////////////////////////////////////////////////////// AstNode* removeIndexCondition(Variable const*, AstNode*); ////////////////////////////////////////////////////////////////////////////// /// @brief remove (now) invalid variables from the condition ////////////////////////////////////////////////////////////////////////////// bool removeInvalidVariables(std::unordered_set const&); ////////////////////////////////////////////////////////////////////////////// /// @brief locate indexes which can be used for conditions /// return value is a pair indicating whether the index can be used for /// filtering(first) and sorting(second) ////////////////////////////////////////////////////////////////////////////// std::pair findIndexes(EnumerateCollectionNode const*, std::vector&, SortCondition const*); ////////////////////////////////////////////////////////////////////////////// /// @brief get the attributes for a sub-condition that are const /// (i.e. compared with equality) ////////////////////////////////////////////////////////////////////////////// std::vector> getConstAttributes (Variable const*, bool); private: ////////////////////////////////////////////////////////////////////////////// /// @brief sort ORs for the same attribute so they are in ascending value /// order. this will only work if the condition is for a single attribute ////////////////////////////////////////////////////////////////////////////// bool sortOrs(Variable const*, std::vector&); ////////////////////////////////////////////////////////////////////////////// /// @brief optimize the condition expression tree ////////////////////////////////////////////////////////////////////////////// void optimize(ExecutionPlan*); ////////////////////////////////////////////////////////////////////////////// /// @brief registers an attribute access for a particular (collection) /// variable ////////////////////////////////////////////////////////////////////////////// void storeAttributeAccess(VariableUsageType&, AstNode const*, size_t, AttributeSideType); //////////////////////////////////////////////////////////////////////////////// /// @brief validate the condition's AST //////////////////////////////////////////////////////////////////////////////// #ifdef ARANGODB_ENABLE_MAINTAINER_MODE void validateAst(AstNode const*, int); #endif ////////////////////////////////////////////////////////////////////////////// /// @brief checks if the current condition covers the other ////////////////////////////////////////////////////////////////////////////// bool canRemove(ConditionPart const&, AstNode const*) const; ////////////////////////////////////////////////////////////////////////////// /// @brief deduplicate IN condition values /// this may modify the node in place ////////////////////////////////////////////////////////////////////////////// void deduplicateInOperation(AstNode*); ////////////////////////////////////////////////////////////////////////////// /// @brief merge the values from two IN operations ////////////////////////////////////////////////////////////////////////////// AstNode* mergeInOperations(AstNode const*, AstNode const*); ////////////////////////////////////////////////////////////////////////////// /// @brief merges the current node with the sub nodes of same type ////////////////////////////////////////////////////////////////////////////// AstNode* collapse(AstNode const*); ////////////////////////////////////////////////////////////////////////////// /// @brief converts binary logical operators into n-ary operators ////////////////////////////////////////////////////////////////////////////// AstNode* transformNode(AstNode*); ////////////////////////////////////////////////////////////////////////////// /// @brief Creates a top-level OR node if it does not already exist, and make /// sure that all second level nodes are AND nodes. Additionally, this step /// will /// remove all NOP nodes. ////////////////////////////////////////////////////////////////////////////// AstNode* fixRoot(AstNode*, int); ////////////////////////////////////////////////////////////////////////////// /// @brief tests if the given index supports the sort condition ////////////////////////////////////////////////////////////////////////////// bool indexSupportsSort(Index const*, Variable const*, SortCondition const*, size_t, double&) const; ////////////////////////////////////////////////////////////////////////////// /// @brief finds the best index that can match this single node ////////////////////////////////////////////////////////////////////////////// std::pair findIndexForAndNode(size_t, Variable const*, EnumerateCollectionNode const*, std::vector&, SortCondition const*); private: ////////////////////////////////////////////////////////////////////////////// /// @brief the AST, used for memory management ////////////////////////////////////////////////////////////////////////////// Ast* _ast; ////////////////////////////////////////////////////////////////////////////// /// @brief root node of the condition ////////////////////////////////////////////////////////////////////////////// AstNode* _root; ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the condition was already normalized ////////////////////////////////////////////////////////////////////////////// bool _isNormalized; ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the condition will return a sorted result ////////////////////////////////////////////////////////////////////////////// bool _isSorted; }; } } #endif