1
0
Fork 0

fixes for explainer and condition

This commit is contained in:
Jan Steemann 2015-10-08 16:22:52 +02:00
parent c6c6ba8f6f
commit 6a4e9658c5
5 changed files with 268 additions and 199 deletions

View File

@ -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];
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -426,12 +426,15 @@ 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;
void Condition::storeAttributeAccess (VariableUsageType& variableUsage,
AstNode const* node,
size_t position,
AttributeSideType side) {
auto storeAttributeAccess = [] (VariableUsageType& variableUsage, AstNode const* node, size_t position, AttributeSideType side) {
std::pair<Variable const*, std::vector<triagens::basics::AttributeName>> result; std::pair<Variable const*, std::vector<triagens::basics::AttributeName>> result;
if (! node->isAttributeAccessForVariable(result)) { if (! node->isAttributeAccessForVariable(result)) {
@ -468,8 +471,13 @@ void Condition::optimize (ExecutionPlan* plan) {
dst.emplace_back(position, side); 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,7 +524,6 @@ 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;
@ -603,7 +620,8 @@ void Condition::optimize (ExecutionPlan* plan) {
if (inNode->numMembers() == 0) { if (inNode->numMembers() == 0) {
// no values left after merging -> IMPOSSIBLE // no values left after merging -> IMPOSSIBLE
_root->removeMemberUnchecked(i); _root->removeMemberUnchecked(r);
retry = true;
goto fastForwardToNextOrItem; goto fastForwardToNextOrItem;
} }
@ -627,8 +645,8 @@ void Condition::optimize (ExecutionPlan* plan) {
// impossible condition // impossible condition
// j = positions.size(); // j = positions.size();
// we remove this one, so fast forward the loops to their end: // we remove this one, so fast forward the loops to their end:
_root->removeMemberUnchecked(i); _root->removeMemberUnchecked(r);
/// i -= 1; <- wenn wir das ohne goto machen... retry = true;
goto fastForwardToNextOrItem; goto fastForwardToNextOrItem;
} }
case CompareResult::SELF_CONTAINED_IN_OTHER: { case CompareResult::SELF_CONTAINED_IN_OTHER: {
@ -667,12 +685,17 @@ void Condition::optimize (ExecutionPlan* plan) {
} // cross compare sub-and-nodes } // cross compare sub-and-nodes
} // foreach sub-and-node } // foreach sub-and-node
fastForwardToNextOrItem: fastForwardToNextOrItem:
continue; 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;
} }
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -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
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -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(", ") + " */");

View File

@ -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(", ") + " */");