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

453 lines
14 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief Ahuacatl, explain
///
/// @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
////////////////////////////////////////////////////////////////////////////////
#include "Ahuacatl/ahuacatl-explain.h"
#include "Ahuacatl/ahuacatl-collections.h"
#include "Ahuacatl/ahuacatl-conversions.h"
#include "Ahuacatl/ahuacatl-scope.h"
#include "Ahuacatl/ahuacatl-statement-walker.h"
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @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;
assert(node);
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
TRI_AppendStringStringBuffer(&buffer, TRI_NodeGroupAql(node, true));
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;
assert(node);
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
TRI_NodeStringAql(&buffer, node);
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(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 const 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 (void) {
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->_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) {
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);
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);
assert(scope);
if (scope->_type != TRI_AQL_SCOPE_SUBQUERY && scope->_type != TRI_AQL_SCOPE_MAIN) {
--explain->_level;
}
break;
}
case TRI_AQL_NODE_EXPAND: {
TRI_aql_node_t* variableNode;
TRI_json_t* row;
row = GetRowProtoType(explain, TRI_AQL_NODE_REFERENCE);
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);
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: {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
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_FOR: {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
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_RETURN:
case TRI_AQL_NODE_RETURN_EMPTY: {
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
AddNodeValue(row, expressionNode);
AddRow(explain, row);
break;
}
case TRI_AQL_NODE_FILTER: {
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
AddNodeValue(row, expressionNode);
AddRow(explain, row);
break;
}
case TRI_AQL_NODE_LET: {
TRI_aql_node_t* variableNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
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_aql_node_t* listNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
AddNodeValue(row, listNode);
AddRow(explain, row);
break;
}
case TRI_AQL_NODE_LIMIT: {
TRI_aql_node_t* offsetNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* countNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"offset",
TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) TRI_AQL_NODE_INT(offsetNode)));
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE,
row,
"count",
TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) TRI_AQL_NODE_INT(countNode)));
AddRow(explain, row);
break;
}
case TRI_AQL_NODE_COLLECT: {
TRI_aql_node_t* listNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);
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
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @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();
if (explain == NULL) {
TRI_SetErrorContextAql(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(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return NULL;
}
TRI_WalkStatementsAql(walker, context->_statements);
result = explain->_result;
FreeExplain(explain);
TRI_FreeStatementWalkerAql(walker);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: