static void FreeNodes (TRI_aql_context_t* const context) { size_t i = context->_memory._nodes._length; while (i--) { TRI_aql_node_t* node = (TRI_aql_node_t*) context->_memory._nodes._buffer[i]; if (node == NULL) { continue; } TRI_DestroyVectorPointer(&node->_members); if (node->_type == TRI_AQL_NODE_COLLECTION) { // free attached collection hint TRI_aql_collection_hint_t* hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(node)); if (hint != NULL) { TRI_FreeCollectionHintAql(hint); } } else if (node->_type == TRI_AQL_NODE_FOR) { // free attached for hint TRI_aql_for_hint_t* hint = (TRI_aql_for_hint_t*) (TRI_AQL_NODE_DATA(node)); if (hint != NULL) { TRI_FreeForHintScopeAql(hint); } } // free node itself TRI_Free(TRI_UNKNOWN_MEM_ZONE, node); } TRI_DestroyVectorPointer(&context->_memory._nodes); }
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 RunWalk (TRI_aql_statement_walker_t* const walker) { size_t i, n; assert(walker); n = walker->_statements->_statements._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* node; TRI_aql_node_type_e nodeType; node = (TRI_aql_node_t*) TRI_AtVectorPointer(&walker->_statements->_statements, i); if (!node) { continue; } nodeType = node->_type; // handle scopes if (nodeType == TRI_AQL_NODE_SCOPE_START) { TRI_PushBackVectorPointer(&walker->_currentScopes, TRI_AQL_NODE_DATA(node)); } // preprocess node if (walker->preVisitStatement != NULL) { // this might change the node ptr VisitStatement(walker, i, walker->preVisitStatement); node = walker->_statements->_statements._buffer[i]; } // process node's members if (walker->visitMember != NULL) { VisitMembers(walker, node); } // post process node if (walker->postVisitStatement != NULL) { VisitStatement(walker, i, walker->postVisitStatement); } if (nodeType == TRI_AQL_NODE_SCOPE_END) { size_t len = walker->_currentScopes._length; assert(len > 0); TRI_RemoveVectorPointer(&walker->_currentScopes, len - 1); } } }
static TRI_aql_node_t* CreateNodeInternalFcall (TRI_aql_context_t* const context, const char* const name, const char* const internalName, const TRI_aql_node_t* const parameters) { CREATE_NODE(TRI_AQL_NODE_FCALL) TRI_AQL_NODE_DATA(node) = 0; { TRI_aql_function_t* function; TRI_associative_pointer_t* functions; TRI_ASSERT(context->_vocbase); functions = context->_vocbase->_functions; TRI_ASSERT(functions); function = TRI_GetByExternalNameFunctionAql(functions, internalName); if (function == NULL) { // function name is unknown TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_QUERY_FUNCTION_NAME_UNKNOWN, name); return NULL; } // validate function call arguments if (! TRI_ValidateArgsFunctionAql(context, function, parameters)) { return NULL; } // initialise ADD_MEMBER(parameters) TRI_AQL_NODE_DATA(node) = function; } return node; }
static TRI_aql_node_t* DumpNode (TRI_aql_statement_walker_t* const walker, TRI_aql_node_t* const node) { TRI_aql_dump_t* state = (TRI_aql_dump_t*) walker->_data; if (node == NULL) { return node; } assert(state); PrintType(state, node->_type); Indent(state); switch (node->_type) { case TRI_AQL_NODE_VALUE: DumpValue(state, node); break; case TRI_AQL_NODE_VARIABLE: case TRI_AQL_NODE_ATTRIBUTE: case TRI_AQL_NODE_REFERENCE: case TRI_AQL_NODE_PARAMETER: case TRI_AQL_NODE_ARRAY_ELEMENT: case TRI_AQL_NODE_ATTRIBUTE_ACCESS: DumpString(state, node); break; case TRI_AQL_NODE_FCALL: printf("name: %s\n", TRI_GetInternalNameFunctionAql((TRI_aql_function_t*) TRI_AQL_NODE_DATA(node))); break; case TRI_AQL_NODE_SORT_ELEMENT: PrintIndent(state); printf("asc/desc: %lu\n", (unsigned long) TRI_AQL_NODE_BOOL(node)); break; default: { // nada } } Outdent(state); return node; }
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; }
static bool CheckPathRestriction (TRI_aql_field_access_t* fieldAccess, TRI_aql_context_t* const context, TRI_aql_node_t* vertexCollection, const char* lookFor, char* name, const size_t n) { size_t len; assert(fieldAccess); assert(lookFor); len = strlen(lookFor); if (len == 0) { return false; } if (n > fieldAccess->_variableNameLength + len && memcmp((void*) lookFor, (void*) name, len) == 0) { // we'll now patch the collection hint TRI_aql_collection_hint_t* hint; // field name is collection.source.abc, e.g. users.source._id LOG_DEBUG("optimising PATHS() field access %s", fieldAccess->_fullName); // we can now modify this fieldaccess in place to collection.abc, 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); return true; } return false; }
size_t TRI_InvalidateStatementListAql (TRI_aql_statement_list_t* const list, const size_t position) { size_t i, n; size_t start; size_t scopes; size_t ignoreScopes; assert(list); assert(position >= 0); n = list->_statements._length; // walk the scope from the specified position backwards until we find the start of the scope scopes = 1; ignoreScopes = 0; i = position; while (true) { TRI_aql_node_t* node = StatementAt(list, i); TRI_aql_node_type_e type = node->_type; list->_statements._buffer[i] = TRI_GetDummyNopNodeAql(); start = i; if (type == TRI_AQL_NODE_SCOPE_START) { // node is a scope start TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node); if (ignoreScopes > 0) { // this is an inner, parallel scope that we can ignore --ignoreScopes; } else { if (scope->_type != TRI_AQL_SCOPE_FOR_NESTED) { // we have reached the scope and need to stop break; } scopes++; } } else if (type == TRI_AQL_NODE_SCOPE_END) { // we found the end of another scope // we must remember how many other scopes we passed ignoreScopes++; } if (i-- == 0) { break; } } assert(ignoreScopes == 0); // remove from position forwards to scope end i = position; while (true) { TRI_aql_node_t* node = StatementAt(list, i); TRI_aql_node_type_e type = node->_type; list->_statements._buffer[i] = TRI_GetDummyNopNodeAql(); if (type == TRI_AQL_NODE_SCOPE_START) { ++scopes; } else if (type == TRI_AQL_NODE_SCOPE_END) { assert(scopes > 0); if (--scopes == 0) { break; } } if (++i == n) { break; } } list->_statements._buffer[start] = TRI_GetDummyReturnEmptyNodeAql(); return start + 1; }
void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const list) { size_t i, n; size_t scopes = 0; size_t targetScope = 0; size_t moveStart = 0; bool watch = false; assert(list); i = 0; n = list->_statements._length; while (i < n) { TRI_aql_node_t* node = StatementAt(list, i); TRI_aql_node_type_e type = node->_type; if (type == TRI_AQL_NODE_SCOPE_START) { // node is a scope start TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node); if (scope->_type == TRI_AQL_SCOPE_SUBQUERY && scope->_selfContained) { if (! watch && scopes > 0) { watch = true; targetScope = scopes; moveStart = i; } } ++scopes; } else if (type == TRI_AQL_NODE_SCOPE_END) { // node is a scope end --scopes; if (watch && scopes == targetScope) { watch = false; node = StatementAt(list, i + 1); // check if next statement is a subquery statement if (i + 1 < n && node->_type == TRI_AQL_NODE_SUBQUERY) { size_t j = moveStart; size_t inserted = 0; // moving statements from the middle to the beginning of the list will also // modify the positions we're moving from while (j < i + 2) { node = StatementAt(list, j + inserted); if (! TRI_InsertStatementListAql(list, node, inserted + 0)) { return; } // insert a dummy node in place of the moved node list->_statements._buffer[j + inserted + 1] = TRI_GetDummyNopNodeAql(); // next ++j; ++inserted; } // moving statements from the middle to the beginning of the list will also // change the list length and the position we'll be continuing from n += inserted; i = j + inserted; } } } ++i; } }
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; }
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); } }
static void PatchVariables (TRI_aql_statement_walker_t* const walker) { TRI_aql_context_t* context = ((aql_optimiser_t*) walker->_data)->_context; TRI_vector_pointer_t* ranges; size_t i, n; ranges = TRI_GetCurrentRangesStatementWalkerAql(walker); if (ranges == NULL) { // no ranges defined, exit early return; } // iterate over all ranges found n = ranges->_length; for (i = 0; i < n; ++i) { TRI_aql_field_access_t* fieldAccess; TRI_aql_variable_t* variable; TRI_aql_node_t* definingNode; TRI_aql_node_t* expressionNode; char* variableName; size_t scopeCount; bool isReference; fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(ranges, i); assert(fieldAccess); assert(fieldAccess->_fullName); assert(fieldAccess->_variableNameLength > 0); variableName = TRI_DuplicateString2Z(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_fullName, fieldAccess->_variableNameLength); if (variableName == NULL) { // out of memory! TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return; } isReference = (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE); variable = TRI_GetVariableStatementWalkerAql(walker, variableName, &scopeCount); TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, variableName); if (variable == NULL) { continue; } if (isReference && scopeCount > 0) { // unfortunately, the referenced variable is in an outer scope, so we cannot use it continue; } // note: we must not modify outer variables of subqueries // get the node that defines the variable definingNode = variable->_definingNode; assert(definingNode != NULL); expressionNode = NULL; switch (definingNode->_type) { case TRI_AQL_NODE_LET: expressionNode = TRI_AQL_NODE_MEMBER(definingNode, 1); break; case TRI_AQL_NODE_FOR: expressionNode = TRI_AQL_NODE_MEMBER(definingNode, 1); break; default: { } } if (expressionNode != NULL) { if (expressionNode->_type == TRI_AQL_NODE_FCALL) { // the defining node is a function call // get the function name TRI_aql_function_t* function = TRI_AQL_NODE_DATA(expressionNode); if (function->optimise != NULL) { // call the function's optimise callback function->optimise(expressionNode, context, fieldAccess); } } if (expressionNode->_type == TRI_AQL_NODE_COLLECTION) { TRI_aql_collection_hint_t* hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(expressionNode)); // set new value hint->_ranges = TRI_AddAccessAql(context, hint->_ranges, fieldAccess); } } } }
static TRI_aql_node_t* OptimiseFcall (TRI_aql_context_t* const context, TRI_aql_node_t* node) { TRI_aql_node_t* args = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_function_t* function; TRI_js_exec_context_t* execContext; TRI_string_buffer_t* code; TRI_json_t* json; size_t i; size_t n; function = (TRI_aql_function_t*) TRI_AQL_NODE_DATA(node); assert(function); // check if function is deterministic if (!function->_isDeterministic) { return node; } // check if function call arguments are deterministic n = args->_members._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* arg = (TRI_aql_node_t*) args->_members._buffer[i]; if (!arg || !TRI_IsConstantValueNodeAql(arg)) { return node; } } // all arguments are constants // create the function code code = FcallCode(function->_internalName, args); if (!code) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return node; } // execute the function code execContext = TRI_CreateExecutionContext(code->_buffer); TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, code); if (!execContext) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return node; } json = TRI_ExecuteResultContext(execContext); TRI_FreeExecutionContext(execContext); if (!json) { // cannot optimise the function call due to an internal error // TODO: check whether we can validate the arguments here already and return an error early // TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_SCRIPT, "function optimisation"); return node; } // use the constant values instead of the function call node node = TRI_JsonNodeAql(context, json); if (!node) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); } TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); LOG_TRACE("optimised function call"); return node; }
size_t TRI_InvalidateStatementListAql (TRI_aql_statement_list_t* const list, const size_t position) { size_t i, n; size_t start; size_t scopes; assert(list); assert(position >= 0); n = list->_statements._length; // remove from position backwards to scope start scopes = 1; i = position; while (true) { TRI_aql_node_t* node = StatementAt(list, i); TRI_aql_node_type_e type = node->_type; list->_statements._buffer[i] = TRI_GetDummyNopNodeAql(); start = i; if (type == TRI_AQL_NODE_SCOPE_START) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node); if (scope->_type != TRI_AQL_SCOPE_FOR_NESTED) { break; } scopes++; } if (i-- == 0) { break; } } // remove from position forwards to scope end i = position; while (true) { TRI_aql_node_t* node = StatementAt(list, i); TRI_aql_node_type_e type = node->_type; list->_statements._buffer[i] = TRI_GetDummyNopNodeAql(); if (type == TRI_AQL_NODE_SCOPE_START) { ++scopes; } else if (type == TRI_AQL_NODE_SCOPE_END) { assert(scopes > 0); if (--scopes == 0) { break; } } if (++i == n) { break; } } list->_statements._buffer[start] = TRI_GetDummyReturnEmptyNodeAql(); return start + 1; }