1
0
Fork 0

removed canThrow tag for various built-in functions

This commit is contained in:
Jan Steemann 2014-11-05 14:40:00 +01:00
parent 19dc758c1a
commit bf30927913
8 changed files with 123 additions and 85 deletions

View File

@ -1073,12 +1073,18 @@ bool AstNode::isComparisonOperator () const {
////////////////////////////////////////////////////////////////////////////////
bool AstNode::canThrow () const {
if (hasFlag(FLAG_THROWS)) {
// fast track exit
return true;
}
// check sub-nodes first
size_t const n = numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = getMember(i);
if (member->canThrow()) {
// if any sub-node may throw, the whole branch may throw
setFlag(FLAG_THROWS);
return true;
}
}
@ -1093,13 +1099,16 @@ bool AstNode::canThrow () const {
// potentially throwing. This is not correct on the one hand, but on
// the other hand we must not optimize or move non-deterministic functions
// during optimization
// TODO: move the check for isDeterministic into a function of its
// own and check it from the optimizer rules
return func->canThrow || ! func->isDeterministic;
if (func->canThrow) {
setFlag(FLAG_THROWS);
return true;
}
return false;
}
if (type == NODE_TYPE_FCALL_USER) {
// user functions can always throw
setFlag(FLAG_THROWS);
return true;
}
@ -1144,12 +1153,18 @@ bool AstNode::canRunOnDBServer () const {
////////////////////////////////////////////////////////////////////////////////
bool AstNode::isDeterministic () const {
if (hasFlag(FLAG_NONDETERMINISTIC)) {
// fast track exit
return false;
}
// check sub-nodes first
size_t const n = numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = getMember(i);
if (! member->isDeterministic()) {
// if any sub-node is non-deterministic, we are neither
setFlag(FLAG_NONDETERMINISTIC);
return false;
}
}
@ -1157,11 +1172,16 @@ bool AstNode::isDeterministic () const {
if (type == NODE_TYPE_FCALL) {
// built-in functions may or may not be deterministic
auto func = static_cast<Function*>(getData());
return func->isDeterministic;
if (! func->isDeterministic) {
setFlag(FLAG_NONDETERMINISTIC);
return false;
}
return true;
}
if (type == NODE_TYPE_FCALL_USER) {
// user functions are always non-deterministic
setFlag(FLAG_NONDETERMINISTIC);
return false;
}

View File

@ -59,11 +59,12 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
enum AstNodeFlagType : uint8_t {
FLAG_SORTED = 1, // node is a list and its members are sorted asc.
FLAG_CONSTANT = 2, // node value is constant (i.e. not dynamic)
FLAG_DYNAMIC = 4, // node value is dynamic (i.e. not constant)
FLAG_SIMPLE = 8 // node value is simple (i.e. for use in a simple expression)
FLAG_SORTED = 1, // node is a list and its members are sorted asc.
FLAG_CONSTANT = 2, // node value is constant (i.e. not dynamic)
FLAG_DYNAMIC = 4, // node value is dynamic (i.e. not constant)
FLAG_SIMPLE = 8, // node value is simple (i.e. for use in a simple expression)
FLAG_THROWS = 16, // node can throws an exception
FLAG_NONDETERMINISTIC = 32 // node produces non-deterministic result (e.g. function call nodes)
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -1824,6 +1824,10 @@ SortInformation SortNode::getSortInformation (ExecutionPlan* plan,
// variable introduced by a calculation
auto expression = static_cast<CalculationNode*>(setter)->expression();
if (! expression->isDeterministic()) {
result.isDeterministic = false;
}
if (! expression->isAttributeAccess() &&
! expression->isReference()) {
result.isComplex = true;

View File

@ -1718,9 +1718,10 @@ namespace triagens {
};
std::vector<std::tuple<ExecutionNode const*, std::string, bool>> criteria;
bool isValid = true;
bool isComplex = false;
bool canThrow = false;
bool isValid = true;
bool isDeterministic = true;
bool isComplex = false;
bool canThrow = false;
Match isCoveredBy (SortInformation const& other) {
if (! isValid || ! other.isValid) {

View File

@ -104,63 +104,63 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
{ "TO_LIST", Function("TO_LIST", "AQL_TO_LIST", ".", true, false, true) },
// string functions
{ "CONCAT", Function("CONCAT", "AQL_CONCAT", "sz,sz|+", true, true, true) },
{ "CONCAT_SEPARATOR", Function("CONCAT_SEPARATOR", "AQL_CONCAT_SEPARATOR", "s,sz,sz|+", true, true, true) },
{ "CHAR_LENGTH", Function("CHAR_LENGTH", "AQL_CHAR_LENGTH", "s", true, true, true) },
{ "LOWER", Function("LOWER", "AQL_LOWER", "s", true, true, true) },
{ "UPPER", Function("UPPER", "AQL_UPPER", "s", true, true, true) },
{ "SUBSTRING", Function("SUBSTRING", "AQL_SUBSTRING", "s,n|n", true, true, true) },
{ "CONTAINS", Function("CONTAINS", "AQL_CONTAINS", "s,s|b", true, true, true) },
{ "LIKE", Function("LIKE", "AQL_LIKE", "s,r|b", true, true, true) },
{ "LEFT", Function("LEFT", "AQL_LEFT", "s,n", true, true, true) },
{ "RIGHT", Function("RIGHT", "AQL_RIGHT", "s,n", true, true, true) },
{ "TRIM", Function("TRIM", "AQL_TRIM", "s|n", true, true, true) },
{ "FIND_FIRST", Function("FIND_FIRST", "AQL_FIND_FIRST", "s,s|zn,zn", true, true, true) },
{ "FIND_LAST", Function("FIND_LAST", "AQL_FIND_LAST", "s,s|zn,zn", true, true, true) },
{ "CONCAT", Function("CONCAT", "AQL_CONCAT", "sz,sz|+", true, false, true) },
{ "CONCAT_SEPARATOR", Function("CONCAT_SEPARATOR", "AQL_CONCAT_SEPARATOR", "s,sz,sz|+", true, false, true) },
{ "CHAR_LENGTH", Function("CHAR_LENGTH", "AQL_CHAR_LENGTH", "s", true, false, true) },
{ "LOWER", Function("LOWER", "AQL_LOWER", "s", true, false, true) },
{ "UPPER", Function("UPPER", "AQL_UPPER", "s", true, false, true) },
{ "SUBSTRING", Function("SUBSTRING", "AQL_SUBSTRING", "s,n|n", true, false, true) },
{ "CONTAINS", Function("CONTAINS", "AQL_CONTAINS", "s,s|b", true, false, true) },
{ "LIKE", Function("LIKE", "AQL_LIKE", "s,r|b", true, false, true) },
{ "LEFT", Function("LEFT", "AQL_LEFT", "s,n", true, false, true) },
{ "RIGHT", Function("RIGHT", "AQL_RIGHT", "s,n", true, false, true) },
{ "TRIM", Function("TRIM", "AQL_TRIM", "s|n", true, false, true) },
{ "FIND_FIRST", Function("FIND_FIRST", "AQL_FIND_FIRST", "s,s|zn,zn", true, false, true) },
{ "FIND_LAST", Function("FIND_LAST", "AQL_FIND_LAST", "s,s|zn,zn", true, false, true) },
// numeric functions
{ "FLOOR", Function("FLOOR", "AQL_FLOOR", "n", true, true, true) },
{ "CEIL", Function("CEIL", "AQL_CEIL", "n", true, true, true) },
{ "ROUND", Function("ROUND", "AQL_ROUND", "n", true, true, true) },
{ "ABS", Function("ABS", "AQL_ABS", "n", true, true, true) },
{ "FLOOR", Function("FLOOR", "AQL_FLOOR", "n", true, false, true) },
{ "CEIL", Function("CEIL", "AQL_CEIL", "n", true, false, true) },
{ "ROUND", Function("ROUND", "AQL_ROUND", "n", true, false, true) },
{ "ABS", Function("ABS", "AQL_ABS", "n", true, false, true) },
{ "RAND", Function("RAND", "AQL_RAND", "", false, false, true) },
{ "SQRT", Function("SQRT", "AQL_SQRT", "n", true, true, true) },
{ "SQRT", Function("SQRT", "AQL_SQRT", "n", true, false, true) },
// list functions
{ "RANGE", Function("RANGE", "AQL_RANGE", "n,n|n", true, true, true) },
{ "UNION", Function("UNION", "AQL_UNION", "l,l|+",true, true, true) },
{ "UNION_DISTINCT", Function("UNION_DISTINCT", "AQL_UNION_DISTINCT", "l,l|+", true, true, true) },
{ "MINUS", Function("MINUS", "AQL_MINUS", "l,l|+", true, true, true) },
{ "INTERSECTION", Function("INTERSECTION", "AQL_INTERSECTION", "l,l|+", true, true, true) },
{ "FLATTEN", Function("FLATTEN", "AQL_FLATTEN", "l|n", true, true, true) },
{ "LENGTH", Function("LENGTH", "AQL_LENGTH", "las", true, true, true) },
{ "MIN", Function("MIN", "AQL_MIN", "l", true, true, true) },
{ "MAX", Function("MAX", "AQL_MAX", "l", true, true, true) },
{ "SUM", Function("SUM", "AQL_SUM", "l", true, true, true) },
{ "MEDIAN", Function("MEDIAN", "AQL_MEDIAN", "l", true, true, true) },
{ "AVERAGE", Function("AVERAGE", "AQL_AVERAGE", "l", true, true, true) },
{ "VARIANCE_SAMPLE", Function("VARIANCE_SAMPLE", "AQL_VARIANCE_SAMPLE", "l", true, true, true) },
{ "VARIANCE_POPULATION", Function("VARIANCE_POPULATION", "AQL_VARIANCE_POPULATION", "l", true, true, true) },
{ "STDDEV_SAMPLE", Function("STDDEV_SAMPLE", "AQL_STDDEV_SAMPLE", "l", true, true, true) },
{ "STDDEV_POPULATION", Function("STDDEV_POPULATION", "AQL_STDDEV_POPULATION", "l", true, true, true) },
{ "UNIQUE", Function("UNIQUE", "AQL_UNIQUE", "l", true, true, true) },
{ "SLICE", Function("SLICE", "AQL_SLICE", "l,n|n", true, true, true) },
{ "REVERSE", Function("REVERSE", "AQL_REVERSE", "ls", true, true, true) }, // note: REVERSE() can be applied on strings, too
{ "FIRST", Function("FIRST", "AQL_FIRST", "l", true, true, true) },
{ "LAST", Function("LAST", "AQL_LAST", "l", true, true, true) },
{ "NTH", Function("NTH", "AQL_NTH", "l,n", true, true, true) },
{ "POSITION", Function("POSITION", "AQL_POSITION", "l,.|b", true, true, true) },
{ "RANGE", Function("RANGE", "AQL_RANGE", "n,n|n", true, false, true) },
{ "UNION", Function("UNION", "AQL_UNION", "l,l|+",true, false, true) },
{ "UNION_DISTINCT", Function("UNION_DISTINCT", "AQL_UNION_DISTINCT", "l,l|+", true, false, true) },
{ "MINUS", Function("MINUS", "AQL_MINUS", "l,l|+", true, false, true) },
{ "INTERSECTION", Function("INTERSECTION", "AQL_INTERSECTION", "l,l|+", true, false, true) },
{ "FLATTEN", Function("FLATTEN", "AQL_FLATTEN", "l|n", true, false, true) },
{ "LENGTH", Function("LENGTH", "AQL_LENGTH", "las", true, false, true) },
{ "MIN", Function("MIN", "AQL_MIN", "l", true, false, true) },
{ "MAX", Function("MAX", "AQL_MAX", "l", true, false, true) },
{ "SUM", Function("SUM", "AQL_SUM", "l", true, false, true) },
{ "MEDIAN", Function("MEDIAN", "AQL_MEDIAN", "l", true, false, true) },
{ "AVERAGE", Function("AVERAGE", "AQL_AVERAGE", "l", true, false, true) },
{ "VARIANCE_SAMPLE", Function("VARIANCE_SAMPLE", "AQL_VARIANCE_SAMPLE", "l", true, false, true) },
{ "VARIANCE_POPULATION", Function("VARIANCE_POPULATION", "AQL_VARIANCE_POPULATION", "l", true, false, true) },
{ "STDDEV_SAMPLE", Function("STDDEV_SAMPLE", "AQL_STDDEV_SAMPLE", "l", true, false, true) },
{ "STDDEV_POPULATION", Function("STDDEV_POPULATION", "AQL_STDDEV_POPULATION", "l", true, false, true) },
{ "UNIQUE", Function("UNIQUE", "AQL_UNIQUE", "l", true, false, true) },
{ "SLICE", Function("SLICE", "AQL_SLICE", "l,n|n", true, false, true) },
{ "REVERSE", Function("REVERSE", "AQL_REVERSE", "ls", true, false, true) }, // note: REVERSE() can be applied on strings, too
{ "FIRST", Function("FIRST", "AQL_FIRST", "l", true, false, true) },
{ "LAST", Function("LAST", "AQL_LAST", "l", true, false, true) },
{ "NTH", Function("NTH", "AQL_NTH", "l,n", true, false, true) },
{ "POSITION", Function("POSITION", "AQL_POSITION", "l,.|b", true, false, true) },
// document functions
{ "HAS", Function("HAS", "AQL_HAS", "az,s", true, true, true) },
{ "ATTRIBUTES", Function("ATTRIBUTES", "AQL_ATTRIBUTES", "a|b,b", true, true, true) },
{ "MERGE", Function("MERGE", "AQL_MERGE", "a,a|+", true, true, true) },
{ "MERGE_RECURSIVE", Function("MERGE_RECURSIVE", "AQL_MERGE_RECURSIVE", "a,a|+", true, true, true) },
{ "HAS", Function("HAS", "AQL_HAS", "az,s", true, false, true) },
{ "ATTRIBUTES", Function("ATTRIBUTES", "AQL_ATTRIBUTES", "a|b,b", true, false, true) },
{ "MERGE", Function("MERGE", "AQL_MERGE", "a,a|+", true, false, true) },
{ "MERGE_RECURSIVE", Function("MERGE_RECURSIVE", "AQL_MERGE_RECURSIVE", "a,a|+", true, false, true) },
{ "DOCUMENT", Function("DOCUMENT", "AQL_DOCUMENT", "h.|.", false, true, false) },
{ "MATCHES", Function("MATCHES", "AQL_MATCHES", ".,l|b", true, true, true) },
{ "UNSET", Function("UNSET", "AQL_UNSET", "a,sl|+", true, true, true) },
{ "KEEP", Function("KEEP", "AQL_KEEP", "a,sl|+", true, true, true) },
{ "TRANSLATE", Function("TRANSLATE", "AQL_TRANSLATE", ".,a|.", true, true, true) },
{ "MATCHES", Function("MATCHES", "AQL_MATCHES", ".,l|b", true, false, true) },
{ "UNSET", Function("UNSET", "AQL_UNSET", "a,sl|+", true, false, true) },
{ "KEEP", Function("KEEP", "AQL_KEEP", "a,sl|+", true, false, true) },
{ "TRANSLATE", Function("TRANSLATE", "AQL_TRANSLATE", ".,a|.", true, false, true) },
// geo functions
{ "NEAR", Function("NEAR", "AQL_NEAR", "h,n,n|nz,s", false, true, false) },
@ -199,26 +199,26 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
// date functions
{ "DATE_NOW", Function("DATE_NOW", "AQL_DATE_NOW", "", false, false, true) },
{ "DATE_TIMESTAMP", Function("DATE_TIMESTAMP", "AQL_DATE_TIMESTAMP", "ns|ns,ns,ns,ns,ns,ns", true, true, true) },
{ "DATE_ISO8601", Function("DATE_ISO8601", "AQL_DATE_ISO8601", "ns|ns,ns,ns,ns,ns,ns", true, true, true) },
{ "DATE_DAYOFWEEK", Function("DATE_DAYOFWEEK", "AQL_DATE_DAYOFWEEK", "ns", true, true, true) },
{ "DATE_YEAR", Function("DATE_YEAR", "AQL_DATE_YEAR", "ns", true, true, true) },
{ "DATE_MONTH", Function("DATE_MONTH", "AQL_DATE_MONTH", "ns", true, true, true) },
{ "DATE_DAY", Function("DATE_DAY", "AQL_DATE_DAY", "ns", true, true, true) },
{ "DATE_HOUR", Function("DATE_HOUR", "AQL_DATE_HOUR", "ns", true, true, true) },
{ "DATE_MINUTE", Function("DATE_MINUTE", "AQL_DATE_MINUTE", "ns", true, true, true) },
{ "DATE_SECOND", Function("DATE_SECOND", "AQL_DATE_SECOND", "ns", true, true, true) },
{ "DATE_MILLISECOND", Function("DATE_MILLISECOND", "AQL_DATE_MILLISECOND", "ns", true, true, true) },
{ "DATE_TIMESTAMP", Function("DATE_TIMESTAMP", "AQL_DATE_TIMESTAMP", "ns|ns,ns,ns,ns,ns,ns", true, false, true) },
{ "DATE_ISO8601", Function("DATE_ISO8601", "AQL_DATE_ISO8601", "ns|ns,ns,ns,ns,ns,ns", true, false, true) },
{ "DATE_DAYOFWEEK", Function("DATE_DAYOFWEEK", "AQL_DATE_DAYOFWEEK", "ns", true, false, true) },
{ "DATE_YEAR", Function("DATE_YEAR", "AQL_DATE_YEAR", "ns", true, false, true) },
{ "DATE_MONTH", Function("DATE_MONTH", "AQL_DATE_MONTH", "ns", true, false, true) },
{ "DATE_DAY", Function("DATE_DAY", "AQL_DATE_DAY", "ns", true, false, true) },
{ "DATE_HOUR", Function("DATE_HOUR", "AQL_DATE_HOUR", "ns", true, false, true) },
{ "DATE_MINUTE", Function("DATE_MINUTE", "AQL_DATE_MINUTE", "ns", true, false, true) },
{ "DATE_SECOND", Function("DATE_SECOND", "AQL_DATE_SECOND", "ns", true, false, true) },
{ "DATE_MILLISECOND", Function("DATE_MILLISECOND", "AQL_DATE_MILLISECOND", "ns", true, false, true) },
// misc functions
{ "FAIL", Function("FAIL", "AQL_FAIL", "|s", false, true, true) },
{ "PASSTHRU", Function("PASSTHRU", "AQL_PASSTHRU", ".", false, true, true) },
{ "SLEEP", Function("SLEEP", "AQL_SLEEP", "n", false, false, true) },
{ "PASSTHRU", Function("PASSTHRU", "AQL_PASSTHRU", ".", false, false, true) },
{ "SLEEP", Function("SLEEP", "AQL_SLEEP", "n", false, true, true) },
{ "COLLECTIONS", Function("COLLECTIONS", "AQL_COLLECTIONS", "", false, true, false) },
{ "NOT_NULL", Function("NOT_NULL", "AQL_NOT_NULL", ".|+", true, true, true) },
{ "NOT_NULL", Function("NOT_NULL", "AQL_NOT_NULL", ".|+", true, false, true) },
{ "FIRST_LIST", Function("FIRST_LIST", "AQL_FIRST_LIST", ".|+", true, false, true) },
{ "FIRST_DOCUMENT", Function("FIRST_DOCUMENT", "AQL_FIRST_DOCUMENT", ".|+", true, false, true) },
{ "PARSE_IDENTIFIER", Function("PARSE_IDENTIFIER", "AQL_PARSE_IDENTIFIER", ".", true, true, true) },
{ "PARSE_IDENTIFIER", Function("PARSE_IDENTIFIER", "AQL_PARSE_IDENTIFIER", ".", true, false, true) },
{ "SKIPLIST", Function("SKIPLIST", "AQL_SKIPLIST", "h,a|n,n", false, true, false) },
{ "CURRENT_USER", Function("CURRENT_USER", "AQL_CURRENT_USER", "", false, false, false) },
{ "CURRENT_DATABASE", Function("CURRENT_DATABASE", "AQL_CURRENT_DATABASE", "", false, false, false) }

View File

@ -96,8 +96,8 @@ int triagens::aql::removeRedundantSorts (Optimizer* opt,
if (nodesRelyingOnSort == 0) {
// a sort directly followed by another sort: now remove one of them
if (other.canThrow) {
// if the sort can throw, we must not remove it
if (other.canThrow || ! other.isDeterministic) {
// if the sort can throw or is non-deterministic, we must not remove it
break;
}
@ -267,8 +267,9 @@ int triagens::aql::moveCalculationsUpRule (Optimizer* opt,
for (auto n : nodes) {
auto nn = static_cast<CalculationNode*>(n);
if (nn->expression()->canThrow()) {
// we will only move expressions up that cannot throw
if (nn->expression()->canThrow() ||
! nn->expression()->isDeterministic()) {
// we will only move expressions up that cannot throw and that are deterministic
continue;
}
@ -367,6 +368,14 @@ int triagens::aql::moveFiltersUpRule (Optimizer* opt,
break;
}
if (current->getType() == EN::CALCULATION) {
// must not move a filter beyond a node with a non-deterministic result
auto calculation = static_cast<CalculationNode const*>(current);
if (! calculation->expression()->isDeterministic()) {
break;
}
}
bool found = false;
auto&& varsSet = current->getVariablesSetHere();
@ -650,9 +659,8 @@ int triagens::aql::removeUnnecessaryCalculationsRule (Optimizer* opt,
if (n->getType() == EN::CALCULATION) {
auto nn = static_cast<CalculationNode*>(n);
if (nn->canThrow() ||
! nn->expression()->isDeterministic()) {
// If this node can throw or is non-deterministic, we must not optimize it away!
if (nn->canThrow()) {
// If this node can throw, we must not optimize it away!
continue;
}
}

View File

@ -104,13 +104,16 @@ function optimizerRuleTestSuite () {
"FOR i IN 1..10 LET a = 1 FILTER i == 1 RETURN a",
"FOR i IN 1..10 FILTER i == 1 LET a = 1 RETURN i",
"FOR i IN 1..10 LET a = 25 + 7 RETURN i",
"FOR i IN 1..10 LET a = MIN([ i, 1 ]) LET b = i + 1 RETURN [ a, b ]",
"FOR i IN 1..10 LET a = RAND() LET b = 25 + i RETURN i",
"FOR i IN 1..10 LET a = SLEEP(0.1) LET b = i + 1 RETURN b",
"FOR i IN 1..10 FOR j IN 1..10 LET a = i + 2 RETURN i",
"FOR i IN 1..10 FILTER i == 7 LET a = i COLLECT x = i RETURN x"
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, paramEnabled);
assertEqual([ ruleName ], result.plan.rules);
assertEqual([ ruleName ], result.plan.rules, query);
});
},

View File

@ -88,7 +88,7 @@ function optimizerRuleTestSuite () {
"FOR i IN 1..10 LET a = i LET b = i FILTER a == b RETURN i",
"FOR i IN 1..10 FOR j IN 1..10 FILTER i > j RETURN i",
"FOR i IN 1..10 LET a = 2 * i FILTER a == 1 RETURN i",
"FOR i IN 1..10 LIMIT 1 FILTER i == 1 RETURN i"
"FOR i IN 1..10 LET a = SLEEP(1) FILTER i == 1 RETURN i" // SLEEP is non-deterministic
];
queries.forEach(function(query) {
@ -106,7 +106,8 @@ function optimizerRuleTestSuite () {
"FOR i IN 1..10 FOR j IN 1..10 FILTER i > 1 RETURN i",
"FOR i IN 1..10 LET x = (FOR j IN [i] RETURN j) FILTER i > 1 RETURN i",
"FOR i IN 1..10 FOR l IN 1..10 LET a = 2 * i FILTER i == 1 RETURN a",
"FOR i IN 1..10 FOR l IN 1..10 LET a = 2 * i FILTER i == 1 LIMIT 1 RETURN a"
"FOR i IN 1..10 FOR l IN 1..10 LET a = 2 * i FILTER i == 1 LIMIT 1 RETURN a",
"FOR i IN 1..10 FOR l IN 1..10 LET a = MIN([1, l]) FILTER i == 1 LIMIT 1 RETURN a",
];
queries.forEach(function(query) {