mirror of https://gitee.com/bigwinds/arangodb
fixes for explainer and condition
This commit is contained in:
parent
c6c6ba8f6f
commit
6a4e9658c5
|
@ -667,7 +667,10 @@ namespace triagens {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
inline AstNode* getMemberUnchecked (size_t i) const throw() {
|
inline AstNode* getMemberUnchecked (size_t i) const throw() {
|
||||||
return members.at(i);
|
#ifdef TRI_ENABLE_MAINTAINER_MODE
|
||||||
|
TRI_ASSERT(i < members.size());
|
||||||
|
#endif
|
||||||
|
return members[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -426,50 +426,58 @@ ConditionPart::ConditionPartCompareResult const ConditionPart::ResultsTable[3][7
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Condition::optimize (ExecutionPlan* plan) {
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
typedef std::vector<std::pair<size_t, AttributeSideType>> UsagePositionType;
|
/// @brief registers an attribute access for a particular (collection) variable
|
||||||
typedef std::unordered_map<std::string, UsagePositionType> AttributeUsageType;
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
typedef std::unordered_map<Variable const*, AttributeUsageType> VariableUsageType;
|
|
||||||
|
|
||||||
auto storeAttributeAccess = [] (VariableUsageType& variableUsage, AstNode const* node, size_t position, AttributeSideType side) {
|
|
||||||
std::pair<Variable const*, std::vector<triagens::basics::AttributeName>> result;
|
|
||||||
|
|
||||||
if (! node->isAttributeAccessForVariable(result)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto variable = result.first;
|
|
||||||
|
|
||||||
if (variable != nullptr) {
|
void Condition::storeAttributeAccess (VariableUsageType& variableUsage,
|
||||||
auto it = variableUsage.find(variable);
|
AstNode const* node,
|
||||||
|
size_t position,
|
||||||
|
AttributeSideType side) {
|
||||||
|
|
||||||
if (it == variableUsage.end()) {
|
std::pair<Variable const*, std::vector<triagens::basics::AttributeName>> result;
|
||||||
// nothing recorded yet for variable
|
|
||||||
it = variableUsage.emplace(variable, AttributeUsageType()).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string attributeName;
|
|
||||||
TRI_AttributeNamesToString(result.second, attributeName, false);
|
|
||||||
|
|
||||||
auto it2 = (*it).second.find(attributeName);
|
|
||||||
|
|
||||||
if (it2 == (*it).second.end()) {
|
if (! node->isAttributeAccessForVariable(result)) {
|
||||||
// nothing recorded yet for attribute name in this variable
|
return;
|
||||||
it2 = (*it).second.emplace(attributeName, UsagePositionType()).first;
|
}
|
||||||
}
|
auto variable = result.first;
|
||||||
|
|
||||||
auto& dst = (*it2).second;
|
|
||||||
|
|
||||||
if (! dst.empty() && dst.back().first == position) {
|
if (variable != nullptr) {
|
||||||
// already have this attribute for this variable. can happen in case a condition refers to itself (e.g. a.x == a.x)
|
auto it = variableUsage.find(variable);
|
||||||
// in this case, we won't optimize it
|
|
||||||
dst.erase(dst.begin() + dst.size() - 1);
|
if (it == variableUsage.end()) {
|
||||||
}
|
// nothing recorded yet for variable
|
||||||
else {
|
it = variableUsage.emplace(variable, AttributeUsageType()).first;
|
||||||
dst.emplace_back(position, side);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
std::string attributeName;
|
||||||
|
TRI_AttributeNamesToString(result.second, attributeName, false);
|
||||||
|
|
||||||
|
auto it2 = (*it).second.find(attributeName);
|
||||||
|
|
||||||
|
if (it2 == (*it).second.end()) {
|
||||||
|
// nothing recorded yet for attribute name in this variable
|
||||||
|
it2 = (*it).second.emplace(attributeName, UsagePositionType()).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& dst = (*it2).second;
|
||||||
|
|
||||||
|
if (! dst.empty() && dst.back().first == position) {
|
||||||
|
// already have this attribute for this variable. can happen in case a condition refers to itself (e.g. a.x == a.x)
|
||||||
|
// in this case, we won't optimize it
|
||||||
|
dst.erase(dst.begin() + dst.size() - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dst.emplace_back(position, side);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief optimize the condition expression tree
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Condition::optimize (ExecutionPlan* plan) {
|
||||||
if (_root == nullptr) {
|
if (_root == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -478,13 +486,15 @@ void Condition::optimize (ExecutionPlan* plan) {
|
||||||
TRI_ASSERT(_root->type == NODE_TYPE_OPERATOR_NARY_OR);
|
TRI_ASSERT(_root->type == NODE_TYPE_OPERATOR_NARY_OR);
|
||||||
|
|
||||||
// handle sub nodes or top-level OR node
|
// handle sub nodes or top-level OR node
|
||||||
size_t const n = _root->numMembers();
|
size_t n = _root->numMembers();
|
||||||
|
size_t r = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) { // foreach OR-Node
|
while (r < n) { // foreach OR-Node
|
||||||
auto andNode = _root->getMemberUnchecked(i);
|
bool retry = false;
|
||||||
|
auto andNode = _root->getMemberUnchecked(r);
|
||||||
TRI_ASSERT(andNode->type == NODE_TYPE_OPERATOR_NARY_AND);
|
TRI_ASSERT(andNode->type == NODE_TYPE_OPERATOR_NARY_AND);
|
||||||
|
|
||||||
restartThisOrItem:
|
restartThisOrItem:
|
||||||
size_t const andNumMembers = andNode->numMembers();
|
size_t const andNumMembers = andNode->numMembers();
|
||||||
|
|
||||||
// deduplicate all IN arrays
|
// deduplicate all IN arrays
|
||||||
|
@ -492,6 +502,14 @@ void Condition::optimize (ExecutionPlan* plan) {
|
||||||
deduplicateInOperation(andNode->getMemberUnchecked(j));
|
deduplicateInOperation(andNode->getMemberUnchecked(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (andNumMembers <= 1) {
|
||||||
|
// simple AND item with 0 or 1 members. nothing to do
|
||||||
|
++r;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_ASSERT(andNumMembers > 1);
|
||||||
|
|
||||||
// move IN operation to the front to make comparison code below simpler
|
// move IN operation to the front to make comparison code below simpler
|
||||||
andNode->sortMembers([] (AstNode const* lhs, AstNode const* rhs) -> bool {
|
andNode->sortMembers([] (AstNode const* lhs, AstNode const* rhs) -> bool {
|
||||||
if (lhs->type == NODE_TYPE_OPERATOR_BINARY_IN) {
|
if (lhs->type == NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||||
|
@ -506,173 +524,178 @@ void Condition::optimize (ExecutionPlan* plan) {
|
||||||
return (lhs < rhs); // compare pointers to have a deterministic order
|
return (lhs < rhs); // compare pointers to have a deterministic order
|
||||||
});
|
});
|
||||||
|
|
||||||
if (andNumMembers > 1) {
|
// optimization is only necessary if an AND node has multiple members
|
||||||
// optimization is only necessary if an AND node has multiple members
|
VariableUsageType variableUsage;
|
||||||
VariableUsageType variableUsage;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < andNumMembers; ++j) {
|
for (size_t j = 0; j < andNumMembers; ++j) {
|
||||||
auto operand = andNode->getMemberUnchecked(j);
|
auto operand = andNode->getMemberUnchecked(j);
|
||||||
|
|
||||||
if (operand->isComparisonOperator()) {
|
if (operand->isComparisonOperator()) {
|
||||||
auto lhs = operand->getMember(0);
|
auto lhs = operand->getMember(0);
|
||||||
auto rhs = operand->getMember(1);
|
auto rhs = operand->getMember(1);
|
||||||
|
|
||||||
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
storeAttributeAccess(variableUsage, lhs, j, ATTRIBUTE_LEFT);
|
storeAttributeAccess(variableUsage, lhs, j, ATTRIBUTE_LEFT);
|
||||||
}
|
}
|
||||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||||
rhs->type == NODE_TYPE_EXPANSION) {
|
rhs->type == NODE_TYPE_EXPANSION) {
|
||||||
storeAttributeAccess(variableUsage, rhs, j, ATTRIBUTE_RIGHT);
|
storeAttributeAccess(variableUsage, rhs, j, ATTRIBUTE_RIGHT);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// now find the variables and attributes for which there are multiple conditions
|
// now find the variables and attributes for which there are multiple conditions
|
||||||
for (auto const& it : variableUsage) { // foreach sub-and-node
|
for (auto const& it : variableUsage) { // foreach sub-and-node
|
||||||
auto variable = it.first;
|
auto variable = it.first;
|
||||||
|
|
||||||
for (auto const& it2 : it.second) { // cross compare sub-and-nodes
|
for (auto const& it2 : it.second) { // cross compare sub-and-nodes
|
||||||
auto const& attributeName = it2.first;
|
auto const& attributeName = it2.first;
|
||||||
auto const& positions = it2.second;
|
auto const& positions = it2.second;
|
||||||
|
|
||||||
if (positions.size() <= 1) {
|
if (positions.size() <= 1) {
|
||||||
// none or only one occurence of the attribute
|
// none or only one occurence of the attribute
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple occurrences of the same attribute
|
||||||
|
auto leftNode = andNode->getMemberUnchecked(positions[0].first);
|
||||||
|
|
||||||
|
ConditionPart current(variable, attributeName, 0, leftNode, positions[0].second);
|
||||||
|
|
||||||
|
if (! current.valueNode->isConstant()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// current.dump();
|
||||||
|
size_t j = 1;
|
||||||
|
|
||||||
|
while (j < positions.size()) {
|
||||||
|
auto rightNode = andNode->getMemberUnchecked(positions[j].first);
|
||||||
|
|
||||||
|
ConditionPart other(variable, attributeName, j, rightNode, positions[j].second);
|
||||||
|
|
||||||
|
if (! other.valueNode->isConstant()) {
|
||||||
|
++j;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiple occurrences of the same attribute
|
// IN-merging
|
||||||
auto leftNode = andNode->getMemberUnchecked(positions[0].first);
|
if (leftNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
||||||
|
leftNode->getMemberUnchecked(1)->isConstant()) {
|
||||||
|
TRI_ASSERT(leftNode->numMembers() == 2);
|
||||||
|
|
||||||
ConditionPart current(variable, attributeName, 0, leftNode, positions[0].second);
|
if (rightNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
||||||
|
rightNode->getMemberUnchecked(1)->isConstant()) {
|
||||||
|
// merge IN with IN on same attribute
|
||||||
|
TRI_ASSERT(rightNode->numMembers() == 2);
|
||||||
|
|
||||||
if (! current.valueNode->isConstant()) {
|
auto merged = _ast->createNodeBinaryOperator(
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// current.dump();
|
|
||||||
size_t j = 1;
|
|
||||||
|
|
||||||
while (j < positions.size()) {
|
|
||||||
auto rightNode = andNode->getMemberUnchecked(positions[j].first);
|
|
||||||
|
|
||||||
ConditionPart other(variable, attributeName, j, rightNode, positions[j].second);
|
|
||||||
|
|
||||||
if (! other.valueNode->isConstant()) {
|
|
||||||
++j;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IN-merging
|
|
||||||
if (leftNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
|
||||||
leftNode->getMemberUnchecked(1)->isConstant()) {
|
|
||||||
TRI_ASSERT(leftNode->numMembers() == 2);
|
|
||||||
|
|
||||||
if (rightNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
|
||||||
rightNode->getMemberUnchecked(1)->isConstant()) {
|
|
||||||
// merge IN with IN on same attribute
|
|
||||||
TRI_ASSERT(rightNode->numMembers() == 2);
|
|
||||||
|
|
||||||
auto merged = _ast->createNodeBinaryOperator(
|
|
||||||
NODE_TYPE_OPERATOR_BINARY_IN,
|
NODE_TYPE_OPERATOR_BINARY_IN,
|
||||||
leftNode->getMemberUnchecked(0),
|
leftNode->getMemberUnchecked(0),
|
||||||
mergeInOperations(leftNode, rightNode)
|
mergeInOperations(leftNode, rightNode)
|
||||||
);
|
);
|
||||||
andNode->removeMemberUnchecked(positions[j].first);
|
andNode->removeMemberUnchecked(positions[j].first);
|
||||||
andNode->changeMember(positions[0].first, merged);
|
andNode->changeMember(positions[0].first, merged);
|
||||||
goto restartThisOrItem;
|
goto restartThisOrItem;
|
||||||
}
|
|
||||||
else if (rightNode->isSimpleComparisonOperator()) {
|
|
||||||
// merge other comparison operator with IN
|
|
||||||
TRI_ASSERT(rightNode->numMembers() == 2);
|
|
||||||
|
|
||||||
auto inNode = _ast->createNodeArray();
|
|
||||||
auto values = leftNode->getMemberUnchecked(1);
|
|
||||||
|
|
||||||
for (size_t k = 0; k < values->numMembers(); ++k) {
|
|
||||||
auto value = values->getMemberUnchecked(k);
|
|
||||||
ConditionPart::ConditionPartCompareResult res = ConditionPart::ResultsTable
|
|
||||||
[CompareAstNodes(value, other.valueNode, false) + 1]
|
|
||||||
[0 /*NODE_TYPE_OPERATOR_BINARY_EQ*/]
|
|
||||||
[other.whichCompareOperation()];
|
|
||||||
|
|
||||||
bool const keep = (res == CompareResult::OTHER_CONTAINED_IN_SELF || res == CompareResult::CONVERT_EQUAL);
|
|
||||||
if (keep) {
|
|
||||||
inNode->addMember(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inNode->numMembers() == 0) {
|
|
||||||
// no values left after merging -> IMPOSSIBLE
|
|
||||||
_root->removeMemberUnchecked(i);
|
|
||||||
goto fastForwardToNextOrItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the new array of values
|
|
||||||
andNode->getMemberUnchecked(positions[0].first)->changeMember(1, inNode);
|
|
||||||
// remove the other operator
|
|
||||||
andNode->removeMemberUnchecked(positions[j].first);
|
|
||||||
goto restartThisOrItem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// end of IN-merging
|
else if (rightNode->isSimpleComparisonOperator()) {
|
||||||
|
// merge other comparison operator with IN
|
||||||
|
TRI_ASSERT(rightNode->numMembers() == 2);
|
||||||
|
|
||||||
// Results are -1, 0, 1, move to 0, 1, 2 for the lookup:
|
auto inNode = _ast->createNodeArray();
|
||||||
ConditionPart::ConditionPartCompareResult res = ConditionPart::ResultsTable
|
auto values = leftNode->getMemberUnchecked(1);
|
||||||
[CompareAstNodes(current.valueNode, other.valueNode, false) + 1]
|
|
||||||
[current.whichCompareOperation()]
|
|
||||||
[other.whichCompareOperation()];
|
|
||||||
|
|
||||||
switch (res) {
|
for (size_t k = 0; k < values->numMembers(); ++k) {
|
||||||
case CompareResult::IMPOSSIBLE: {
|
auto value = values->getMemberUnchecked(k);
|
||||||
// impossible condition
|
ConditionPart::ConditionPartCompareResult res = ConditionPart::ResultsTable
|
||||||
// j = positions.size();
|
[CompareAstNodes(value, other.valueNode, false) + 1]
|
||||||
// we remove this one, so fast forward the loops to their end:
|
[0 /*NODE_TYPE_OPERATOR_BINARY_EQ*/]
|
||||||
_root->removeMemberUnchecked(i);
|
[other.whichCompareOperation()];
|
||||||
/// i -= 1; <- wenn wir das ohne goto machen...
|
|
||||||
|
bool const keep = (res == CompareResult::OTHER_CONTAINED_IN_SELF || res == CompareResult::CONVERT_EQUAL);
|
||||||
|
if (keep) {
|
||||||
|
inNode->addMember(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inNode->numMembers() == 0) {
|
||||||
|
// no values left after merging -> IMPOSSIBLE
|
||||||
|
_root->removeMemberUnchecked(r);
|
||||||
|
retry = true;
|
||||||
goto fastForwardToNextOrItem;
|
goto fastForwardToNextOrItem;
|
||||||
}
|
}
|
||||||
case CompareResult::SELF_CONTAINED_IN_OTHER: {
|
|
||||||
std::cout << "SELF IS CONTAINED IN OTHER\n";
|
|
||||||
andNode->removeMemberUnchecked(positions[0].first);
|
|
||||||
goto restartThisOrItem;
|
|
||||||
}
|
|
||||||
case CompareResult::OTHER_CONTAINED_IN_SELF: {
|
|
||||||
std::cout << "OTHER IS CONTAINED IN SELF\n";
|
|
||||||
andNode->removeMemberUnchecked(positions[j].first);
|
|
||||||
goto restartThisOrItem;
|
|
||||||
}
|
|
||||||
case CompareResult::CONVERT_EQUAL: { /// beide gehen, werden umgeformt zu a == x (== y)
|
|
||||||
andNode->removeMemberUnchecked(positions[j].first);
|
|
||||||
auto origNode = andNode->getMemberUnchecked(positions[0].first);
|
|
||||||
auto newNode = plan->getAst()->createNode(NODE_TYPE_OPERATOR_BINARY_EQ);
|
|
||||||
for (size_t iMemb = 0; iMemb < origNode->numMembers(); iMemb++) {
|
|
||||||
newNode->addMember(origNode->getMemberUnchecked(iMemb));
|
|
||||||
}
|
|
||||||
|
|
||||||
andNode->changeMember(positions[0].first, newNode);
|
// use the new array of values
|
||||||
std::cout << "RESULT equals X/Y\n";
|
andNode->getMemberUnchecked(positions[0].first)->changeMember(1, inNode);
|
||||||
break;
|
// remove the other operator
|
||||||
}
|
andNode->removeMemberUnchecked(positions[j].first);
|
||||||
case CompareResult::DISJOINT: {
|
goto restartThisOrItem;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CompareResult::UNKNOWN: {
|
|
||||||
std::cout << "UNKNOWN\n";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++j;
|
|
||||||
}
|
}
|
||||||
} // cross compare sub-and-nodes
|
// end of IN-merging
|
||||||
} // foreach sub-and-node
|
|
||||||
|
|
||||||
fastForwardToNextOrItem:
|
// Results are -1, 0, 1, move to 0, 1, 2 for the lookup:
|
||||||
continue;
|
ConditionPart::ConditionPartCompareResult res = ConditionPart::ResultsTable
|
||||||
|
[CompareAstNodes(current.valueNode, other.valueNode, false) + 1]
|
||||||
|
[current.whichCompareOperation()]
|
||||||
|
[other.whichCompareOperation()];
|
||||||
|
|
||||||
|
switch (res) {
|
||||||
|
case CompareResult::IMPOSSIBLE: {
|
||||||
|
// impossible condition
|
||||||
|
// j = positions.size();
|
||||||
|
// we remove this one, so fast forward the loops to their end:
|
||||||
|
_root->removeMemberUnchecked(r);
|
||||||
|
retry = true;
|
||||||
|
goto fastForwardToNextOrItem;
|
||||||
|
}
|
||||||
|
case CompareResult::SELF_CONTAINED_IN_OTHER: {
|
||||||
|
std::cout << "SELF IS CONTAINED IN OTHER\n";
|
||||||
|
andNode->removeMemberUnchecked(positions[0].first);
|
||||||
|
goto restartThisOrItem;
|
||||||
|
}
|
||||||
|
case CompareResult::OTHER_CONTAINED_IN_SELF: {
|
||||||
|
std::cout << "OTHER IS CONTAINED IN SELF\n";
|
||||||
|
andNode->removeMemberUnchecked(positions[j].first);
|
||||||
|
goto restartThisOrItem;
|
||||||
|
}
|
||||||
|
case CompareResult::CONVERT_EQUAL: { /// beide gehen, werden umgeformt zu a == x (== y)
|
||||||
|
andNode->removeMemberUnchecked(positions[j].first);
|
||||||
|
auto origNode = andNode->getMemberUnchecked(positions[0].first);
|
||||||
|
auto newNode = plan->getAst()->createNode(NODE_TYPE_OPERATOR_BINARY_EQ);
|
||||||
|
for (size_t iMemb = 0; iMemb < origNode->numMembers(); iMemb++) {
|
||||||
|
newNode->addMember(origNode->getMemberUnchecked(iMemb));
|
||||||
|
}
|
||||||
|
|
||||||
|
andNode->changeMember(positions[0].first, newNode);
|
||||||
|
std::cout << "RESULT equals X/Y\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompareResult::DISJOINT: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompareResult::UNKNOWN: {
|
||||||
|
std::cout << "UNKNOWN\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
} // cross compare sub-and-nodes
|
||||||
|
} // foreach sub-and-node
|
||||||
|
|
||||||
|
fastForwardToNextOrItem:
|
||||||
|
if (retry) {
|
||||||
|
// number of root sub-nodes has probably changed.
|
||||||
|
// now recalculate the number and don't modify r!
|
||||||
|
n = _root->numMembers();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// root nodes hasn't changed. go to next sub-node!
|
||||||
|
++r;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -853,7 +876,7 @@ AstNode* Condition::transformNode (AstNode* node) {
|
||||||
node->changeMember(0, transformNode(sub));
|
node->changeMember(0, transformNode(sub));
|
||||||
// fallthrough intentional
|
// fallthrough intentional
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,16 @@ namespace triagens {
|
||||||
|
|
||||||
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
|
// --SECTION-- constructors / destructors
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -211,6 +221,15 @@ namespace triagens {
|
||||||
|
|
||||||
void normalize (ExecutionPlan* plan);
|
void normalize (ExecutionPlan* plan);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief registers an attribute access for a particular (collection) variable
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void storeAttributeAccess (VariableUsageType&,
|
||||||
|
AstNode const*,
|
||||||
|
size_t,
|
||||||
|
AttributeSideType);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief optimize the condition expression tree
|
/// @brief optimize the condition expression tree
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -191,7 +191,7 @@ function printIndexes (indexes) {
|
||||||
stringBuilder.appendLine(" " + value("none"));
|
stringBuilder.appendLine(" " + value("none"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var maxIdLen = String("Id").length;
|
var maxIdLen = String("By").length;
|
||||||
var maxCollectionLen = String("Collection").length;
|
var maxCollectionLen = String("Collection").length;
|
||||||
var maxUniqueLen = String("Unique").length;
|
var maxUniqueLen = String("Unique").length;
|
||||||
var maxSparseLen = String("Sparse").length;
|
var maxSparseLen = String("Sparse").length;
|
||||||
|
@ -216,7 +216,7 @@ function printIndexes (indexes) {
|
||||||
maxCollectionLen = l;
|
maxCollectionLen = l;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var line = " " + pad(1 + maxIdLen - String("Id").length) + header("Id") + " " +
|
var line = " " + pad(1 + maxIdLen - String("By").length) + header("By") + " " +
|
||||||
header("Type") + pad(1 + maxTypeLen - "Type".length) + " " +
|
header("Type") + pad(1 + maxTypeLen - "Type".length) + " " +
|
||||||
header("Collection") + pad(1 + maxCollectionLen - "Collection".length) + " " +
|
header("Collection") + pad(1 + maxCollectionLen - "Collection".length) + " " +
|
||||||
header("Unique") + pad(1 + maxUniqueLen - "Unique".length) + " " +
|
header("Unique") + pad(1 + maxUniqueLen - "Unique".length) + " " +
|
||||||
|
@ -232,7 +232,13 @@ function printIndexes (indexes) {
|
||||||
var sparsity = (indexes[i].hasOwnProperty("sparse") ? (indexes[i].sparse ? "true" : "false") : "n/a");
|
var sparsity = (indexes[i].hasOwnProperty("sparse") ? (indexes[i].sparse ? "true" : "false") : "n/a");
|
||||||
var fields = indexes[i].fields.map(attribute).join(", ");
|
var fields = indexes[i].fields.map(attribute).join(", ");
|
||||||
var fieldsLen = indexes[i].fields.map(attributeUncolored).join(", ").length;
|
var fieldsLen = indexes[i].fields.map(attributeUncolored).join(", ").length;
|
||||||
var ranges = "[ " + indexes[i].ranges + " ]";
|
var ranges;
|
||||||
|
if (indexes[i].hasOwnProperty("condition")) {
|
||||||
|
ranges = indexes[i].condition;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ranges = "[ " + indexes[i].ranges + " ]";
|
||||||
|
}
|
||||||
var selectivity = (indexes[i].hasOwnProperty("selectivityEstimate") ?
|
var selectivity = (indexes[i].hasOwnProperty("selectivityEstimate") ?
|
||||||
(indexes[i].selectivityEstimate * 100).toFixed(2) + " %" :
|
(indexes[i].selectivityEstimate * 100).toFixed(2) + " %" :
|
||||||
"n/a"
|
"n/a"
|
||||||
|
@ -378,8 +384,6 @@ function processQuery (query, explain) {
|
||||||
references[node.subNodes[0].subNodes[0].name] = node.subNodes[0].subNodes[1];
|
references[node.subNodes[0].subNodes[0].name] = node.subNodes[0].subNodes[1];
|
||||||
}
|
}
|
||||||
return buildExpression(node.subNodes[1]);
|
return buildExpression(node.subNodes[1]);
|
||||||
case "verticalizer":
|
|
||||||
return buildExpression(node.subNodes[0]);
|
|
||||||
case "user function call":
|
case "user function call":
|
||||||
return func(node.name) + "(" + ((node.subNodes && node.subNodes[0].subNodes) || [ ]).map(buildExpression).join(", ") + ")" + " " + annotation("/* user-defined function */");
|
return func(node.name) + "(" + ((node.subNodes && node.subNodes[0].subNodes) || [ ]).map(buildExpression).join(", ") + ")" + " " + annotation("/* user-defined function */");
|
||||||
case "function call":
|
case "function call":
|
||||||
|
@ -416,6 +420,10 @@ function processQuery (query, explain) {
|
||||||
return buildExpression(node.subNodes[0]) + " && " + buildExpression(node.subNodes[1]);
|
return buildExpression(node.subNodes[0]) + " && " + buildExpression(node.subNodes[1]);
|
||||||
case "ternary":
|
case "ternary":
|
||||||
return buildExpression(node.subNodes[0]) + " ? " + buildExpression(node.subNodes[1]) + " : " + buildExpression(node.subNodes[2]);
|
return buildExpression(node.subNodes[0]) + " ? " + buildExpression(node.subNodes[1]) + " : " + buildExpression(node.subNodes[2]);
|
||||||
|
case "n-ary or":
|
||||||
|
return node.subNodes.map(function(sub) { return buildExpression(sub); }).join(" || ");
|
||||||
|
case "n-ary and":
|
||||||
|
return node.subNodes.map(function(sub) { return buildExpression(sub); }).join(" && ");
|
||||||
default:
|
default:
|
||||||
return "unhandled node type (" + node.type + ")";
|
return "unhandled node type (" + node.type + ")";
|
||||||
}
|
}
|
||||||
|
@ -486,11 +494,14 @@ function processQuery (query, explain) {
|
||||||
case "IndexNode":
|
case "IndexNode":
|
||||||
collectionVariables[node.outVariable.id] = node.collection;
|
collectionVariables[node.outVariable.id] = node.collection;
|
||||||
var types = [ ];
|
var types = [ ];
|
||||||
node.indexes.forEach(function (idx) {
|
node.indexes.forEach(function (idx, i) {
|
||||||
types.push((idx.reverse ? "reverse " : "") + idx.type + " index scan");
|
types.push((idx.reverse ? "reverse " : "") + idx.type + " index scan");
|
||||||
idx.collection = node.collection;
|
idx.collection = node.collection;
|
||||||
idx.node = node.id;
|
idx.node = node.id;
|
||||||
idx.ranges = [ ];
|
var condition = "";
|
||||||
|
if (node.condition.type === 'n-ary or') {
|
||||||
|
idx.condition = buildExpression(node.condition.subNodes[i]);
|
||||||
|
}
|
||||||
indexes.push(idx);
|
indexes.push(idx);
|
||||||
});
|
});
|
||||||
return keyword("FOR") + " " + variableName(node.outVariable) + " " + keyword("IN") + " " + collection(node.collection) + " " + annotation("/* " + types.join(", ") + " */");
|
return keyword("FOR") + " " + variableName(node.outVariable) + " " + keyword("IN") + " " + collection(node.collection) + " " + annotation("/* " + types.join(", ") + " */");
|
||||||
|
|
|
@ -190,7 +190,7 @@ function printIndexes (indexes) {
|
||||||
stringBuilder.appendLine(" " + value("none"));
|
stringBuilder.appendLine(" " + value("none"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var maxIdLen = String("Id").length;
|
var maxIdLen = String("By").length;
|
||||||
var maxCollectionLen = String("Collection").length;
|
var maxCollectionLen = String("Collection").length;
|
||||||
var maxUniqueLen = String("Unique").length;
|
var maxUniqueLen = String("Unique").length;
|
||||||
var maxSparseLen = String("Sparse").length;
|
var maxSparseLen = String("Sparse").length;
|
||||||
|
@ -215,7 +215,7 @@ function printIndexes (indexes) {
|
||||||
maxCollectionLen = l;
|
maxCollectionLen = l;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var line = " " + pad(1 + maxIdLen - String("Id").length) + header("Id") + " " +
|
var line = " " + pad(1 + maxIdLen - String("By").length) + header("By") + " " +
|
||||||
header("Type") + pad(1 + maxTypeLen - "Type".length) + " " +
|
header("Type") + pad(1 + maxTypeLen - "Type".length) + " " +
|
||||||
header("Collection") + pad(1 + maxCollectionLen - "Collection".length) + " " +
|
header("Collection") + pad(1 + maxCollectionLen - "Collection".length) + " " +
|
||||||
header("Unique") + pad(1 + maxUniqueLen - "Unique".length) + " " +
|
header("Unique") + pad(1 + maxUniqueLen - "Unique".length) + " " +
|
||||||
|
@ -231,7 +231,13 @@ function printIndexes (indexes) {
|
||||||
var sparsity = (indexes[i].hasOwnProperty("sparse") ? (indexes[i].sparse ? "true" : "false") : "n/a");
|
var sparsity = (indexes[i].hasOwnProperty("sparse") ? (indexes[i].sparse ? "true" : "false") : "n/a");
|
||||||
var fields = indexes[i].fields.map(attribute).join(", ");
|
var fields = indexes[i].fields.map(attribute).join(", ");
|
||||||
var fieldsLen = indexes[i].fields.map(attributeUncolored).join(", ").length;
|
var fieldsLen = indexes[i].fields.map(attributeUncolored).join(", ").length;
|
||||||
var ranges = "[ " + indexes[i].ranges + " ]";
|
var ranges;
|
||||||
|
if (indexes[i].hasOwnProperty("condition")) {
|
||||||
|
ranges = indexes[i].condition;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ranges = "[ " + indexes[i].ranges + " ]";
|
||||||
|
}
|
||||||
var selectivity = (indexes[i].hasOwnProperty("selectivityEstimate") ?
|
var selectivity = (indexes[i].hasOwnProperty("selectivityEstimate") ?
|
||||||
(indexes[i].selectivityEstimate * 100).toFixed(2) + " %" :
|
(indexes[i].selectivityEstimate * 100).toFixed(2) + " %" :
|
||||||
"n/a"
|
"n/a"
|
||||||
|
@ -377,8 +383,6 @@ function processQuery (query, explain) {
|
||||||
references[node.subNodes[0].subNodes[0].name] = node.subNodes[0].subNodes[1];
|
references[node.subNodes[0].subNodes[0].name] = node.subNodes[0].subNodes[1];
|
||||||
}
|
}
|
||||||
return buildExpression(node.subNodes[1]);
|
return buildExpression(node.subNodes[1]);
|
||||||
case "verticalizer":
|
|
||||||
return buildExpression(node.subNodes[0]);
|
|
||||||
case "user function call":
|
case "user function call":
|
||||||
return func(node.name) + "(" + ((node.subNodes && node.subNodes[0].subNodes) || [ ]).map(buildExpression).join(", ") + ")" + " " + annotation("/* user-defined function */");
|
return func(node.name) + "(" + ((node.subNodes && node.subNodes[0].subNodes) || [ ]).map(buildExpression).join(", ") + ")" + " " + annotation("/* user-defined function */");
|
||||||
case "function call":
|
case "function call":
|
||||||
|
@ -415,6 +419,10 @@ function processQuery (query, explain) {
|
||||||
return buildExpression(node.subNodes[0]) + " && " + buildExpression(node.subNodes[1]);
|
return buildExpression(node.subNodes[0]) + " && " + buildExpression(node.subNodes[1]);
|
||||||
case "ternary":
|
case "ternary":
|
||||||
return buildExpression(node.subNodes[0]) + " ? " + buildExpression(node.subNodes[1]) + " : " + buildExpression(node.subNodes[2]);
|
return buildExpression(node.subNodes[0]) + " ? " + buildExpression(node.subNodes[1]) + " : " + buildExpression(node.subNodes[2]);
|
||||||
|
case "n-ary or":
|
||||||
|
return node.subNodes.map(function(sub) { return buildExpression(sub); }).join(" || ");
|
||||||
|
case "n-ary and":
|
||||||
|
return node.subNodes.map(function(sub) { return buildExpression(sub); }).join(" && ");
|
||||||
default:
|
default:
|
||||||
return "unhandled node type (" + node.type + ")";
|
return "unhandled node type (" + node.type + ")";
|
||||||
}
|
}
|
||||||
|
@ -485,11 +493,16 @@ function processQuery (query, explain) {
|
||||||
case "IndexNode":
|
case "IndexNode":
|
||||||
collectionVariables[node.outVariable.id] = node.collection;
|
collectionVariables[node.outVariable.id] = node.collection;
|
||||||
var types = [ ];
|
var types = [ ];
|
||||||
node.indexes.forEach(function (idx) {
|
node.indexes.forEach(function (idx, i) {
|
||||||
types.push((idx.reverse ? "reverse " : "") + idx.type + " index scan");
|
types.push((idx.reverse ? "reverse " : "") + idx.type + " index scan");
|
||||||
idx.collection = node.collection;
|
idx.collection = node.collection;
|
||||||
idx.node = node.id;
|
idx.node = node.id;
|
||||||
idx.ranges = [ ];
|
if (node.condition.type === 'n-ary or') {
|
||||||
|
idx.condition = buildExpression(node.condition.subNodes[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idx.conditionn = "";
|
||||||
|
}
|
||||||
indexes.push(idx);
|
indexes.push(idx);
|
||||||
});
|
});
|
||||||
return keyword("FOR") + " " + variableName(node.outVariable) + " " + keyword("IN") + " " + collection(node.collection) + " " + annotation("/* " + types.join(", ") + " */");
|
return keyword("FOR") + " " + variableName(node.outVariable) + " " + keyword("IN") + " " + collection(node.collection) + " " + annotation("/* " + types.join(", ") + " */");
|
||||||
|
|
Loading…
Reference in New Issue