1
0
Fork 0
arangodb/arangod/Ahuacatl/ahuacatl-explain.cpp

529 lines
16 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Ahuacatl, explain
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "Ahuacatl/ahuacatl-explain.h"
#include "Basics/json.h"
#include "Basics/string-buffer.h"
#include "Ahuacatl/ahuacatl-collections.h"
#include "Ahuacatl/ahuacatl-context.h"
#include "Ahuacatl/ahuacatl-conversions.h"
#include "Ahuacatl/ahuacatl-node.h"
#include "Ahuacatl/ahuacatl-scope.h"
#include "Ahuacatl/ahuacatl-statement-walker.h"
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief convert a node to a string describing its type
////////////////////////////////////////////////////////////////////////////////
static TRI_json_t* NodeType (const TRI_aql_node_t* const node) {
TRI_json_t* result;
TRI_string_buffer_t buffer;
TRI_ASSERT(node);
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
if (TRI_ERROR_NO_ERROR != TRI_AppendStringStringBuffer(&buffer, TRI_NodeGroupAql(node, true))) {
TRI_DestroyStringBuffer(&buffer);
return NULL;
}
result = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, buffer._buffer);
TRI_DestroyStringBuffer(&buffer);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief convert a node to a string describing its content
////////////////////////////////////////////////////////////////////////////////
static TRI_json_t* NodeDescription (const TRI_aql_node_t* const node) {
TRI_string_buffer_t buffer;
TRI_json_t* result;
TRI_ASSERT(node);
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
if (! TRI_NodeStringAql(&buffer, node)) {
TRI_DestroyStringBuffer(&buffer);
return NULL;
}
result = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, buffer._buffer);
TRI_DestroyStringBuffer(&buffer);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add an expression node to the json result structure
////////////////////////////////////////////////////////////////////////////////
static bool AddNodeValue (TRI_json_t* row, TRI_aql_node_t* const node) {
TRI_json_t* result;
TRI_json_t* type;
TRI_json_t* value;
result = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
if (result == NULL) {
return false;
}
type = NodeType(node);
if (type != NULL) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
result,
"type",
type);
}
value = NodeDescription(node);
if (value != NULL) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
result,
"value",
value);
}
if (node->_type == TRI_AQL_NODE_COLLECTION) {
TRI_json_t* extra = TRI_GetJsonCollectionHintAql(
static_cast<TRI_aql_collection_hint_t*>
(TRI_AQL_NODE_DATA(node)) );
if (extra != NULL) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
result,
"extra",
extra);
}
}
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, row, "expression", result);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add a row to the result json structure
////////////////////////////////////////////////////////////////////////////////
static inline bool AddRow (TRI_aql_explain_t* const explain, TRI_json_t* value) {
return TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, explain->_result, value) == TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get an empty row protoype
////////////////////////////////////////////////////////////////////////////////
static inline TRI_json_t* GetRowProtoType (TRI_aql_explain_t* const explain,
const TRI_aql_node_type_e type) {
TRI_json_t* row;
row = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE);
if (row == NULL) {
return NULL;
}
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"id",
TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) ++explain->_count));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"loopLevel",
TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) explain->_level));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"type",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_NodeNameAql(type)));
return row;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an explain structure
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_explain_t* CreateExplain (TRI_aql_context_t* context) {
TRI_aql_explain_t* explain;
explain = (TRI_aql_explain_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_explain_t), false);
if (explain == NULL) {
return NULL;
}
explain->_context = context;
explain->_count = 0;
explain->_level = 0;
explain->_result = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE);
if (explain->_result == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, explain);
return NULL;
}
return explain;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free an explain structure
////////////////////////////////////////////////////////////////////////////////
static void FreeExplain (TRI_aql_explain_t* const explain) {
TRI_ASSERT(explain);
// note: explain->_result is intentionally not freed here
// the value is returned by TRI_ExplainAql() and must be
// freed by the caller
TRI_Free(TRI_UNKNOWN_MEM_ZONE, explain);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief process a single statement
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const walker,
TRI_aql_node_t* node) {
TRI_aql_explain_t* explain;
TRI_aql_node_type_e type = node->_type;
explain = (TRI_aql_explain_t*) walker->_data;
switch (type) {
case TRI_AQL_NODE_SCOPE_START: {
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
TRI_ASSERT(scope);
if (scope->_type != TRI_AQL_SCOPE_SUBQUERY && scope->_type != TRI_AQL_SCOPE_MAIN) {
++explain->_level;
}
break;
}
case TRI_AQL_NODE_SCOPE_END: {
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
TRI_ASSERT(scope);
if (scope->_type != TRI_AQL_SCOPE_SUBQUERY && scope->_type != TRI_AQL_SCOPE_MAIN) {
--explain->_level;
}
break;
}
case TRI_AQL_NODE_EXPAND: {
TRI_json_t* row;
row = GetRowProtoType(explain, TRI_AQL_NODE_REFERENCE);
if (row != NULL) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
AddNodeValue(row, TRI_AQL_NODE_MEMBER(node, 2));
AddRow(explain, row);
}
row = GetRowProtoType(explain, TRI_AQL_NODE_EXPAND);
if (row != NULL) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
AddNodeValue(row, TRI_AQL_NODE_MEMBER(node, 3));
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_SUBQUERY:
case TRI_AQL_NODE_SUBQUERY_CACHED: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_REMOVE:
case TRI_AQL_NODE_INSERT:
case TRI_AQL_NODE_UPDATE:
case TRI_AQL_NODE_REPLACE: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 0);
AddNodeValue(row, expressionNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_FOR: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_aql_for_hint_t* hint;
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
hint = static_cast<TRI_aql_for_hint_t*>(TRI_AQL_NODE_DATA(node));
if (hint != NULL &&
hint->_limit._status == TRI_AQL_LIMIT_USE) {
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"limit",
TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true));
}
AddNodeValue(row, expressionNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_RETURN:
case TRI_AQL_NODE_RETURN_EMPTY: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 0);
AddNodeValue(row, expressionNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_FILTER: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 0);
AddNodeValue(row, expressionNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_LET: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
AddNodeValue(row, expressionNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_SORT: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* listNode = TRI_AQL_NODE_MEMBER(node, 0);
AddNodeValue(row, listNode);
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_LIMIT: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* offsetNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* countNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"offset",
TRI_NodeJsonAql(explain->_context, offsetNode));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"count",
TRI_NodeJsonAql(explain->_context, countNode));
AddRow(explain, row);
}
break;
}
case TRI_AQL_NODE_COLLECT: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
if (row != NULL) {
TRI_aql_node_t* listNode = TRI_AQL_NODE_MEMBER(node, 0);
if (node->_members._length > 1) {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"resultVariable",
TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_AQL_NODE_STRING(variableNode)));
}
AddNodeValue(row, listNode);
AddRow(explain, row);
}
break;
}
default: {
}
}
return NULL;
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief explain a query
////////////////////////////////////////////////////////////////////////////////
TRI_json_t* TRI_ExplainAql (TRI_aql_context_t* const context) {
TRI_aql_statement_walker_t* walker;
TRI_aql_explain_t* explain;
TRI_json_t* result;
explain = CreateExplain(context);
if (explain == NULL) {
TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return NULL;
}
walker = TRI_CreateStatementWalkerAql((void*) explain,
false,
NULL,
&ProcessStatement,
NULL);
if (walker == NULL) {
FreeExplain(explain);
TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return NULL;
}
TRI_WalkStatementsAql(walker, context->_statements);
result = explain->_result;
FreeExplain(explain);
TRI_FreeStatementWalkerAql(walker);
return result;
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: