static TRI_aql_node_t* OptimiseReference (TRI_aql_statement_walker_t* const walker, TRI_aql_node_t* node) { TRI_aql_variable_t* variable; TRI_aql_node_t* definingNode; char* variableName = (char*) TRI_AQL_NODE_STRING(node); size_t scopeCount; // ignored assert(variableName); variable = TRI_GetVariableStatementWalkerAql(walker, variableName, &scopeCount); if (variable == NULL) { return node; } definingNode = variable->_definingNode; if (definingNode == NULL) { return node; } if (definingNode->_type == TRI_AQL_NODE_LET) { // variable is defined via a let TRI_aql_node_t* expressionNode; expressionNode = TRI_AQL_NODE_MEMBER(definingNode, 1); if (expressionNode && TRI_IsConstantValueNodeAql(expressionNode)) { // the source variable is constant, so we can replace the reference with // the source's value return expressionNode; } } return node; }
static void SetWriteOperation (TRI_aql_context_t* context, TRI_aql_node_t const* collection, TRI_aql_query_type_e type, TRI_aql_node_t* options) { if (context->_writeCollection != nullptr || context->_type != TRI_AQL_QUERY_READ) { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_QUERY_MULTI_MODIFY, nullptr); return; } if (context->_subQueries > 0) { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_QUERY_MODIFY_IN_SUBQUERY, nullptr); return; } if (collection->_type == TRI_AQL_NODE_COLLECTION) { // collection specified by name TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(collection, 0); if (nameNode == nullptr) { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, nullptr); return; } context->_writeCollection = TRI_AQL_NODE_STRING(nameNode); } else if (collection->_type == TRI_AQL_NODE_PARAMETER) { // collection specified via bind parameter context->_writeCollection = TRI_AQL_NODE_STRING(collection); } else { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_INTERNAL, nullptr); TRI_ASSERT(false); return; } if (options != nullptr) { if (! TRI_IsConstantValueNodeAql(options)) { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_QUERY_COMPILE_TIME_OPTIONS, nullptr); return; } } context->_type = type; context->_writeOptions = options; }
static TRI_aql_node_t* FixAttributeAccess (TRI_aql_statement_walker_t* const walker, TRI_aql_node_t* node) { TRI_aql_context_t* context; TRI_aql_node_t* valueNode; char* stringValue; if (node == NULL || node->_type != TRI_AQL_NODE_BOUND_ATTRIBUTE_ACCESS) { return node; } // we found a bound attribute access context = (TRI_aql_context_t*) walker->_data; assert(context); assert(node->_members._length == 2); valueNode = TRI_AQL_NODE_MEMBER(node, 1); if (valueNode == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return node; } if (valueNode->_type != TRI_AQL_NODE_VALUE || valueNode->_value._type != TRI_AQL_TYPE_STRING) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, "(unknown)"); return node; } stringValue = TRI_AQL_NODE_STRING(valueNode); if (stringValue == NULL || strlen(stringValue) == 0) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, "(unknown)"); return node; } // remove second node TRI_RemoveVectorPointer(&node->_members, 1); // set attribute name TRI_AQL_NODE_STRING(node) = stringValue; // convert node type node->_type = TRI_AQL_NODE_ATTRIBUTE_ACCESS; return node; }
static TRI_aql_node_t* InjectParameter (TRI_aql_statement_walker_t* const walker, TRI_aql_node_t* node) { TRI_aql_bind_parameter_t* bind; TRI_associative_pointer_t* bindValues; TRI_aql_context_t* context; char* name; if (node == NULL || node->_type != TRI_AQL_NODE_PARAMETER) { return node; } // we found a parameter node context = (TRI_aql_context_t*) walker->_data; assert(context); bindValues = (TRI_associative_pointer_t*) &context->_parameters._values; assert(bindValues); name = TRI_AQL_NODE_STRING(node); assert(name); bind = (TRI_aql_bind_parameter_t*) TRI_LookupByKeyAssociativePointer(bindValues, name); if (bind) { if (*name == '@') { // a collection name bind parameter if (TRI_IsStringJson(bind->_value)) { char* collectionName = TRI_RegisterStringAql(context, bind->_value->_value._string.data, bind->_value->_value._string.length - 1, false); node = TRI_CreateNodeCollectionAql(context, collectionName); } else { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, name); node = NULL; } } else { node = TRI_JsonNodeAql(context, bind->_value); } if (node == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETERS_INVALID, NULL); } } return node; }
static void AttachCollectionHint (TRI_aql_context_t* const context, TRI_aql_node_t* const node) { TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0); TRI_vector_pointer_t* availableIndexes; TRI_aql_collection_hint_t* hint; TRI_aql_index_t* idx; TRI_aql_collection_t* collection; char* collectionName; collectionName = TRI_AQL_NODE_STRING(nameNode); assert(collectionName); hint = (TRI_aql_collection_hint_t*) TRI_AQL_NODE_DATA(node); if (hint == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return; } if (hint->_ranges == NULL) { // no ranges found to be used as indexes return; } collection = TRI_GetCollectionAql(context, collectionName); if (collection == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return; } hint->_collection = collection; availableIndexes = &(((TRI_document_collection_t*) collection->_collection->_collection)->_allIndexes); if (availableIndexes == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return; } idx = TRI_DetermineIndexAql(context, availableIndexes, collectionName, hint->_ranges); hint->_index = idx; }
static void DumpValue (TRI_aql_dump_t* const state, const TRI_aql_node_t* const node) { PrintIndent(state); switch (node->_value._type) { case TRI_AQL_TYPE_FAIL: printf("fail\n"); break; case TRI_AQL_TYPE_NULL: printf("null\n"); break; case TRI_AQL_TYPE_BOOL: printf("bool (%lu)\n", (unsigned long) TRI_AQL_NODE_BOOL(node)); break; case TRI_AQL_TYPE_INT: printf("int (%ld)\n", (long) TRI_AQL_NODE_INT(node)); break; case TRI_AQL_TYPE_DOUBLE: printf("double (%f)\n", TRI_AQL_NODE_DOUBLE(node)); break; case TRI_AQL_TYPE_STRING: printf("string (%s)\n", TRI_AQL_NODE_STRING(node)); break; } }
static bool CheckArgumentType (TRI_aql_node_t* parameter, const param_t* const allowed) { param_t found = InitParam(); if (parameter->_type == TRI_AQL_NODE_PARAMETER) { // node is a bind parameter char* name = TRI_AQL_NODE_STRING(parameter); if (*name == '@') { // collection bind parameter. this is an error found._collection = true; found._list = true; // a collection is a list of documents } else { // regular bind parameter found._null = true; found._bool = true; found._number = true; found._string = true; found._list = true; found._array = true; } } else if (parameter->_type == TRI_AQL_NODE_VALUE) { switch (parameter->_value._type) { case TRI_AQL_TYPE_FAIL: case TRI_AQL_TYPE_NULL: found._null = true; break; case TRI_AQL_TYPE_BOOL: found._bool = true; break; case TRI_AQL_TYPE_INT: case TRI_AQL_TYPE_DOUBLE: found._number = true; break; case TRI_AQL_TYPE_STRING: found._string = true; break; } } else if (parameter->_type == TRI_AQL_NODE_LIST) { // actual parameter is a list found._list = true; } else if (parameter->_type == TRI_AQL_NODE_ARRAY) { // actual parameter is an array found._array = true; } else if (parameter->_type == TRI_AQL_NODE_COLLECTION) { // actual parameter is a collection found._collection = true; found._list = true; // a collection is a list of documents } else { // we cannot yet determine the type of the parameter // this is the case if the argument is an expression, a function call etc. if (!allowed->_collection) { // if we do require anything else but a collection, we don't know the // type and must exit here return true; } // if we require a collection, it must be passed in a form that we know // the collection name at parse time. otherwise, an error will be raised } if (allowed->_null && found._null) { // argument is a null value, and this is allowed return true; } if (allowed->_bool && found._bool) { // argument is a bool value, and this is allowed return true; } if (allowed->_number && found._number) { // argument is a numeric value, and this is allowed return true; } if (allowed->_string && found._string) { // argument is a string value, and this is allowed return true; } if (allowed->_list && found._list) { // argument is a list, and this is allowed return true; } if (allowed->_array && found._array) { // argument is an array, and this is allowed return true; } if (allowed->_collection && found._collection) { // argument is a collection, and this is allowed return true; } return false; }
bool TRI_NodeStringAql (TRI_string_buffer_t* const buffer, const TRI_aql_node_t* const node) { switch (node->_type) { case TRI_AQL_NODE_VALUE: { return TRI_ValueStringAql(buffer, &node->_value, node->_value._type); } case TRI_AQL_NODE_ARRAY_ELEMENT: { if (! TRI_ValueStringAql(buffer, &node->_value, TRI_AQL_TYPE_STRING)) { return false; } if (TRI_AppendStringStringBuffer(buffer, " : ") != TRI_ERROR_NO_ERROR) { return false; } return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0)); } case TRI_AQL_NODE_LIST: { if (TRI_AppendStringStringBuffer(buffer, "[ ") != TRI_ERROR_NO_ERROR) { return false; } if (! AppendListValues(buffer, node, &TRI_NodeStringAql)) { return false; } return (TRI_AppendStringStringBuffer(buffer, " ]") == TRI_ERROR_NO_ERROR); } case TRI_AQL_NODE_ARRAY: { if (TRI_AppendStringStringBuffer(buffer, "{ ") != TRI_ERROR_NO_ERROR) { return false; } if (! AppendListValues(buffer, node, &TRI_NodeStringAql)) { return false; } return (TRI_AppendStringStringBuffer(buffer, " }") == TRI_ERROR_NO_ERROR); } case TRI_AQL_NODE_OPERATOR_UNARY_PLUS: case TRI_AQL_NODE_OPERATOR_UNARY_MINUS: case TRI_AQL_NODE_OPERATOR_UNARY_NOT: { if (TRI_AppendStringStringBuffer(buffer, GetStringOperator(node->_type)) != TRI_ERROR_NO_ERROR) { return false; } return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0)); } case TRI_AQL_NODE_OPERATOR_BINARY_AND: case TRI_AQL_NODE_OPERATOR_BINARY_OR: case TRI_AQL_NODE_OPERATOR_BINARY_PLUS: case TRI_AQL_NODE_OPERATOR_BINARY_MINUS: case TRI_AQL_NODE_OPERATOR_BINARY_TIMES: case TRI_AQL_NODE_OPERATOR_BINARY_DIV: case TRI_AQL_NODE_OPERATOR_BINARY_MOD: case TRI_AQL_NODE_OPERATOR_BINARY_EQ: case TRI_AQL_NODE_OPERATOR_BINARY_NE: case TRI_AQL_NODE_OPERATOR_BINARY_LT: case TRI_AQL_NODE_OPERATOR_BINARY_LE: case TRI_AQL_NODE_OPERATOR_BINARY_GT: case TRI_AQL_NODE_OPERATOR_BINARY_GE: case TRI_AQL_NODE_OPERATOR_BINARY_IN: { if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } if (TRI_AppendStringStringBuffer(buffer, GetStringOperator(node->_type)) != TRI_ERROR_NO_ERROR) { return false; } return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 1)); } case TRI_AQL_NODE_OPERATOR_TERNARY: { if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } if (TRI_AppendStringStringBuffer(buffer, " ? ") != TRI_ERROR_NO_ERROR) { return false; } if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 1))) { return false; } if (TRI_AppendStringStringBuffer(buffer, " : ") != TRI_ERROR_NO_ERROR) { return false; } return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 2)); } case TRI_AQL_NODE_ATTRIBUTE_ACCESS: { if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } if (TRI_AppendStringStringBuffer(buffer, ".") != TRI_ERROR_NO_ERROR) { return false; } return TRI_AppendStringStringBuffer(buffer, TRI_AQL_NODE_STRING(node)) == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_INDEXED: { if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } if (TRI_AppendStringStringBuffer(buffer, "[") != TRI_ERROR_NO_ERROR) { return false; } if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 1))) { return false; } return TRI_AppendStringStringBuffer(buffer, "]") == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_FCALL: { TRI_aql_function_t* function = (TRI_aql_function_t*) TRI_AQL_NODE_DATA(node); if (TRI_AppendStringStringBuffer(buffer, function->_externalName) != TRI_ERROR_NO_ERROR) { return false; } if (TRI_AppendStringStringBuffer(buffer, "(") != TRI_ERROR_NO_ERROR) { return false; } if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } return TRI_AppendStringStringBuffer(buffer, ")") == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_EXPAND: { return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 3)); } case TRI_AQL_NODE_REFERENCE: { return TRI_AppendStringStringBuffer(buffer, TRI_AQL_NODE_STRING(node)) == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_COLLECTION: { TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0); char* name = TRI_AQL_NODE_STRING(nameNode); return TRI_AppendStringStringBuffer(buffer, name) == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_SORT: { return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0)); } case TRI_AQL_NODE_SORT_ELEMENT: { if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) { return false; } return TRI_AppendStringStringBuffer(buffer, (TRI_AQL_NODE_BOOL(node) ? " ASC" : " DESC")) == TRI_ERROR_NO_ERROR; } case TRI_AQL_NODE_ASSIGN: { TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0); char* name = TRI_AQL_NODE_STRING(nameNode); if (TRI_AppendStringStringBuffer(buffer, name) != TRI_ERROR_NO_ERROR) { return false; } if (TRI_AppendStringStringBuffer(buffer, " = ") != TRI_ERROR_NO_ERROR) { return false; } return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 1)); } default: { // nadata } } return true; }
TRI_json_t* TRI_NodeJsonAql (TRI_aql_context_t* const context, const TRI_aql_node_t* const node) { switch (node->_type) { case TRI_AQL_NODE_VALUE: { switch (node->_value._type) { case TRI_AQL_TYPE_FAIL: case TRI_AQL_TYPE_NULL: return TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE); case TRI_AQL_TYPE_BOOL: return TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, node->_value._value._bool); case TRI_AQL_TYPE_INT: return TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, (double) node->_value._value._int); case TRI_AQL_TYPE_DOUBLE: return TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, node->_value._value._double); case TRI_AQL_TYPE_STRING: return TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, node->_value._value._string); } } case TRI_AQL_NODE_LIST: { TRI_json_t* result = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); if (result) { size_t i, n; n = node->_members._length; for (i = 0; i < n; ++i) { TRI_json_t* subValue = TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(node, i)); if (subValue) { TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, result, subValue); } } } return result; } case TRI_AQL_NODE_ARRAY: { TRI_json_t* result = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); if (result) { size_t i, n; n = node->_members._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(node, i); TRI_json_t* subValue = TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(element, 0)); if (subValue) { TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result, TRI_AQL_NODE_STRING(element), subValue); } } } return result; } default: { return NULL; } } }
static void DumpString (TRI_aql_dump_t* const state, const TRI_aql_node_t* const node) { PrintIndent(state); printf("name: %s\n", TRI_AQL_NODE_STRING(node)); }
static void OptimisePaths (const TRI_aql_node_t* const fcallNode, TRI_aql_context_t* const context, TRI_aql_field_access_t* fieldAccess) { TRI_aql_node_t* args; TRI_aql_node_t* vertexCollection; TRI_aql_node_t* edgeCollection; TRI_aql_node_t* direction; char* directionValue; char* name; size_t n; args = TRI_AQL_NODE_MEMBER(fcallNode, 0); if (args == NULL) { return; } vertexCollection = TRI_AQL_NODE_MEMBER(args, 0); edgeCollection = TRI_AQL_NODE_MEMBER(args, 1); direction = TRI_AQL_NODE_MEMBER(args, 2); assert(vertexCollection); assert(edgeCollection); assert(direction); assert(fieldAccess); n = strlen(fieldAccess->_fullName); name = fieldAccess->_fullName + fieldAccess->_variableNameLength; directionValue = TRI_AQL_NODE_STRING(direction); // try to optimise the vertex collection access if (TRI_EqualString(directionValue, "outbound")) { CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n); } else if (TRI_EqualString(directionValue, "inbound")) { CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n); } else if (TRI_EqualString(directionValue, "any")) { // "any" cannot be optimised sanely becuase the conditions would be AND-combined // (but for "any", we'd need them OR-combined) // CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n); // CheckPathRestriction(fieldAccess, context, vertexCollection, ".destination.", name, n); } // check if we have a filter on LENGTH(edges) if (args->_members._length <= 4 && TRI_EqualString(name, ".edges.LENGTH()")) { // length restriction, can only be applied if length parameters are not already set TRI_json_t* value; double minValue = 0.0; double maxValue = 0.0; bool useMin = false; bool useMax = false; if (fieldAccess->_type == TRI_AQL_ACCESS_EXACT) { value = fieldAccess->_value._value; if (value != NULL && value->_type == TRI_JSON_NUMBER) { // LENGTH(p.edges) == const minValue = maxValue = value->_value._number; useMin = useMax = true; } } else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { value = fieldAccess->_value._singleRange._value; if (value != NULL && value->_type == TRI_JSON_NUMBER) { // LENGTH(p.edges) operator const if (fieldAccess->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED) { minValue = value->_value._number; useMin = true; } else if (fieldAccess->_value._singleRange._type == TRI_AQL_RANGE_UPPER_INCLUDED) { maxValue = value->_value._number; useMax = true; } else if (fieldAccess->_value._singleRange._type == TRI_AQL_RANGE_LOWER_EXCLUDED) { if ((double) ((int) value->_value._number) == value->_value._number) { minValue = value->_value._number + 1.0; useMin = true; } } else if (fieldAccess->_value._singleRange._type == TRI_AQL_RANGE_UPPER_EXCLUDED) { if ((double) ((int) value->_value._number) == value->_value._number) { maxValue = value->_value._number - 1.0; useMax = true; } } } } else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { // LENGTH(p.edges) > const && LENGTH(p.edges) < const value = fieldAccess->_value._between._lower._value; if (value != NULL && value->_type == TRI_JSON_NUMBER) { if (fieldAccess->_value._between._lower._type == TRI_AQL_RANGE_LOWER_INCLUDED) { minValue = value->_value._number; useMin = true; } else if (fieldAccess->_value._between._lower._type == TRI_AQL_RANGE_LOWER_EXCLUDED) { if ((double) ((int) value->_value._number) == value->_value._number) { minValue = value->_value._number + 1.0; useMin = true; } } } value = fieldAccess->_value._between._upper._value; if (value != NULL && value->_type == TRI_JSON_NUMBER) { if (fieldAccess->_value._between._upper._type == TRI_AQL_RANGE_UPPER_INCLUDED) { maxValue = value->_value._number; useMax = true; } else if (fieldAccess->_value._between._upper._type == TRI_AQL_RANGE_UPPER_EXCLUDED) { if ((double) ((int) value->_value._number) == value->_value._number) { maxValue = value->_value._number - 1.0; useMax = true; } } } } if (useMin || useMax) { TRI_aql_node_t* argNode; // minLength and maxLength are parameters 5 & 6 // add as many null value nodes as are missing while (args->_members._length < 4) { argNode = TRI_CreateNodeValueNullAql(context); if (argNode) { TRI_PushBackVectorPointer(&args->_members, (void*) argNode); } } // add min and max values to the function call argument list argNode = TRI_CreateNodeValueIntAql(context, useMin ? (int64_t) minValue : (int64_t) 0); if (argNode) { // min value node TRI_PushBackVectorPointer(&args->_members, (void*) argNode); argNode = TRI_CreateNodeValueIntAql(context, useMax ? (int64_t) maxValue : (int64_t) (1024 * 1024)); if (argNode) { // max value node TRI_PushBackVectorPointer(&args->_members, (void*) argNode); } } } } }
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_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_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 = 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; }
static void OptimisePaths (const TRI_aql_node_t* const fcallNode, TRI_aql_context_t* const context, TRI_aql_field_access_t* fieldAccess) { TRI_aql_collection_hint_t* hint; TRI_aql_node_t* args; TRI_aql_node_t* vertexCollection; TRI_aql_node_t* edgeCollection; TRI_aql_node_t* direction; char* directionValue; char* name; const char* lookFor; size_t len; size_t n; args = TRI_AQL_NODE_MEMBER(fcallNode, 0); if (args == NULL) { return; } vertexCollection = TRI_AQL_NODE_MEMBER(args, 0); edgeCollection = TRI_AQL_NODE_MEMBER(args, 1); direction = TRI_AQL_NODE_MEMBER(args, 2); assert(vertexCollection); assert(edgeCollection); assert(direction); assert(fieldAccess); n = strlen(fieldAccess->_fullName); name = fieldAccess->_fullName + fieldAccess->_variableNameLength; directionValue = TRI_AQL_NODE_STRING(direction); // try to optimise the vertex collection access if (TRI_EqualString(directionValue, "outbound")) { lookFor = ".source."; len = strlen(lookFor); } else if (TRI_EqualString(directionValue, "inbound")) { lookFor = ".destination."; len = strlen(lookFor); } else { lookFor = NULL; len = 0; } if (len > 0 && n > fieldAccess->_variableNameLength + len && memcmp((void*) lookFor, (void*) name, len) == 0) { // field name is collection.source.XXX, e.g. users.source._id LOG_DEBUG("optimising PATHS() field access %s", fieldAccess->_fullName); // we can now modify this fieldaccess in place to collection.XXX, e.g. users._id // copy trailing \0 byte as well memmove(name, name + len - 1, n - fieldAccess->_variableNameLength - len + 2); // attach the modified fieldaccess to the collection hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(vertexCollection)); hint->_ranges = TRI_AddAccessAql(context, hint->_ranges, fieldAccess); } }