1
0
Fork 0

Merge branch 'aql2' of https://github.com/triAGENS/ArangoDB into aql2

This commit is contained in:
Jan Steemann 2014-09-08 09:05:58 +02:00
commit 7b3ee1e452
15 changed files with 1325 additions and 45 deletions

View File

@ -110,7 +110,21 @@ void AqlItemBlock::shrink (size_t nrItems) {
// erase all stored values in the region that we freed
for (size_t i = nrItems; i < _nrItems; ++i) {
for (RegisterId j = 0; j < _nrRegs; ++j) {
eraseValue(i, j);
AqlValue& a(_data[_nrRegs * i + j]);
if (! a.isEmpty()) {
auto it = _valueCount.find(a);
if (it != _valueCount.end()) {
if (--it->second == 0) {
a.destroy();
try {
_valueCount.erase(it);
}
catch (...) {
}
}
}
a.erase();
}
}
}
@ -131,12 +145,11 @@ void AqlItemBlock::clearRegisters (std::unordered_set<RegisterId>& toClear) {
auto it = _valueCount.find(a);
if (it != _valueCount.end()) {
if (--it->second == 0) {
a.destroy();
try {
_valueCount.erase(it);
}
catch (...) {
it->second++;
throw;
}
}
}

View File

@ -130,8 +130,6 @@ namespace triagens {
_valueCount.erase(it);
}
catch (...) {
it->second++;
throw;
}
}
}

View File

@ -444,7 +444,7 @@ Json AqlValue::toJson (AQL_TRANSACTION_V8* trx,
////////////////////////////////////////////////////////////////////////////////
/// @brief extract an attribute value from the AqlValue
/// this will return null if the value is not an array
/// this will return an empty Json if the value is not an array
////////////////////////////////////////////////////////////////////////////////
Json AqlValue::extractArrayMember (AQL_TRANSACTION_V8* trx,

View File

@ -1023,7 +1023,7 @@ void IndexRangeBlock::readSkiplistIndex () {
skiplistOperator = TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, nullptr,
nullptr, parameters.copy().steal(), shaper, nullptr, i, nullptr);
}
if (range._low._undefined) {
if (!range._low._undefined) {
auto op = range._low.toIndexOperator(false, parameters.copy(), shaper);
if (skiplistOperator != nullptr) {
skiplistOperator = TRI_CreateIndexOperator(TRI_AND_INDEX_OPERATOR,
@ -1033,7 +1033,7 @@ void IndexRangeBlock::readSkiplistIndex () {
skiplistOperator = op;
}
}
if (range._high._undefined) {
if (!range._high._undefined) {
auto op = range._high.toIndexOperator(true, parameters.copy(), shaper);
if (skiplistOperator != nullptr) {
skiplistOperator = TRI_CreateIndexOperator(TRI_AND_INDEX_OPERATOR,

View File

@ -263,7 +263,7 @@ ExecutionNode::CompareIndex (TRI_index_t* idx,
return match;
}
match.fullmatch = idx->_fields._length == attrs.size();
match.fullmatch = idx->_fields._length >= attrs.size();
size_t interestingCount = 0;
size_t j = 0;
@ -583,11 +583,12 @@ void IndexRangeNode::toJsonHelper (triagens::basics::Json& nodes,
TRI_json_t* idxJson = _index->json(_index);
if (idxJson != nullptr) {
try {
json.set("index", Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, idxJson)));
TRI_json_t* copy = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, idxJson);
json.set("index", Json(TRI_UNKNOWN_MEM_ZONE, copy));
}
catch (...) {
}
TRI_Free(TRI_CORE_MEM_ZONE, idxJson);
TRI_FreeJson(TRI_CORE_MEM_ZONE, idxJson);
}
// And add it:

View File

@ -107,6 +107,7 @@ namespace triagens {
ExecutionNode (size_t id)
: _id(id),
_estimatedCost(0.0),
_estimatedCostSet(false),
_varUsageValid(false) {
}
@ -353,8 +354,9 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
double getCost () {
if (_estimatedCost == 0.0) {
if (! _estimatedCostSet) {
_estimatedCost = estimateCost();
_estimatedCostSet = true;
TRI_ASSERT(_estimatedCost >= 0.0);
}
return _estimatedCost;
@ -517,10 +519,12 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
/// @brief _estimatedCost = 0 if uninitialised and otherwise stores the result
/// of estimateCost()
/// of estimateCost(), the bool indicates if the cost has been set, it starts
/// out as false
////////////////////////////////////////////////////////////////////////////////
double _estimatedCost;
bool _estimatedCostSet;
////////////////////////////////////////////////////////////////////////////////
/// @brief _varsUsedLater and _varsValid, the former contains those
@ -1424,23 +1428,31 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
struct SortInformation {
enum Match {
unequal,
weSupersede,
otherSupersedes,
allEqual
};
std::vector<std::tuple<ExecutionNode const*, std::string, bool>> criteria;
bool isValid = true;
bool isComplex = false;
bool isCoveredBy (SortInformation const& other) {
Match isCoveredBy (SortInformation const& other) {
if (! isValid || ! other.isValid) {
return false;
return unequal;
}
if (isComplex) {
return false;
return unequal;
}
size_t const n = criteria.size();
for (size_t i = 0; i < n; ++i) {
if (other.criteria.size() < i) {
return false;
if (other.criteria.size() <= i) {
return otherSupersedes;
}
auto ours = criteria[i];
@ -1448,16 +1460,18 @@ namespace triagens {
if (std::get<2>(ours) != std::get<2>(theirs)) {
// sort order is different
return false;
return unequal;
}
if (std::get<1>(ours) != std::get<1>(theirs)) {
// sort criterion is different
return false;
return unequal;
}
}
return true;
if (other.criteria.size() > n)
return weSupersede;
else
return allEqual;
}
};
@ -1525,7 +1539,12 @@ namespace triagens {
double estimateCost () {
double depCost = _dependencies.at(0)->getCost();
return log(depCost) * depCost;
if (depCost <= 2.0) {
return depCost;
}
else {
return log(depCost) * depCost;
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -184,7 +184,8 @@ void Expression::analyzeExpression () {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief execute an expression of type SIMPLE
/// @brief execute an expression of type SIMPLE, the convention is that
/// the resulting AqlValue will be destroyed outside eventually
////////////////////////////////////////////////////////////////////////////////
AqlValue Expression::executeSimpleExpression (AstNode const* node,
@ -207,6 +208,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
auto j = result.extractArrayMember(trx, myCollection, name);
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
@ -228,6 +230,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
if (result.isList()) {
if (index->isNumericValue()) {
auto j = result.extractListMember(trx, myCollection, index->getIntValue());
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
else if (index->isStringValue()) {
@ -238,6 +241,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
// stoll() might throw an exception if the string is not a number
int64_t position = static_cast<int64_t>(std::stoll(p));
auto j = result.extractListMember(trx, myCollection, position);
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
catch (...) {
@ -250,6 +254,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
if (index->isNumericValue()) {
std::string const indexString = std::to_string(index->getIntValue());
auto j = result.extractArrayMember(trx, myCollection, indexString.c_str());
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
else if (index->isStringValue()) {
@ -257,10 +262,12 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
TRI_ASSERT(p != nullptr);
auto j = result.extractArrayMember(trx, myCollection, p);
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
// fall-through to returning null
}
result.destroy();
return AqlValue(new Json(Json::Null));
}
@ -276,6 +283,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
list->add(result.toJson(trx, myCollection));
result.destroy();
}
return AqlValue(list);
}
@ -300,6 +308,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
resultArray->set(key, result.toJson(trx, myCollection));
result.destroy();
}
return AqlValue(resultArray);
}
@ -319,7 +328,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
// save the collection info
*collection = docColls[regs[i]];
return argv[startPos + regs[i]];
return argv[startPos + regs[i]].clone();
}
}
// fall-through to exception
@ -344,7 +353,9 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
auto member = node->getMember(0);
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
return func->implementation(trx, myCollection, result);
auto res2 = func->implementation(trx, myCollection, result);
result.destroy();
return res2;
}
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unhandled type in simple expression");

View File

@ -69,7 +69,14 @@ int triagens::aql::removeRedundantSorts (Optimizer* opt,
// we found another sort. now check if they are compatible!
auto other = static_cast<SortNode*>(current)->getSortInformation(plan);
if (sortInfo.isCoveredBy(other)) {
switch (sortInfo.isCoveredBy(other)) {
case triagens::aql::SortInformation::unequal:
break;
case triagens::aql::SortInformation::otherSupersedes:
toUnlink.insert(current);
break;
case triagens::aql::SortInformation::weSupersede:
case triagens::aql::SortInformation::allEqual:
// the sort at the start of the pipeline makes the sort at the end
// superfluous, so we'll remove it
toUnlink.insert(n);
@ -645,6 +652,12 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
buildRangeInfo(node->getMember(0), enumCollVar, attr);
buildRangeInfo(node->getMember(1), enumCollVar, attr);
}
/* TODO: or isn't implemented yet.
if (node->type == NODE_TYPE_OPERATOR_BINARY_OR) {
buildRangeInfo(node->getMember(0), enumCollVar, attr);
buildRangeInfo(node->getMember(1), enumCollVar, attr);
}
*/
attr = "";
enumCollVar = "";
return;
@ -787,13 +800,13 @@ public:
/// @brief removes the sortNode and its referenced Calculationnodes from the plan.
////////////////////////////////////////////////////////////////////////////////
void removeSortNodeFromPlan (ExecutionPlan *newPlan) {
newPlan->unlinkNode(newPlan->getNodeById(sortNodeID));
for (auto idToRemove = _sortNodeData.begin();
idToRemove != _sortNodeData.end();
++idToRemove) {
newPlan->unlinkNode(newPlan->getNodeById((*idToRemove)->calculationNodeID));
}
newPlan->unlinkNode(newPlan->getNodeById(sortNodeID));
}
};
@ -817,7 +830,7 @@ class sortToIndexNode : public WalkerWorker<ExecutionNode> {
_plan(plan),
_sortNode(Node),
_level(level) {
planModified = true;
planModified = false;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -178,15 +178,35 @@ void Parser::registerParseError (int errorCode,
// extract the query string part where the error happened
std::string const region(_query->extractRegion(line, column));
// create a neat pointer to the location of the error.
auto arrowpointer = (char*) malloc (sizeof(char) * (column + 10) );
size_t i;
for (i = 0; i < (size_t) column; i++) {
arrowpointer[i] = ' ';
}
if (i > 0) {
i --;
arrowpointer[i++] = '^';
}
arrowpointer[i++] = '^';
arrowpointer[i++] = '^';
arrowpointer[i++] = '\0';
// note: line numbers reported by bison/flex start at 1, columns start at 0
char buffer[512];
snprintf(buffer,
sizeof(buffer),
"%s near '%s' at position %d:%d",
"%s near '%s' at position %d:%d:\n%s\n%s\n",
data,
region.c_str(),
line,
column + 1);
column + 1,
_query->queryString(),
arrowpointer);
free(arrowpointer);
registerError(errorCode, buffer);
}

View File

@ -84,11 +84,13 @@ namespace triagens {
void assign (AstNode const* bound, bool include) {
_include = include;
_bound = Json(TRI_UNKNOWN_MEM_ZONE, bound->toJson(TRI_UNKNOWN_MEM_ZONE, true));
_undefined = false;
}
void assign (RangeInfoBound copy) {
_include = copy._include;
_bound = copy._bound;
_undefined = false;
}
Json toJson () const {
@ -154,7 +156,7 @@ namespace triagens {
RangeInfo () : _valid(false), _undefined(true) {}
RangeInfo (basics::Json const& json) :
_var(basics::JsonHelper::checkAndGetStringValue(json.json(), "var")),
_var(basics::JsonHelper::checkAndGetStringValue(json.json(), "variable")),
_attr(basics::JsonHelper::checkAndGetStringValue(json.json(), "attr")),
_valid(basics::JsonHelper::checkAndGetBooleanValue(json.json(), "valid")),
_undefined(false) {
@ -178,7 +180,7 @@ namespace triagens {
Json toJson () {
Json item(basics::Json::Array);
item("var", Json(_var))("attr", Json(_attr));
item("variable", Json(_var))("attr", Json(_attr));
if(!_low._undefined){
item("low", _low.toJson());
}
@ -254,7 +256,7 @@ namespace triagens {
for (auto x : _ranges) {
for (auto y: x.second){
Json item(Json::Array);
item("var", Json(x.first))
item("variable", Json(x.first))
("attribute name", Json(y.first))
("range info", y.second.toJson());
list(item);

View File

@ -425,10 +425,51 @@ function getCompactPlan (explainResult) {
return out;
}
function findExecutionNodes(plan, nodetype) {
var matches = [];
plan.plan.nodes.forEach(function(node) {
if (node.type === nodetype) {
matches.push(node);
}
else if (node.type === "SubqueryNode") {
var subPlan = {"plan" : node.subquery};
matches = matches.concat(findExecutionNodes(subPlan, nodetype));
}
});
return matches;
}
function findReferencedNodes(plan, testNode) {
var matches = [];
if (testNode.elements) {
testNode.elements.forEach(function(element) {
plan.plan.nodes.forEach(function(node) {
if (node.hasOwnProperty("outVariable") &&
node.outVariable.id ===
element.inVariable.id) {
matches.push(node);
}
});
});
}
else {
plan.plan.nodes.forEach(function(node) {
if (node.outVariable.id === testNode.inVariable.id) {
matches.push(node);
}
});
}
return matches;
}
// -----------------------------------------------------------------------------
// --SECTION-- module exports
// -----------------------------------------------------------------------------
exports.isEqual = isEqual;
exports.getParseResults = getParseResults;
exports.assertParseError = assertParseError;
exports.getQueryExplanation = getQueryExplanation;
@ -441,6 +482,8 @@ exports.assertQueryError = assertQueryError;
exports.assertQueryError2 = assertQueryError2;
exports.getLinearizedPlan = getLinearizedPlan;
exports.getCompactPlan = getCompactPlan;
exports.findExecutionNodes = findExecutionNodes;
exports.findReferencedNodes = findReferencedNodes;
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE

View File

@ -37,7 +37,10 @@ var assertQueryError = helper.assertQueryError2;
function optimizerRuleTestSuite () {
var ruleName = "move-calculations-up";
// various choices to control the optimizer:
var paramNone = { optimizer: { rules: [ "-all" ] } };
var paramMCU = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
var paramNoMCU = { optimizer: { rules: [ "+all", "-" + ruleName ] } };
return {
////////////////////////////////////////////////////////////////////////////////
@ -66,7 +69,7 @@ function optimizerRuleTestSuite () {
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, { optimizer: { rules: [ "-all" ] } });
var result = AQL_EXPLAIN(query, { }, paramNone);
assertEqual([ ], result.plan.rules);
});
},
@ -84,7 +87,7 @@ function optimizerRuleTestSuite () {
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
var result = AQL_EXPLAIN(query, { }, paramMCU);
assertEqual([ ], result.plan.rules, query);
});
},
@ -104,7 +107,7 @@ function optimizerRuleTestSuite () {
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
var result = AQL_EXPLAIN(query, { }, paramMCU);
assertEqual([ ruleName ], result.plan.rules);
});
},
@ -120,7 +123,7 @@ function optimizerRuleTestSuite () {
];
plans.forEach(function(plan) {
var result = AQL_EXPLAIN(plan[0], { }, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
var result = AQL_EXPLAIN(plan[0], { }, paramMCU);
assertEqual([ ruleName ], result.plan.rules, plan[0]);
assertEqual(plan[1], helper.getCompactPlan(result).map(function(node) { return node.type; }), plan[0]);
});
@ -139,11 +142,11 @@ function optimizerRuleTestSuite () {
];
queries.forEach(function(query) {
var planDisabled = AQL_EXPLAIN(query[0], { }, { optimizer: { rules: [ "+all", "-" + ruleName ] } });
var planEnabled = AQL_EXPLAIN(query[0], { }, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
var planDisabled = AQL_EXPLAIN(query[0], { }, paramNoMCU);
var planEnabled = AQL_EXPLAIN(query[0], { }, paramMCU);
var resultDisabled = AQL_EXECUTE(query[0], { }, { optimizer: { rules: [ "+all", "-" + ruleName ] } });
var resultEnabled = AQL_EXECUTE(query[0], { }, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
var resultDisabled = AQL_EXECUTE(query[0], { }, paramNoMCU);
var resultEnabled = AQL_EXECUTE(query[0], { }, paramMCU);
assertTrue(planDisabled.plan.rules.indexOf(ruleName) === -1, query[0]);
assertTrue(planEnabled.plan.rules.indexOf(ruleName) !== -1, query[0]);

View File

@ -0,0 +1,392 @@
/*jslint indent: 2, nomen: true, maxlen: 200, sloppy: true, vars: true, white: true, plusplus: true */
/*global require, exports, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXPLAIN, fail, loopmax */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 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 triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var errors = require("internal").errors;
var helper = require("org/arangodb/aql-helper");
var getQueryResults = helper.getQueryResults2;
var assertQueryError = helper.assertQueryError2;
var isEqual = helper.isEqual;
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function optimizerRuleTestSuite () {
var ruleName = "remove-unnecessary-calculations";
// various choices to control the optimizer:
var paramNone = { optimizer: { rules: [ "-all" ] } };
var paramMCU = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
var paramNoMCU = { optimizer: { rules: [ "+all", "-" + ruleName ] } };
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has no effect when explicitly disabled
////////////////////////////////////////////////////////////////////////////////
testRuleDisabled : function () {
var queries = [
"FOR a IN 1 RETURN a + 1",
"FOR a IN 1 RETURN a + 0",
"FOR a IN 1 RETURN a - 1",
"FOR a IN 1 RETURN a * 2",
"FOR a IN 4 RETURN a / 2",
"FOR a IN 4 RETURN a % 3",
"FOR a IN 4 RETURN a == 8",
"FOR a IN 4 RETURN 2",
"FOR a IN [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)",
"FOR a IN 17.33 RETURN FLOOR(a)",
"FOR a IN 17.33 RETURN CEIL(a)",
"FOR a IN 17.33 RETURN FLOOR(a + 3)",
"FOR a IN 17.33 RETURN CEIL(a + 3)",
"FOR a IN 17.33 RETURN FLOOR(a / 77)",
"FOR a IN 17.33 RETURN CEIL(a / 77)",
"FOR a IN -12 RETURN TO_BOOL(a)",
"FOR a IN \"-12\" RETURN TO_NUMBER(a)",
"FOR a IN \"-12\" RETURN IS_NUMBER(a)",
"FOR a IN \"-12\" RETURN TO_LIST(a)",
"FOR a IN { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)",
"FOR a IN -12 RETURN ABS(a)",
"FOR a IN -12 RETURN ABS(a + 17)",
"FOR a IN 17.33 RETURN ROUND(a)",
"FOR a IN 17.33 RETURN SQRT(a)",
"FOR a IN -17.33 RETURN SQRT(a)",
"FOR a IN CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1",
"FOR a IN 7 return a..12",
"FOR a IN [1, 7, 3, 12] RETURN AVERAGE(a)",
"FOR a IN [1, 7, 3, 12] RETURN MEDIAN(a)",
"FOR a IN [1, 7, 3, 12] RETURN MAX(a)",
"FOR a IN [1, 7, 3, 12] RETURN SUM(a)",
"FOR a IN [1, 7, 3, 12, null] RETURN NOT_NULL(a)",
"FOR a IN [1, 7, 3, 12, null] RETURN FIRST_LIST(a)",
"FOR a IN [null, \"not a doc!\"] RETURN FIRST_DOCUMENT(a)",
"FOR a IN [0.75, 0.8] RETURN VARIANCE_SAMPLE(a)",
"FOR a IN [0.75, 0.8] RETURN STDDEV_POPULATION(a)",
"FOR a IN 5 RETURN DATE_DAYOFWEEK(a)",
"FOR a IN 5 RETURN DATE_MONTH(a)",
"FOR a IN 5 RETURN DATE_DAY(a)",
"FOR a IN 5 RETURN DATE_HOUR(a)",
"FOR a IN 5 RETURN DATE_MINUTE(a)",
"FOR a IN 5 RETURN DATE_SECOND(a)",
"FOR a IN 5 RETURN DATE_MILLISECOND(a)",
"FOR a IN 5 RETURN DATE_TIMESTAMP(a)",
"FOR a IN 5 RETURN DATE_ISO8601(a)",
"FOR a IN 1975 RETURN DATE_YEAR(a)",
"LET a = SLEEP(2) RETURN 1"
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, paramNone);
assertEqual([ ], result.plan.rules);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has no effect
////////////////////////////////////////////////////////////////////////////////
testRuleNoEffect : function () {
var queries = [
"FOR a IN 1 RETURN a + 1",
"FOR a IN 1 RETURN a + 0",
"FOR a IN 1 RETURN a - 1",
"FOR a IN 1 RETURN a * 2",
"FOR a IN 4 RETURN a / 2",
"FOR a IN 4 RETURN a % 3",
"FOR a IN 4 RETURN a == 8",
"FOR a IN 4 RETURN 2",
"FOR a IN [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)",
"FOR a IN 17.33 RETURN FLOOR(a)",
"FOR a IN 17.33 RETURN CEIL(a)",
"FOR a IN 17.33 RETURN FLOOR(a + 3)",
"FOR a IN 17.33 RETURN CEIL(a + 3)",
"FOR a IN 17.33 RETURN FLOOR(a / 77)",
"FOR a IN 17.33 RETURN CEIL(a / 77)",
"FOR a IN -12 RETURN TO_BOOL(a)",
"FOR a IN \"-12\" RETURN TO_NUMBER(a)",
"FOR a IN \"-12\" RETURN IS_NUMBER(a)",
"FOR a IN \"-12\" RETURN TO_LIST(a)",
"FOR a IN { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)",
"FOR a IN -12 RETURN ABS(a)",
"FOR a IN -12 RETURN ABS(a + 17)",
"FOR a IN 17.33 RETURN ROUND(a)",
"FOR a IN 17.33 RETURN SQRT(a)",
"FOR a IN -17.33 RETURN SQRT(a)",
"FOR a IN CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1",
"FOR a IN 7 return a..12",
"FOR a IN [1, 7, 3, 12] RETURN AVERAGE(a)",
"FOR a IN [1, 7, 3, 12] RETURN MEDIAN(a)",
"FOR a IN [1, 7, 3, 12] RETURN MAX(a)",
"FOR a IN [1, 7, 3, 12] RETURN SUM(a)",
"FOR a IN [1, 7, 3, 12, null] RETURN NOT_NULL(a)",
"FOR a IN [1, 7, 3, 12, null] RETURN FIRST_LIST(a)",
"FOR a IN [null, \"not a doc!\"] RETURN FIRST_DOCUMENT(a)",
"FOR a IN [0.75, 0.8] RETURN VARIANCE_SAMPLE(a)",
"FOR a IN [0.75, 0.8] RETURN STDDEV_POPULATION(a)",
"FOR a IN 5 RETURN DATE_DAYOFWEEK(a)",
"FOR a IN 5 RETURN DATE_MONTH(a)",
"FOR a IN 5 RETURN DATE_DAY(a)",
"FOR a IN 5 RETURN DATE_HOUR(a)",
"FOR a IN 5 RETURN DATE_MINUTE(a)",
"FOR a IN 5 RETURN DATE_SECOND(a)",
"FOR a IN 5 RETURN DATE_MILLISECOND(a)",
"FOR a IN 5 RETURN DATE_TIMESTAMP(a)",
"FOR a IN 5 RETURN DATE_ISO8601(a)",
"FOR a IN 1975 RETURN DATE_YEAR(a)",
"LET a = SLEEP(2) RETURN 1"
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, paramMCU);
assertEqual([ ], result.plan.rules, query);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has an effect
////////////////////////////////////////////////////////////////////////////////
testRuleHasEffect : function () {
var queries = [
"LET a = 1 RETURN a + 1",
"LET a = 1 RETURN a + 0",
"LET a = 1 RETURN a - 1",
"LET a = 1 RETURN a * 2",
"LET a = 4 RETURN a / 2",
"LET a = 4 RETURN a % 3",
"LET a = 4 RETURN a == 8",
"LET a = 4 RETURN 2",
"LET a = [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)",
"LET a = 17.33 RETURN FLOOR(a)",
"LET a = 17.33 RETURN CEIL(a)",
"LET a = 17.33 RETURN FLOOR(a + 3)",
"LET a = 17.33 RETURN CEIL(a + 3)",
"LET a = 17.33 RETURN FLOOR(a / 77)",
"LET a = 17.33 RETURN CEIL(a / 77)",
"LET a = -12 RETURN TO_BOOL(a)",
"LET a = \"-12\" RETURN TO_NUMBER(a)",
"LET a = \"-12\" RETURN IS_NUMBER(a)",
"LET a = \"-12\" RETURN TO_LIST(a)",
"LET a = { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)",
"LET a = -12 RETURN ABS(a)",
"LET a = -12 RETURN ABS(a + 17)",
"LET a = 17.33 RETURN ROUND(a)",
"LET a = 17.33 RETURN SQRT(a)",
"LET a = -17.33 RETURN SQRT(a)",
"LET a = CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1",
"LET a = 7 return a..12",
"LET a = 1 LET b = 2 RETURN [ a, b ]",
"LET a = [1, 7, 3, 12] RETURN AVERAGE(a)",
"LET a = [1, 7, 3, 12] RETURN MEDIAN(a)",
"LET a = [1, 7, 3, 12] RETURN MAX(a)",
"LET a = [1, 7, 3, 12] RETURN SUM(a)",
"LET a = [1, 7, 3, 12, null] RETURN NOT_NULL(a)",
"LET a = [1, 7, 3, 12, null] RETURN FIRST_LIST(a)",
"LET a = [null, \"not a doc!\"] RETURN FIRST_DOCUMENT(a)",
"LET a = [0.75, 0.8] RETURN VARIANCE_SAMPLE(a)",
"LET a = [0.75, 0.8] RETURN STDDEV_POPULATION(a)",
"LET a = 5 RETURN DATE_DAYOFWEEK(a)",
"LET a = 5 RETURN DATE_MONTH(a)",
"LET a = 5 RETURN DATE_DAY(a)",
"LET a = 5 RETURN DATE_HOUR(a)",
"LET a = 5 RETURN DATE_MINUTE(a)",
"LET a = 5 RETURN DATE_SECOND(a)",
"LET a = 5 RETURN DATE_MILLISECOND(a)",
"LET a = 5 RETURN DATE_TIMESTAMP(a)",
"LET a = 5 RETURN DATE_ISO8601(a)",
"LET a = 1975 RETURN DATE_YEAR(a)",
"FOR i IN 1..10 LET a = 1 FILTER i == a RETURN i"
// "FOR i IN 1..10 LET a = i + 1 FILTER i != a RETURN i"
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, paramMCU);
//require("internal").print(result);
assertEqual([ ruleName ], result.plan.rules);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test generated plans
////////////////////////////////////////////////////////////////////////////////
testPlans : function () {
var plans = [
["LET a = 1 RETURN a + 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a + 0", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a - 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a * 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a / 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a % 3", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a == 8", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a + 3)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a + 3)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a / 77)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a / 77)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN TO_BOOL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN TO_NUMBER(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN IS_NUMBER(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN TO_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN ABS(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN ABS(a + 17)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN ROUND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN SQRT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -17.33 RETURN SQRT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 7 return a..12", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 LET b = 2 RETURN [ a, b ]", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN AVERAGE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN MEDIAN(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN MAX(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN SUM(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12, null] RETURN NOT_NULL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12, null] RETURN FIRST_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [null, \"not a doc!\"] RETURN FIRST_DOCUMENT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [0.75, 0.8] RETURN VARIANCE_SAMPLE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [0.75, 0.8] RETURN STDDEV_POPULATION(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_DAYOFWEEK(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MONTH(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_DAY(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_HOUR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MINUTE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_SECOND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MILLISECOND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_TIMESTAMP(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_ISO8601(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1975 RETURN DATE_YEAR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["FOR i IN 1..10 LET a = 1 FILTER i == a RETURN i", ["SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "FilterNode", "ReturnNode" ]]
];
plans.forEach(function(plan) {
var result = AQL_EXPLAIN(plan[0], { }, paramMCU);
assertEqual([ ruleName ], result.plan.rules, plan[0]);
//require("internal").print(helper.getCompactPlan(result).map(function(node) { return node.type; }));
assertEqual(plan[1], helper.getCompactPlan(result).map(function(node) { return node.type; }), plan[0]);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////
testResults : function () {
var queries = [
["LET a = 1 RETURN a + 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a + 0", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a - 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 RETURN a * 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a / 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a % 3", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN a == 8", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 4 RETURN 2", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a + 3)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a + 3)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN FLOOR(a / 77)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN CEIL(a / 77)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN TO_BOOL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN TO_NUMBER(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN IS_NUMBER(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = \"-12\" RETURN TO_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN ABS(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -12 RETURN ABS(a + 17)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN ROUND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 17.33 RETURN SQRT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = -17.33 RETURN SQRT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 7 return a..12", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1 LET b = 2 RETURN [ a, b ]", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN AVERAGE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN MEDIAN(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN MAX(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12] RETURN SUM(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12, null] RETURN NOT_NULL(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [1, 7, 3, 12, null] RETURN FIRST_LIST(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [null, \"not a doc!\"] RETURN FIRST_DOCUMENT(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [0.75, 0.8] RETURN VARIANCE_SAMPLE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = [0.75, 0.8] RETURN STDDEV_POPULATION(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_DAYOFWEEK(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MONTH(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_DAY(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_HOUR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MINUTE(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_SECOND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_MILLISECOND(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_TIMESTAMP(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 5 RETURN DATE_ISO8601(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["LET a = 1975 RETURN DATE_YEAR(a)", ["SingletonNode", "CalculationNode", "ReturnNode"]],
["FOR i IN 1..10 LET a = 1 FILTER i == a RETURN i", ["SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "FilterNode", "ReturnNode" ]]
];
queries.forEach(function(query) {
var resultDisabled = AQL_EXECUTE(query[0], { }, paramNoMCU).json;
var resultEnabled = AQL_EXECUTE(query[0], { }, paramMCU).json;
assertTrue(isEqual(resultDisabled, resultEnabled), query[0]);
});
}
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(optimizerRuleTestSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -0,0 +1,760 @@
/*jslint indent: 2, nomen: true, maxlen: 200, sloppy: true, vars: true, white: true, plusplus: true */
/*global require, exports, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXPLAIN, fail, loopmax */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 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 triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var internal = require("internal");
var jsunity = require("jsunity");
var errors = require("internal").errors;
var helper = require("org/arangodb/aql-helper");
var getQueryResults = helper.getQueryResults2;
var assertQueryError = helper.assertQueryError2;
var isEqual = helper.isEqual;
var findExecutionNodes = helper.findExecutionNodes;
var findReferencedNodes = helper.findReferencedNodes;
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function optimizerRuleTestSuite() {
var ruleName = "use-index-for-sort";
var secondRuleName = "use-index-range";
var thirdRuleName = "remove-redundant-sorts";
var colName = "UnitTestsAqlOptimizer" + ruleName.replace(/-/g, "_");
var colNameOther = colName + "_XX";
// various choices to control the optimizer:
var paramNone = { optimizer: { rules: [ "-all" ] } };
var paramIFS = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
var paramIR = { optimizer: { rules: [ "-all", "+" + secondRuleName ] } };
var paramRS = { optimizer: { rules: [ "-all", "+" + thirdRuleName ] } };
var paramBoth = { optimizer: { rules: [ "-all", "+" + ruleName, "+" + secondRuleName ] } };
var skiplist;
var skiplist2;
var sortArray = function (l, r) {
if (l[0] !== r[0]) {
return l[0] < r[0] ? -1 : 1;
}
if (l[1] !== r[1]) {
return l[1] < r[1] ? -1 : 1;
}
return 0;
};
var hasSortNode = function (plan) {
assertEqual(findExecutionNodes(plan, "SortNode").length, 1, "Has SortNode");
};
var hasNoSortNode = function (plan) {
assertEqual(findExecutionNodes(plan, "SortNode").length, 0, "Has NO SortNode");
};
var hasCalculationNodes = function (plan, countXPect) {
assertEqual(findExecutionNodes(plan, "CalculationNode").length,
countXPect,
"Has " + countXPect + "CalculationNode");
};
var hasNoCalculationNode = function (plan) {
assertEqual(findExecutionNodes(plan, "CalculationNode").length, 0, "Has NO CalculationNode");
};
var hasIndexRangeNode_WithRanges = function (plan, haveRanges) {
var rn = findExecutionNodes(plan, "IndexRangeNode");
assertEqual(rn.length, 1, "Has IndexRangeNode");
if (haveRanges) {
assertTrue(rn[0].ranges.length > 0, "Have IndexRangeNode with ranges");
}
else {
assertEqual(rn[0].ranges.length, 0, "Have IndexRangeNode with NO ranges");
}
};
var getRangeAttributes = function (plan) {
var rn = findExecutionNodes(plan, "IndexRangeNode");
assertEqual(rn.length, 1, "Has IndexRangeNode");
assertTrue(rn[0].ranges.length > 0, "Have IndexRangeNode with ranges");
return rn[0].ranges;
};
var getRangeAttribute = function (rangeAttributes, varcmp, attrcmp, getNth) {
var ret = {};
rangeAttributes.forEach(function compare(oneRA) {
if ( (oneRA.variable === varcmp) &&
(oneRA.attr === attrcmp)) {
getNth --;
if (getNth === 0) {
ret = oneRA;
}
}
});
return ret;
};
var isNodeType = function(node, type) {
assertEqual(node.type, type, "check whether this node is of type "+type);
};
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
// Datastructure:
// - double index on (a,b)/(f,g) for tests with these
// - single column index on d/j to test sort behaviour without sub-columns
// - non-indexed columns c/h to sort without indices.
// - non-skiplist indexed columns e/j to check whether its not selecting them.
// - join column 'joinme' to intersect both tables.
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
var loopto;
if (typeof loopmax === 'undefined') {
loopto = 10;
}
else {
loopto = loopmax;
}
/// require("internal").print("loopto: " + loopto + "\n");
internal.db._drop(colName);
skiplist = internal.db._create(colName);
var i, j;
for (j = 1; j <= loopto; ++j) {
for (i = 1; i <= loopto; ++i) {
skiplist.save({ "a" : i, "b": j , "c": j, "d": i, "e": i, "joinme" : "aoeu " + j});
}
skiplist.save( { "a" : i, "c": j, "d": i, "e": i, "joinme" : "aoeu " + j});
skiplist.save( { "c": j, "joinme" : "aoeu " + j});
}
skiplist.ensureSkiplist("a", "b");
skiplist.ensureSkiplist("d");
skiplist.ensureIndex({ type: "hash", fields: [ "c" ], unique: false });
skiplist2 = internal.db._create(colNameOther);
for (j = 1; j <= loopto; ++j) {
for (i = 1; i <= loopto; ++i) {
skiplist2.save({ "f" : i, "g": j , "h": j, "i": i, "j": i, "joinme" : "aoeu " + j});
}
skiplist2.save( { "f" : i, "g": j, "i": i, "j": i, "joinme" : "aoeu " + j});
skiplist2.save( { "h": j, "joinme" : "aoeu " + j});
}
skiplist2.ensureSkiplist("f", "g");
skiplist2.ensureSkiplist("i");
skiplist2.ensureIndex({ type: "hash", fields: [ "h" ], unique: false });
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
internal.db._drop(colName);
internal.db._drop(colNameOther);
skiplist = null;
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has no effect when explicitly disabled
////////////////////////////////////////////////////////////////////////////////
testRuleDisabled : function () {
/*
var queries = [
"LET a = 1 FOR i IN 1..10 FILTER a == 1 RETURN i",
"FOR i IN 1..10 LET a = 25 RETURN i",
"FOR i IN 1..10 LET a = 1 LET b = 2 LET c = 3 FILTER i > 3 LET d = 1 RETURN i"
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { }, { optimizer: { rules: [ "-all" ] } });
assertEqual([ ], result.plan.rules);
});
*/
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has no effect
////////////////////////////////////////////////////////////////////////////////
testRuleNoEffect : function () {
/*
var queries = [
"FOR v IN " + colName + " SORT v.c RETURN [v.a, v.b]",
// todo: we use an index anyways right now. "FOR v IN " + colName + " SORT v.a DESC RETURN [v.a, v.b]",// currently only ASC supported.
"FOR v IN " + colName + " SORT v.b, v.a RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.a + 1 RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT CONCAT(TO_STRING(v.a), \"lol\") RETURN [v.a, v.b]",
"FOR v IN " + colName + " FILTER v.a > 2 LIMIT 3 SORT v.a RETURN [v.a, v.b] ", // TODO: limit blocks sort atm.
"FOR v IN " + colName + " FOR w IN " + colNameOther + " SORT v.a RETURN [v.a, v.b]"
];
queries.forEach(function(query) {
// require("internal").print(query);
var result = AQL_EXPLAIN(query, { }, paramIFS);
// require("internal").print(result);
assertEqual([], result.plan.rules, query);
});
*/
},
/*
////////////////////////////////////////////////////////////////////////////////
/// @brief test that rule has an effect
////////////////////////////////////////////////////////////////////////////////
testRuleHasEffect : function () {
var queries = [
"FOR v IN " + colName + " SORT v.d DESC RETURN [v.d]",// currently only ASC supported, but we use the index range anyways. todo: this may change.
"FOR v IN " + colName + " SORT v.d FILTER v.a > 2 LIMIT 3 RETURN [v.d] ",
"FOR v IN " + colName + " FOR w IN 1..10 SORT v.d RETURN [v.d]",
"FOR v IN " + colName + " LET x = (FOR w IN " + colNameOther + " RETURN w.f ) SORT v.a RETURN [v.a]"
];
var QResults = [];
var i = 0;
queries.forEach(function(query) {
//require("internal").print(query);
var result = AQL_EXPLAIN(query, { }, paramIFS);
assertEqual([ ruleName ], result.plan.rules, query);
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json;
QResults[1] = AQL_EXECUTE(query, { }, paramIFS ).json;
// require("internal").print(result);
assertTrue(isEqual(QResults[0], QResults[1]), "Result " + i + " is Equal?");
i++;
});
},
testRuleHasEffectButSortsStill : function () {
var queries = [
"FOR v IN " + colName + " FILTER v.a == 1 SORT v.a, v.c RETURN [v.a, v.b, v.c]",
"FOR v IN " + colName + " LET x = (FOR w IN " + colNameOther + " SORT w.j, w.h RETURN w.f ) SORT v.a RETURN [v.a]"
];
var QResults = [];
var i = 0;
queries.forEach(function(query) {
// require("internal").print(query);
var result = AQL_EXPLAIN(query, { }, paramIFS);
// require("internal").print(result);
assertEqual([ ruleName ], result.plan.rules);
hasIndexRangeNode_WithRanges(result, false);
hasSortNode(result);
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json;
QResults[1] = AQL_EXECUTE(query, { }, paramIFS ).json;
assertTrue(isEqual(QResults[0], QResults[1]), "Result " + i + " is Equal?");
i++;
});
},
*/
////////////////////////////////////////////////////////////////////////////////
/// @brief test generated plans
////////////////////////////////////////////////////////////////////////////////
/*
testPlans : function () {
var plans = [
[ "FOR i IN 1..10 LET a = 1 RETURN i", [ "SingletonNode", "CalculationNode", "CalculationNode", "EnumerateListNode", "ReturnNode" ] ],
[ "FOR i IN 1..10 FILTER i == 1 LET a = 1 RETURN i", [ "SingletonNode", "CalculationNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "FilterNode", "ReturnNode" ] ]
];
plans.forEach(function(plan) {
var result = AQL_EXPLAIN(plan[0], { }, paramIFS);
assertEqual([ ruleName ], result.plan.rules, plan[0]);
assertEqual(plan[1], helper.getCompactPlan(result).map(function(node) { return node.type; }), plan[0]);
});
},
*/
////////////////////////////////////////////////////////////////////////////////
/// @brief test results
////////////////////////////////////////////////////////////////////////////////
/*
testResults : function () {
var queries = [
[ "FOR i IN 1..10 LET a = 1 RETURN i", [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ],
[ "FOR i IN 1..10 LET a = 1 RETURN a", [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ],
[ "FOR i IN 1..10 FILTER i == 1 LET a = 1 RETURN i", [ 1 ] ],
[ "FOR i IN 1..10 FILTER i == 1 LET a = 1 RETURN a", [ 1 ] ],
];
queries.forEach(function(query) {
var planDisabled = AQL_EXPLAIN(query[0], { }, { optimizer: { rules: [ "+all", "-" + ruleName ] } });
var planEnabled = AQL_EXPLAIN(query[0], { }, paramIFS);
var resultDisabled = AQL_EXECUTE(query[0], { }, { optimizer: { rules: [ "+all", "-" + ruleName ] } });
var resultEnabled = AQL_EXECUTE(query[0], { }, paramIFS);
assertTrue(planDisabled.plan.rules.indexOf(ruleName) === -1, query[0]);
assertTrue(planEnabled.plan.rules.indexOf(ruleName) !== -1, query[0]);
assertEqual(resultDisabled.json, query[1], query[0]);
assertEqual(resultEnabled.json, query[1], query[0]);
});
},
*/
/*
// -----------------------------------------------------------------------------
// --SECTION-- sortToIndexRange
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief this sort is replaceable by an index.
////////////////////////////////////////////////////////////////////////////////
testSortIndexable: function () {
var query = "FOR v IN " + colName + " SORT v.a RETURN [v.a, v.b]";
var XPresult;
var QResults=[];
// we have to re-sort here, because of the index has one more sort criteria.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-for-sort alone.
XPresult = AQL_EXPLAIN(query, { }, paramNone);
XPresult = AQL_EXPLAIN(query, { }, paramIFS);
QResults[1] = AQL_EXECUTE(query, { }, paramIFS).json;
// our rule should have been applied.
assertEqual([ ruleName ], XPresult.plan.rules);
// The sortnode and its calculation node should have been removed.
hasNoSortNode(XPresult);
hasCalculationNodes(XPresult, 1);
// The IndexRangeNode created by this rule is simple; it shouldn't have ranges.
hasIndexRangeNode_WithRanges(XPresult, false);
assertTrue(isEqual(QResults[0], QResults[1]), "Query results are equal?");
},
testSortMoreThanIndexed: function () {
var query = "FOR v IN " + colName + " FILTER v.a == 1 SORT v.a, v.c RETURN [v.a, v.b, v.c]";
// no index can be used for v.c -> sort has to remain in place!
var XPresult;
var QResults=[];
var i;
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-for-sort alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIFS).json;
XPresult = AQL_EXPLAIN(query, { }, paramIFS);
// our rule should be there.
assertEqual([ ruleName ], XPresult.plan.rules);
// The sortnode and its calculation node should have been removed.
// require("internal").print(XPresult);
hasSortNode(XPresult);
hasCalculationNodes(XPresult, 4);
// The IndexRangeNode created by this rule is simple; it shouldn't have ranges.
hasIndexRangeNode_WithRanges(XPresult, false);
// -> combined use-index-for-sort and use-index-range
// use-index-range superseedes use-index-for-sort
QResults[2] = AQL_EXECUTE(query, { }, paramBoth).json;
XPresult = AQL_EXPLAIN(query, { }, paramBoth);
assertEqual([ secondRuleName ], XPresult.plan.rules.sort());
// The sortnode and its calculation node should not have been removed.
hasSortNode(XPresult);
hasCalculationNodes(XPresult, 4);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
hasIndexRangeNode_WithRanges(XPresult, true);
// -> use-index-range alone.
QResults[3] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasSortNode(XPresult);
hasCalculationNodes(XPresult,4);
// we should be able to find exactly one sortnode property - its a Calculation node.
var sortProperty = findReferencedNodes(XPresult, findExecutionNodes(XPresult, "SortNode")[0]);
assertEqual(sortProperty.length, 2);
assertEqual(sortProperty[0].type, "CalculationNode");
assertEqual(sortProperty[1].type, "CalculationNode");
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
hasIndexRangeNode_WithRanges(XPresult, true);
for (i = 1; i < 4; i++) {
assertTrue(isEqual(QResults[0], QResults[i]), "Result " + i + " is Equal?");
}
},
testRangeSuperseedsSort: function () {
var query = "FOR v IN " + colName + " FILTER v.a == 1 SORT v.a RETURN [v.a, v.b, v.c]";
var XPresult;
var QResults=[];
var i;
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-for-sort alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIFS).json;
XPresult = AQL_EXPLAIN(query, { }, paramIFS);
// our rule should be there.
assertEqual([ ruleName ], XPresult.plan.rules);
// The sortnode and its calculation node should have been removed.
hasNoSortNode(XPresult);
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule is simple; it shouldn't have ranges.
hasIndexRangeNode_WithRanges(XPresult, false);
// -> combined use-index-for-sort and use-index-range
QResults[2] = AQL_EXECUTE(query, { }, paramBoth).json;
XPresult = AQL_EXPLAIN(query, { }, paramBoth);
assertEqual([ secondRuleName, ruleName ].sort(), XPresult.plan.rules.sort());
// The sortnode and its calculation node should have been removed.
hasNoSortNode(XPresult);
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
hasIndexRangeNode_WithRanges(XPresult, true);
// -> use-index-range alone.
QResults[3] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasSortNode(XPresult);
hasCalculationNodes(XPresult, 3);
// we should be able to find exactly one sortnode property - its a Calculation node.
var sortProperty = findReferencedNodes(XPresult, findExecutionNodes(XPresult, "SortNode")[0]);
assertEqual(sortProperty.length, 1);
isNodeType(sortProperty[0], "CalculationNode");
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
hasIndexRangeNode_WithRanges(XPresult, true);
for (i = 1; i < 4; i++) {
assertTrue(isEqual(QResults[0], QResults[i]), "Result " + i + " is Equal?");
}
},
*/
// -----------------------------------------------------------------------------
// --SECTION-- toIndexRange
// -----------------------------------------------------------------------------
/*
testRangeEquals: function () {
var query = "FOR v IN " + colName + " FILTER v.a == 1 RETURN [v.a, v.b, v.c]";
var XPresult;
var QResults=[];
var i;
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json;
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
//require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
//require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.high.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, first.high.bound.value);
for (i = 1; i < 2; i++) {
assertTrue(isEqual(QResults[0].sort(sortArray), QResults[i]), "Result " + i + " is Equal?");
}
},
testRangeLessThen: function () {
var query = "FOR v IN " + colName + " FILTER v.a < 5 RETURN [v.a, v.b]";
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
var first = getRangeAttribute(RAs, "v", "a", 1);
assertEqual(first.high.bound.vType, "int", "Type is int");
assertEqual(first.high.bound.value, 5, "proper value was set");
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
},
*/
testRangeGreaterThen: function () {
/*
TODO: can skiplist do a range?
var query = "FOR v IN " + colName + " FILTER v.a < 5 RETURN [v.a, v.b, v.c]";
var query = "FOR v IN " + colName + " FILTER v.a > 1 && v.a < 5 RETURN [v.a, v.b, v.c]";
var query = "FOR v IN " + colName + " FILTER v.c > 1 && v.c < 5 RETURN [v.a, v.b, v.c]";
*/
var query = "FOR v IN " + colName + " FILTER v.a > 5 RETURN [v.a, v.b]";
/*
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
// require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
// require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, 5, "proper value was set");
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
*/
},
testRangeBandpass: function () {
var query = "FOR v IN " + colName + " FILTER v.a > 4 && v.a < 10 RETURN [v.a, v.b]";
/*
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
// require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
// require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, 4, "proper value was set");
assertEqual(first.high.bound.vType, "int", "Type is int");
assertEqual(first.high.bound.value, 10, "proper value was set");
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
*/
},
testRangeBandpassInvalid: function () {
/* TODO: this doesn't do anything. should it simply flush that range since its empty? or even raise?
var query = "FOR v IN " + colName + " FILTER v.a > 7 && v.a < 4 RETURN [v.a, v.b]";
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
require("internal").print(XPresult);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
// require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
require("internal").print(first);
// require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, 4, "proper value was set");
assertEqual(first.high.bound.vType, "int", "Type is int");
assertEqual(first.high.bound.value, 10, "proper value was set");
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
*/
},
testRangeBandstop: function () {
/* TODO: OR isn't implemented
var query = "FOR v IN " + colName + " FILTER v.a < 5 || v.a > 10 RETURN [v.a, v.b]";
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, 5, "proper value was set");
require("internal").print(QResults[0]);
require("internal").print(QResults[1]);
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
*/
},
testMultiRangeBandpass: function () {
/* TODO: OR isn't implemented
var query = "FOR v IN " + colName + " FILTER ((v.a > 3 && v.a < 5) || (v.a > 4 && v.a < 7)) RETURN [v.a, v.b]";
var XPresult;
var QResults=[];
// the index we will compare to sorts by a & b, so we need to re-sort the result here to accomplish similarity.
QResults[0] = AQL_EXECUTE(query, { }, paramNone).json.sort(sortArray);
// -> use-index-range alone.
QResults[1] = AQL_EXECUTE(query, { }, paramIR).json;
XPresult = AQL_EXPLAIN(query, { }, paramIR);
assertEqual([ secondRuleName ], XPresult.plan.rules);
// the sortnode and its calculation node should be there.
hasCalculationNodes(XPresult, 2);
// The IndexRangeNode created by this rule should be more clever, it knows the ranges.
var RAs = getRangeAttributes(XPresult);
// require("internal").print(RAs);
var first = getRangeAttribute(RAs, "v", "a", 1);
// require("internal").print(first);
assertEqual(first.low.bound.vType, "int", "Type is int");
assertEqual(first.low.bound.value, 4, "proper value was set");
assertEqual(first.high.bound.vType, "int", "Type is int");
assertEqual(first.high.bound.value, 10, "proper value was set");
assertTrue(isEqual(QResults[0], QResults[1]), "Results are Equal?");
*/
},
// -----------------------------------------------------------------------------
// --SECTION-- toIndexRange
// -----------------------------------------------------------------------------
testDSRuleHasEffect : function () {
var queries = [
"FOR v IN " + colName + " SORT v.c SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c SORT v.c , v.d RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c, v.d SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c SORT v.c, v.d SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c, v.d SORT v.c SORT v.c, v.d SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c FILTER v.c > 3 SORT v.c RETURN [v.a, v.b]"
/*
// todo: we use an index anyways right now. "FOR v IN " + colName + " SORT v.a DESC RETURN [v.a, v.b]",// currently only ASC supported.
"FOR v IN " + colName + " SORT v.b, v.a RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.c RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT v.a + 1 RETURN [v.a, v.b]",
"FOR v IN " + colName + " SORT CONCAT(TO_STRING(v.a), \"lol\") RETURN [v.a, v.b]",
"FOR v IN " + colName + " FILTER v.a > 2 LIMIT 3 SORT v.a RETURN [v.a, v.b] ", // TODO: limit blocks sort atm.
"FOR v IN " + colName + " FOR w IN " + colNameOther + " SORT v.a RETURN [v.a, v.b]"
*/
];
queries.forEach(function(query) {
// require("internal").print(query);
var result = AQL_EXPLAIN(query, { }, paramRS);
// require("internal").print(result);
assertEqual([thirdRuleName], result.plan.rules, query);
});
}
//*/
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(optimizerRuleTestSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -186,6 +186,10 @@ bool ReadlineShell::close() {
bool res = writeHistory();
clear_history();
HIST_ENTRY** hist = history_list();
free(hist);
#ifndef __APPLE__
// reset state of the terminal to what it was before readline()
rl_cleanup_after_signal();
@ -225,7 +229,8 @@ void ReadlineShell::addHistory(char const* str) {
if (current_history()) {
do {
if (strcmp(current_history()->line, str) == 0) {
remove_history(where_history());
HIST_ENTRY* e = remove_history(where_history());
free_history_entry(e);
break;
}
}