static void FreeScope (TRI_aql_scope_t* const scope) { size_t i, n; // free variables lookup hash n = scope->_variables._nrAlloc; for (i = 0; i < n; ++i) { TRI_aql_variable_t* variable = scope->_variables._table[i]; if (variable) { TRI_FreeVariableAql(variable); } } TRI_DestroyAssociativePointer(&scope->_variables); if (scope->_ranges) { // free ranges if set TRI_FreeAccessesAql(scope->_ranges); } for (i = 0; i < scope->_sorts._length; ++i) { char* criterion = (char*) TRI_AtVectorPointer(&scope->_sorts, i); TRI_Free(TRI_UNKNOWN_MEM_ZONE, criterion); } TRI_DestroyVectorPointer(&scope->_sorts); TRI_Free(TRI_UNKNOWN_MEM_ZONE, scope); }
bool TRI_VariableExistsScopeAql (TRI_aql_context_t* const context, const char* const name) { size_t n; if (name == NULL) { TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } n = context->_currentScopes._length; assert(n > 0); while (n > 0) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, --n); assert(scope); if (TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name)) { // duplicate variable return true; } if (n == 0) { // reached the outermost scope break; } } return false; }
static void VisitMembers (TRI_aql_statement_walker_t* const walker, TRI_aql_node_t* const node) { size_t i, n; assert(node); n = node->_members._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* member; TRI_aql_node_t* modified; member = (TRI_aql_node_t*) TRI_AtVectorPointer(&node->_members, i); if (!member) { continue; } VisitMembers(walker, member); modified = walker->visitMember(walker, member); if (walker->_canModify && modified != member) { if (modified == NULL) { modified = TRI_GetDummyNopNodeAql(); } node->_members._buffer[i] = modified; } } }
bool TRI_KillExternalProcess (TRI_external_id_t pid) { TRI_external_t* external = nullptr; // just to please the compiler size_t i; bool ok = true; LOG_DEBUG("killing process: %d", (int) pid._pid); TRI_LockMutex(&ExternalProcessesLock); for (i = 0; i < ExternalProcesses._length; ++i) { external = static_cast<TRI_external_t*>(TRI_AtVectorPointer(&ExternalProcesses, i)); if (external->_pid == pid._pid) { break; } } if (i == ExternalProcesses._length) { TRI_UnlockMutex(&ExternalProcessesLock); LOG_DEBUG("kill: process not found: %d", (int) pid._pid); #ifndef _WIN32 // Kill just in case: if (0 == kill(pid._pid, SIGTERM)) { int count; // Otherwise we just let it be. for (count = 0; count < 10; count++) { int loc; pid_t p; // And wait for it to avoid a zombie: sleep(1); p = waitpid(pid._pid, &loc, WUNTRACED | WNOHANG); if (p == pid._pid) { return true; } if (count == 8) { kill(pid._pid, SIGKILL); } } } return false; #else return ourKillProcessPID(pid._pid); #endif } if (external->_status == TRI_EXT_RUNNING || external->_status == TRI_EXT_STOPPED) { ok = ourKillProcess(external); } TRI_RemoveVectorPointer(&ExternalProcesses, i); TRI_UnlockMutex(&ExternalProcessesLock); FreeExternal(external); return ok; }
TRI_aql_scope_t* TRI_GetCurrentScopeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) { size_t n; assert(walker); n = walker->_currentScopes._length; assert(n > 0); return (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, n - 1); }
static inline TRI_aql_scope_t* CurrentScope (TRI_aql_context_t* const context) { TRI_aql_scope_t* scope; size_t n; assert(context); n = context->_currentScopes._length; assert(n > 0); scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, n - 1); assert(scope); return scope; }
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); } } }
void TRI_FreeScopesAql (TRI_aql_context_t* const context) { size_t i, n; assert(context); n = context->_memory._scopes._length; for (i = 0; i < n; ++i) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_memory._scopes, i); FreeScope(scope); } TRI_DestroyVectorPointer(&context->_memory._scopes); TRI_DestroyVectorPointer(&context->_currentScopes); }
TRI_aql_collection_t* TRI_GetCollectionAql (const TRI_aql_context_t* const context, const char* const collectionName) { size_t i, n; assert(context); n = context->_collections._length; for (i = 0; i < n; ++i) { TRI_aql_collection_t* col = (TRI_aql_collection_t*) TRI_AtVectorPointer(&context->_collections, i); if (TRI_EqualString(col->_name, collectionName)) { return col; } } return NULL; }
static void VisitStatement (TRI_aql_statement_walker_t* const walker, const size_t position, TRI_aql_visit_f func) { TRI_aql_node_t* node; TRI_aql_node_t* modified; node = (TRI_aql_node_t*) TRI_AtVectorPointer(&walker->_statements->_statements, position); assert(node); modified = func(walker, node); if (walker->_canModify && modified != node) { if (modified == NULL) { modified = TRI_GetDummyNopNodeAql(); } walker->_statements->_statements._buffer[position] = modified; } }
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker, const char* const name, size_t* scopeCount) { size_t n; // init scope counter to 0 *scopeCount = 0; assert(name != NULL); n = walker->_currentScopes._length; while (n > 0) { TRI_aql_scope_t* scope; TRI_aql_variable_t* variable; scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, --n); assert(scope); variable = TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name); if (variable != NULL) { return variable; } if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) { // reached the outermost scope if (scope->_type == TRI_AQL_SCOPE_SUBQUERY) { // variable not found but we reached the end of the scope // we must mark the scope as not self-contained so it is not moved to // some other position scope->_selfContained = false; } return NULL; } // increase the scope counter (*scopeCount)++; } // variable not found return NULL; }
static bool IterateDatafilesVector (const TRI_vector_pointer_t* const files, bool (*iterator)(TRI_df_marker_t const*, void*, TRI_datafile_t*, bool), void* data) { size_t i, n; n = files->_length; for (i = 0; i < n; ++i) { TRI_datafile_t* datafile; int result; datafile = (TRI_datafile_t*) TRI_AtVectorPointer(files, i); result = TRI_IterateDatafile(datafile, iterator, data, false); if (! result) { return false; } } return true; }
void TRI_EmptyScopeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) { size_t n = walker->_currentScopes._length; while (n > 0) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, n - 1); if (scope->_type == TRI_AQL_SCOPE_SUBQUERY) { break; } scope->_empty = true; if (scope->_type != TRI_AQL_SCOPE_FOR && scope->_type != TRI_AQL_SCOPE_FOR_NESTED && scope->_type != TRI_AQL_SCOPE_MAIN) { break; } --n; } }
static TRI_multi_pointer_t* FindEdgesIndex (TRI_document_collection_t* const document) { size_t i, n; if (document->base.base._info._type != TRI_COL_TYPE_EDGE) { // collection is not an edge collection... caller must handle that return NULL; } n = document->_allIndexes._length; for (i = 0; i < n; ++i) { TRI_index_t* idx = (TRI_index_t*) TRI_AtVectorPointer(&document->_allIndexes, i); if (idx->_type == TRI_IDX_TYPE_EDGE_INDEX) { TRI_edge_index_t* edgesIndex = (TRI_edge_index_t*) idx; return &edgesIndex->_edges; } } // collection does not have an edges index... caller must handle that return NULL; }
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); } } } }
TRI_external_status_t TRI_CheckExternalProcess (TRI_external_id_t pid, bool wait) { TRI_external_status_t status; TRI_external_t* external = nullptr; // Just to please the compiler size_t i; TRI_LockMutex(&ExternalProcessesLock); status._status = TRI_EXT_NOT_FOUND; status._exitStatus = 0; for (i = 0; i < ExternalProcesses._length; ++i) { external = static_cast<TRI_external_t*>(TRI_AtVectorPointer(&ExternalProcesses, i)); if (external->_pid == pid._pid) { break; } } if (i == ExternalProcesses._length) { TRI_UnlockMutex(&ExternalProcessesLock); status._errorMessage = std::string("the pid you're looking for is not in our list: ") + triagens::basics::StringUtils::itoa(static_cast<int64_t>(pid._pid)); status._status = TRI_EXT_NOT_FOUND; LOG_WARNING("checkExternal: pid not found: %d", (int) pid._pid); return status; } if (external->_status == TRI_EXT_RUNNING || external->_status == TRI_EXT_STOPPED) { #ifndef _WIN32 TRI_pid_t res; int opts; int loc = 0; if (wait) { opts = WUNTRACED; } else { opts = WNOHANG | WUNTRACED; } res = waitpid(external->_pid, &loc, opts); if (res == 0) { if (wait) { status._errorMessage = std::string("waitpid returned 0 for pid while it shouldn't ") + triagens::basics::StringUtils::itoa(external->_pid); if (WIFEXITED(loc)) { external->_status = TRI_EXT_TERMINATED; external->_exitStatus = WEXITSTATUS(loc); } else if (WIFSIGNALED(loc)) { external->_status = TRI_EXT_ABORTED; external->_exitStatus = WTERMSIG(loc); } else if (WIFSTOPPED(loc)) { external->_status = TRI_EXT_STOPPED; external->_exitStatus = 0; } else { external->_status = TRI_EXT_ABORTED; external->_exitStatus = 0; } } else { external->_exitStatus = 0; } } else if (res == -1) { TRI_set_errno(TRI_ERROR_SYS_ERROR); LOG_WARNING("waitpid returned error for pid %d (%d): %s", (int) external->_pid, (int) wait, TRI_last_error()); status._errorMessage = std::string("waitpid returned error for pid ") + triagens::basics::StringUtils::itoa(external->_pid) + std::string(": ") + std::string(TRI_last_error()); } else if (static_cast<TRI_pid_t>(external->_pid) == static_cast<TRI_pid_t>(res)) { if (WIFEXITED(loc)) { external->_status = TRI_EXT_TERMINATED; external->_exitStatus = WEXITSTATUS(loc); } else if (WIFSIGNALED(loc)) { external->_status = TRI_EXT_ABORTED; external->_exitStatus = WTERMSIG(loc); } else if (WIFSTOPPED(loc)) { external->_status = TRI_EXT_STOPPED; external->_exitStatus = 0; } else { external->_status = TRI_EXT_ABORTED; external->_exitStatus = 0; } } else { LOG_WARNING("unexpected waitpid result for pid %d: %d", (int) external->_pid, (int) res); status._errorMessage = std::string("unexpected waitpid result for pid ") + triagens::basics::StringUtils::itoa(external->_pid) + std::string(": ") + triagens::basics::StringUtils::itoa(res); } #else { char windowsErrorBuf[256]; bool wantGetExitCode = wait; if (wait) { DWORD result; result = WaitForSingleObject(external->_process, INFINITE); if (result == WAIT_FAILED) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, windowsErrorBuf, sizeof(windowsErrorBuf), NULL); LOG_WARNING("could not wait for subprocess with PID '%ud': %s", (unsigned int) external->_pid, windowsErrorBuf); status._errorMessage = std::string("could not wait for subprocess with PID '") + triagens::basics::StringUtils::itoa(static_cast<int64_t>(external->_pid)) + std::string("'") + windowsErrorBuf; status._exitStatus = GetLastError(); } } else { DWORD result; result = WaitForSingleObject(external->_process, 0); switch (result) { case WAIT_ABANDONED: wantGetExitCode = true; LOG_WARNING("WAIT_ABANDONED while waiting for subprocess with PID '%ud'", (unsigned int)external->_pid); break; case WAIT_OBJECT_0: /// this seems to be the exit case - want getExitCodeProcess here. wantGetExitCode = true; break; case WAIT_TIMEOUT: // success - everything went well. external->_exitStatus = 0; break; case WAIT_FAILED: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, windowsErrorBuf, sizeof(windowsErrorBuf), NULL); LOG_WARNING("could not wait for subprocess with PID '%ud': %s", (unsigned int)external->_pid, windowsErrorBuf); status._errorMessage = std::string("could not wait for subprocess with PID '") + triagens::basics::StringUtils::itoa(static_cast<int64_t>(external->_pid)) + std::string("'") + windowsErrorBuf; status._exitStatus = GetLastError(); default: wantGetExitCode = true; LOG_WARNING("unexpected status while waiting for subprocess with PID '%ud'", (unsigned int)external->_pid); } } if (wantGetExitCode) { DWORD exitCode = STILL_ACTIVE; if (!GetExitCodeProcess(external->_process, &exitCode)) { LOG_WARNING("exit status could not be determined for PID '%ud'", (unsigned int)external->_pid); status._errorMessage = std::string("exit status could not be determined for PID '") + triagens::basics::StringUtils::itoa(static_cast<int64_t>(external->_pid)) + std::string("'"); } else { if (exitCode == STILL_ACTIVE) { external->_exitStatus = 0; } else if (exitCode > 255) { // this should be one of our signals which we mapped... external->_status = TRI_EXT_ABORTED; external->_exitStatus = exitCode - 255; } else { external->_status = TRI_EXT_TERMINATED; external->_exitStatus = exitCode; } } } else { external->_status = TRI_EXT_RUNNING; } } #endif } else { LOG_WARNING("unexpected process status %d: %d", (int) external->_status, (int) external->_exitStatus); status._errorMessage = std::string("unexpected process status ") + triagens::basics::StringUtils::itoa(external->_status) + std::string(": ") + triagens::basics::StringUtils::itoa(external->_exitStatus); } status._status = external->_status; status._exitStatus = external->_exitStatus; // Do we have to free our data? if (external->_status != TRI_EXT_RUNNING && external->_status != TRI_EXT_STOPPED) { TRI_RemoveVectorPointer(&ExternalProcesses, i); FreeExternal(external); } TRI_UnlockMutex(&ExternalProcessesLock); return status; }
TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context, const TRI_vector_pointer_t* const availableIndexes, const char* const collectionName, const TRI_vector_pointer_t* candidates) { TRI_aql_index_t* picked = NULL; TRI_vector_pointer_t matches; size_t i, n; TRI_InitVectorPointer(&matches, TRI_UNKNOWN_MEM_ZONE); assert(context); assert(collectionName); assert(candidates); n = availableIndexes->_length; for (i = 0; i < n; ++i) { TRI_index_t* idx = (TRI_index_t*) availableIndexes->_buffer[i]; size_t numIndexFields; bool lastTypeWasExact; size_t j; if (! CanUseIndex(idx)) { continue; } LogIndexString("checking", idx, collectionName); TRI_ClearVectorPointer(&matches); lastTypeWasExact = true; numIndexFields = idx->_fields._length; // now loop over all index fields, from left to right // index field order is important because skiplists can be used with leftmost prefixes as well, // but not with rightmost prefixes for (j = 0; j < numIndexFields; ++j) { char* indexedFieldName; char* fieldName; size_t k; indexedFieldName = idx->_fields._buffer[j]; if (indexedFieldName == NULL) { continue; } // now loop over all candidates for (k = 0; k < candidates->_length; ++k) { TRI_aql_field_access_t* candidate = (TRI_aql_field_access_t*) TRI_AtVectorPointer(candidates, k); if (candidate->_type == TRI_AQL_ACCESS_IMPOSSIBLE || candidate->_type == TRI_AQL_ACCESS_ALL) { // wrong index type, doesn't help us at all continue; } fieldName = candidate->_fullName + candidate->_variableNameLength + 1; if (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) { // primary index key names must be treated differently. _id and _key are the same if (! TRI_EqualString("_id", fieldName) && ! TRI_EqualString(TRI_VOC_ATTRIBUTE_KEY, fieldName)) { continue; } } else if (idx->_type == TRI_IDX_TYPE_EDGE_INDEX) { // edge index key names must be treated differently. _from and _to can be used independently if (! TRI_EqualString(TRI_VOC_ATTRIBUTE_FROM, fieldName) && ! TRI_EqualString(TRI_VOC_ATTRIBUTE_TO, fieldName)) { continue; } } else if (! TRI_EqualString(indexedFieldName, fieldName)) { // different attribute, doesn't help continue; } // attribute is used in index if (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX || idx->_type == TRI_IDX_TYPE_EDGE_INDEX) { if (! IsExactCandidate(candidate)) { // wrong access type for primary index continue; } TRI_PushBackVectorPointer(&matches, candidate); } else if (idx->_type == TRI_IDX_TYPE_HASH_INDEX) { if (! IsExactCandidate(candidate)) { // wrong access type for hash index continue; } if (candidate->_type == TRI_AQL_ACCESS_LIST && numIndexFields != 1) { // we found a list, but the index covers multiple attributes. that means we cannot use list access continue; } TRI_PushBackVectorPointer(&matches, candidate); } else if (idx->_type == TRI_IDX_TYPE_BITARRAY_INDEX) { if (! IsExactCandidate(candidate)) { // wrong access type for hash index continue; } if (candidate->_type == TRI_AQL_ACCESS_LIST) { // we found a list, but the index covers multiple attributes. that means we cannot use list access continue; } TRI_PushBackVectorPointer(&matches, candidate); } else if (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) { bool candidateIsExact; if (candidate->_type != TRI_AQL_ACCESS_EXACT && candidate->_type != TRI_AQL_ACCESS_LIST && candidate->_type != TRI_AQL_ACCESS_RANGE_SINGLE && candidate->_type != TRI_AQL_ACCESS_RANGE_DOUBLE && candidate->_type != TRI_AQL_ACCESS_REFERENCE) { // wrong access type for skiplists continue; } if (candidate->_type == TRI_AQL_ACCESS_LIST && numIndexFields != 1) { // we found a list, but the index covers multiple attributes. that means we cannot use list access continue; } candidateIsExact = IsExactCandidate(candidate); if ((candidateIsExact && ! lastTypeWasExact) || (! candidateIsExact && ! lastTypeWasExact)) { // if we already had a range query, we cannot check for equality after that // if we already had a range query, we cannot check another range after that continue; } if (candidate->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { // range type. check if the compare value is a list or an object TRI_json_t* value = candidate->_value._singleRange._value; if (TRI_IsListJson(value) || TRI_IsArrayJson(value)) { // list or object, we cannot use this for comparison in a skiplist continue; } } else if (candidate->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { // range type. check if the compare value is a list or an object TRI_json_t* value = candidate->_value._between._lower._value; if (TRI_IsListJson(value) || TRI_IsArrayJson(value)) { // list or object, we cannot use this for comparison in a skiplist continue; } value = candidate->_value._between._upper._value; if (TRI_IsListJson(value) || TRI_IsArrayJson(value)) { // list or object, we cannot use this for comparison in a skiplist continue; } } lastTypeWasExact = candidateIsExact; TRI_PushBackVectorPointer(&matches, candidate); } } // finished iterating over all candidates if (matches._length != j + 1) { // we already have picked less candidate fields than we should break; } } if (matches._length < 1) { // nothing found continue; } // we now do or don't have an index candidate in the matches vector if (matches._length < numIndexFields && TRI_NeedsFullCoverageIndex(idx->_type)) { // the matches vector does not fully cover the indexed fields, but the index requires it continue; } // if we can use the primary index, we'll use it picked = PickIndex(context, picked, idx, &matches); } TRI_DestroyVectorPointer(&matches); if (picked) { LogIndexString("using", picked->_idx, collectionName); } return picked; }
static inline TRI_aql_node_t* StatementAt (const TRI_aql_statement_list_t* const list, const size_t position) { return (TRI_aql_node_t*) TRI_AtVectorPointer(&list->_statements, position); }