mirror of https://gitee.com/bigwinds/arangodb
add cacheability info for explain
This commit is contained in:
parent
9eb51e9a20
commit
c20cf66e93
|
@ -1007,7 +1007,11 @@ QueryResult Query::explain () {
|
|||
it->planRegisters();
|
||||
out.add(it->toJson(parser.ast(), TRI_UNKNOWN_MEM_ZONE, verbosePlans()));
|
||||
}
|
||||
|
||||
result.json = out.steal();
|
||||
|
||||
// cacheability not available here
|
||||
result.cached = false;
|
||||
}
|
||||
else {
|
||||
// Now plan and all derived plans belong to the optimizer
|
||||
|
@ -1017,6 +1021,10 @@ QueryResult Query::explain () {
|
|||
bestPlan->findVarUsage();
|
||||
bestPlan->planRegisters();
|
||||
result.json = bestPlan->toJson(parser.ast(), TRI_UNKNOWN_MEM_ZONE, verbosePlans()).steal();
|
||||
|
||||
// cacheability
|
||||
result.cached = (_queryString != nullptr && _queryLength > 0 &&
|
||||
! _isModificationQuery && _warnings.empty() && _ast->root()->isCacheable());
|
||||
}
|
||||
|
||||
_trx->commit();
|
||||
|
|
|
@ -1235,7 +1235,9 @@ static void JS_ExplainAql (const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||
}
|
||||
else {
|
||||
result->Set(TRI_V8_ASCII_STRING("plan"), TRI_ObjectJson(isolate, queryResult.json));
|
||||
result->Set(TRI_V8_ASCII_STRING("cacheable"), v8::Boolean::New(isolate, queryResult.cached));
|
||||
}
|
||||
|
||||
if (queryResult.clusterplan != nullptr) {
|
||||
result->Set(TRI_V8_ASCII_STRING("clusterplans"), TRI_ObjectJson(isolate, queryResult.clusterplan));
|
||||
}
|
||||
|
|
|
@ -93,6 +93,10 @@ var ERRORS = require("internal").errors;
|
|||
/// The result will also contain an attribute *warnings*, which is an array of
|
||||
/// warnings that occurred during optimization or execution plan creation. Additionally,
|
||||
/// a *stats* attribute is contained in the result with some optimizer statistics.
|
||||
/// If *allPlans* is set to *false*, the result will contain an attribute *cacheable*
|
||||
/// that states whether the query results can be cached on the server if the query
|
||||
/// result cache were used. The *cacheable* attribute is not present when *allPlans*
|
||||
/// is set to *true*.
|
||||
///
|
||||
/// Each plan in the result is a JSON object with the following attributes:
|
||||
/// - *nodes*: the array of execution nodes of the plan. The array of available node types
|
||||
|
@ -314,7 +318,8 @@ function post_api_explain (req, res) {
|
|||
result = {
|
||||
plan: result.plan,
|
||||
warnings: result.warnings,
|
||||
stats: result.stats
|
||||
stats: result.stats,
|
||||
cacheable: result.cacheable
|
||||
};
|
||||
}
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result);
|
||||
|
|
|
@ -148,7 +148,8 @@ ArangoStatement.prototype.explain = function (options) {
|
|||
return {
|
||||
plan: requestResult.plan,
|
||||
warnings: requestResult.warnings,
|
||||
stats: requestResult.stats
|
||||
stats: requestResult.stats,
|
||||
cacheable: requestResult.cacheable
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -126,7 +126,16 @@ function wrap (str, width) {
|
|||
/* print query string */
|
||||
function printQuery (query) {
|
||||
'use strict';
|
||||
stringBuilder.appendLine(section("Query string:"));
|
||||
// restrict max length of printed query to avoid endless printing for
|
||||
// very long query strings
|
||||
var maxLength = 4096;
|
||||
if (query.length > maxLength) {
|
||||
stringBuilder.appendLine(section("Query string (truncated):"));
|
||||
query = query.substr(0, maxLength / 2) + " ... " + query.substr(query.length - maxLength / 2);
|
||||
}
|
||||
else {
|
||||
stringBuilder.appendLine(section("Query string:"));
|
||||
}
|
||||
stringBuilder.appendLine(" " + value(wrap(query, 100).replace(/\n+/g, "\n ", query)));
|
||||
stringBuilder.appendLine();
|
||||
}
|
||||
|
@ -261,8 +270,7 @@ function printIndexes (indexes) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* print indexes used */
|
||||
/* print traversal info */
|
||||
function printTraversalDetails (traversals) {
|
||||
'use strict';
|
||||
if (traversals.length === 0) {
|
||||
|
@ -408,7 +416,8 @@ function processQuery (query, explain) {
|
|||
indexes = [ ],
|
||||
traversalDetails = [],
|
||||
modificationFlags,
|
||||
isConst = true;
|
||||
isConst = true,
|
||||
currentNode = null;
|
||||
|
||||
var variableName = function (node) {
|
||||
try {
|
||||
|
@ -427,8 +436,26 @@ function processQuery (query, explain) {
|
|||
return variable(node.name);
|
||||
};
|
||||
|
||||
var addHint = function () { };
|
||||
// uncomment this to show "style" hints
|
||||
// var addHint = function (dst, currentNode, msg) {
|
||||
// dst.push({ code: "Hint", message: "Node #" + currentNode + ": " + msg });
|
||||
// };
|
||||
|
||||
var buildExpression = function (node) {
|
||||
isConst = isConst && ([ "value", "object", "object element", "array" ].indexOf(node.type) !== -1);
|
||||
|
||||
if (node.type !== "attribute access" &&
|
||||
node.hasOwnProperty("subNodes")) {
|
||||
for (var i = 0; i < node.subNodes.length; ++i) {
|
||||
if (node.subNodes[i].type === "reference" &&
|
||||
collectionVariables.hasOwnProperty(node.subNodes[i].id)) {
|
||||
addHint(explain.warnings, currentNode, "reference to collection document variable '" +
|
||||
node.subNodes[i].name + "' used in potentially non-working way");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "reference":
|
||||
|
@ -453,6 +480,7 @@ function processQuery (query, explain) {
|
|||
}
|
||||
return variableName(node);
|
||||
case "collection":
|
||||
addHint(explain.warnings, currentNode, "using all documents from collection '" + node.name + "' in expression");
|
||||
return collection(node.name) + " " + annotation("/* all collection documents */");
|
||||
case "value":
|
||||
return value(JSON.stringify(node.value));
|
||||
|
@ -487,6 +515,21 @@ function processQuery (query, explain) {
|
|||
case "array limit":
|
||||
return buildExpression(node.subNodes[0]) + ", " + buildExpression(node.subNodes[1]);
|
||||
case "attribute access":
|
||||
if (node.subNodes[0].type === "reference" &&
|
||||
collectionVariables.hasOwnProperty(node.subNodes[0].id)) {
|
||||
// top-level attribute access
|
||||
var collectionName = collectionVariables[node.subNodes[0].id],
|
||||
collectionObject = db._collection(collectionName);
|
||||
if (collectionObject !== null) {
|
||||
var isEdgeCollection = (collectionObject.type() === 3),
|
||||
isSystem = (node.name[0] === '_');
|
||||
|
||||
if ((isSystem && [ "_key", "_id", "_rev"].concat(isEdgeCollection ? [ "_from", "_to" ] : [ ]).indexOf(node.name) === -1) ||
|
||||
(! isSystem && isEdgeCollection && [ "from", "to" ].indexOf(node.name) !== -1)) {
|
||||
addHint(explain.warnings, currentNode, "reference to potentially non-existing attribute '" + node.name + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return buildExpression(node.subNodes[0]) + "." + attribute(node.name);
|
||||
case "indexed access":
|
||||
return buildExpression(node.subNodes[0]) + "[" + buildExpression(node.subNodes[1]) + "]";
|
||||
|
@ -810,6 +853,7 @@ function processQuery (query, explain) {
|
|||
|
||||
var preHandle = function (node) {
|
||||
usedVariables = { };
|
||||
currentNode = node.id;
|
||||
isConst = true;
|
||||
if (node.type === "SubqueryNode") {
|
||||
subqueries.push(level);
|
||||
|
|
|
@ -147,7 +147,8 @@ ArangoStatement.prototype.explain = function (options) {
|
|||
return {
|
||||
plan: requestResult.plan,
|
||||
warnings: requestResult.warnings,
|
||||
stats: requestResult.stats
|
||||
stats: requestResult.stats,
|
||||
cacheable: requestResult.cacheable
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -125,7 +125,16 @@ function wrap (str, width) {
|
|||
/* print query string */
|
||||
function printQuery (query) {
|
||||
'use strict';
|
||||
stringBuilder.appendLine(section("Query string:"));
|
||||
// restrict max length of printed query to avoid endless printing for
|
||||
// very long query strings
|
||||
var maxLength = 4096;
|
||||
if (query.length > maxLength) {
|
||||
stringBuilder.appendLine(section("Query string (truncated):"));
|
||||
query = query.substr(0, maxLength / 2) + " ... " + query.substr(query.length - maxLength / 2);
|
||||
}
|
||||
else {
|
||||
stringBuilder.appendLine(section("Query string:"));
|
||||
}
|
||||
stringBuilder.appendLine(" " + value(wrap(query, 100).replace(/\n+/g, "\n ", query)));
|
||||
stringBuilder.appendLine();
|
||||
}
|
||||
|
@ -260,8 +269,7 @@ function printIndexes (indexes) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* print indexes used */
|
||||
/* print traversal info */
|
||||
function printTraversalDetails (traversals) {
|
||||
'use strict';
|
||||
if (traversals.length === 0) {
|
||||
|
@ -407,7 +415,8 @@ function processQuery (query, explain) {
|
|||
indexes = [ ],
|
||||
traversalDetails = [],
|
||||
modificationFlags,
|
||||
isConst = true;
|
||||
isConst = true,
|
||||
currentNode = null;
|
||||
|
||||
var variableName = function (node) {
|
||||
try {
|
||||
|
@ -426,8 +435,26 @@ function processQuery (query, explain) {
|
|||
return variable(node.name);
|
||||
};
|
||||
|
||||
var addHint = function () { };
|
||||
// uncomment this to show "style" hints
|
||||
// var addHint = function (dst, currentNode, msg) {
|
||||
// dst.push({ code: "Hint", message: "Node #" + currentNode + ": " + msg });
|
||||
// };
|
||||
|
||||
var buildExpression = function (node) {
|
||||
isConst = isConst && ([ "value", "object", "object element", "array" ].indexOf(node.type) !== -1);
|
||||
|
||||
if (node.type !== "attribute access" &&
|
||||
node.hasOwnProperty("subNodes")) {
|
||||
for (var i = 0; i < node.subNodes.length; ++i) {
|
||||
if (node.subNodes[i].type === "reference" &&
|
||||
collectionVariables.hasOwnProperty(node.subNodes[i].id)) {
|
||||
addHint(explain.warnings, currentNode, "reference to collection document variable '" +
|
||||
node.subNodes[i].name + "' used in potentially non-working way");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "reference":
|
||||
|
@ -452,6 +479,7 @@ function processQuery (query, explain) {
|
|||
}
|
||||
return variableName(node);
|
||||
case "collection":
|
||||
addHint(explain.warnings, currentNode, "using all documents from collection '" + node.name + "' in expression");
|
||||
return collection(node.name) + " " + annotation("/* all collection documents */");
|
||||
case "value":
|
||||
return value(JSON.stringify(node.value));
|
||||
|
@ -486,6 +514,21 @@ function processQuery (query, explain) {
|
|||
case "array limit":
|
||||
return buildExpression(node.subNodes[0]) + ", " + buildExpression(node.subNodes[1]);
|
||||
case "attribute access":
|
||||
if (node.subNodes[0].type === "reference" &&
|
||||
collectionVariables.hasOwnProperty(node.subNodes[0].id)) {
|
||||
// top-level attribute access
|
||||
var collectionName = collectionVariables[node.subNodes[0].id],
|
||||
collectionObject = db._collection(collectionName);
|
||||
if (collectionObject !== null) {
|
||||
var isEdgeCollection = (collectionObject.type() === 3),
|
||||
isSystem = (node.name[0] === '_');
|
||||
|
||||
if ((isSystem && [ "_key", "_id", "_rev"].concat(isEdgeCollection ? [ "_from", "_to" ] : [ ]).indexOf(node.name) === -1) ||
|
||||
(! isSystem && isEdgeCollection && [ "from", "to" ].indexOf(node.name) !== -1)) {
|
||||
addHint(explain.warnings, currentNode, "reference to potentially non-existing attribute '" + node.name + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
return buildExpression(node.subNodes[0]) + "." + attribute(node.name);
|
||||
case "indexed access":
|
||||
return buildExpression(node.subNodes[0]) + "[" + buildExpression(node.subNodes[1]) + "]";
|
||||
|
@ -809,6 +852,7 @@ function processQuery (query, explain) {
|
|||
|
||||
var preHandle = function (node) {
|
||||
usedVariables = { };
|
||||
currentNode = node.id;
|
||||
isConst = true;
|
||||
if (node.type === "SubqueryNode") {
|
||||
subqueries.push(level);
|
||||
|
|
|
@ -252,6 +252,8 @@ function StatementSuite () {
|
|||
assertTrue(plan.hasOwnProperty("collections"));
|
||||
assertEqual([ ], plan.collections);
|
||||
assertTrue(plan.hasOwnProperty("variables"));
|
||||
assertTrue(result.hasOwnProperty("cacheable"));
|
||||
assertTrue(result.cacheable);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -275,6 +277,7 @@ function StatementSuite () {
|
|||
assertTrue(plan.hasOwnProperty("collections"));
|
||||
assertEqual([ ], plan.collections);
|
||||
assertTrue(plan.hasOwnProperty("variables"));
|
||||
assertFalse(result.hasOwnProperty("cacheable"));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -298,6 +301,7 @@ function StatementSuite () {
|
|||
assertTrue(plan.hasOwnProperty("collections"));
|
||||
assertEqual([ ], plan.collections);
|
||||
assertTrue(plan.hasOwnProperty("variables"));
|
||||
assertFalse(result.hasOwnProperty("cacheable"));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -370,6 +374,8 @@ function StatementSuite () {
|
|||
assertTrue(plan.hasOwnProperty("collections"));
|
||||
assertEqual([ ], plan.collections);
|
||||
assertTrue(plan.hasOwnProperty("variables"));
|
||||
assertTrue(result.hasOwnProperty("cacheable"));
|
||||
assertTrue(result.cacheable);
|
||||
},
|
||||
|
||||
|
||||
|
@ -393,6 +399,24 @@ function StatementSuite () {
|
|||
assertTrue(plan.hasOwnProperty("collections"));
|
||||
assertEqual([ ], plan.collections);
|
||||
assertTrue(plan.hasOwnProperty("variables"));
|
||||
assertTrue(result.hasOwnProperty("cacheable"));
|
||||
assertFalse(result.cacheable);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test non cacheable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testExplainNoncacheable : function () {
|
||||
var st = db._createStatement({ query : "RETURN RAND()" });
|
||||
var result = st.explain();
|
||||
|
||||
assertEqual(0, result.warnings.length);
|
||||
assertTrue(result.hasOwnProperty("plan"));
|
||||
assertFalse(result.hasOwnProperty("plans"));
|
||||
|
||||
assertTrue(result.hasOwnProperty("cacheable"));
|
||||
assertFalse(result.cacheable);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue