1
0
Fork 0
arangodb/arangod/Aql/Condition.h

487 lines
19 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, condition
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 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
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_AQL_CONDITION_H
#define ARANGODB_AQL_CONDITION_H 1
#include "Basics/Common.h"
#include "Aql/AstNode.h"
#include "Basics/AttributeNameParser.h"
#include "Basics/JsonHelper.h"
namespace triagens {
namespace aql {
class Ast;
class EnumerateCollectionNode;
class ExecutionPlan;
struct Index;
class SortCondition;
struct Variable;
// -----------------------------------------------------------------------------
// --SECTION-- public types
// -----------------------------------------------------------------------------
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
};
// -----------------------------------------------------------------------------
// --SECTION-- struct ConditionPart
// -----------------------------------------------------------------------------
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<triagens::basics::AttributeName> 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;
};
// -----------------------------------------------------------------------------
// --SECTION-- class Condition
// -----------------------------------------------------------------------------
class Condition {
// -----------------------------------------------------------------------------
// --SECTION-- private typedefs
// -----------------------------------------------------------------------------
private:
typedef std::vector<std::pair<size_t, AttributeSideType>> UsagePositionType;
typedef std::unordered_map<std::string, UsagePositionType> AttributeUsageType;
typedef std::unordered_map<Variable const*, AttributeUsageType> VariableUsageType;
// -----------------------------------------------------------------------------
// --SECTION-- constructors / destructors
// -----------------------------------------------------------------------------
public:
Condition (Condition const&) = delete;
Condition& operator= (Condition const&) = delete;
Condition () = delete;
////////////////////////////////////////////////////////////////////////////////
/// @brief create the condition
////////////////////////////////////////////////////////////////////////////////
explicit Condition (Ast*);
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the condition
////////////////////////////////////////////////////////////////////////////////
~Condition ();
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief return the condition root
////////////////////////////////////////////////////////////////////////////////
inline AstNode const* 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
////////////////////////////////////////////////////////////////////////////////
triagens::basics::Json toJson (TRI_memory_zone_t* zone, bool verbose) const {
if (_root == nullptr) {
return triagens::basics::Json(triagens::basics::Json::Object);
}
return triagens::basics::Json(zone, _root->toJson(zone, verbose));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a condition from JSON
////////////////////////////////////////////////////////////////////////////////
static Condition* fromJson (ExecutionPlan*, triagens::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 const* removeIndexCondition (Variable const*,
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove (now) invalid variables from the condition
////////////////////////////////////////////////////////////////////////////////
bool removeInvalidVariables (std::unordered_set<Variable const*> 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<bool, bool> findIndexes (EnumerateCollectionNode const*,
std::vector<Index const*>&,
SortCondition const*);
// -----------------------------------------------------------------------------
// --SECTION-- private methods
// -----------------------------------------------------------------------------
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<Index const*>&);
////////////////////////////////////////////////////////////////////////////////
/// @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 TRI_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<bool, bool> findIndexForAndNode (size_t,
Variable const*,
EnumerateCollectionNode const*,
std::vector<Index const*>&,
SortCondition const*);
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
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
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: