Пример #1
0
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);
}
Пример #2
0
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);
        }
    }
}
Пример #4
0
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;
}
Пример #5
0
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;
}
Пример #6
0
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;
}
Пример #7
0
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;
}
Пример #8
0
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;
}
Пример #9
0
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;
  }
}
Пример #10
0
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;
}
Пример #11
0
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;
}
Пример #12
0
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);
  }
}
Пример #13
0
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);
      }
    }
  } 
}
Пример #14
0
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;
}
Пример #15
0
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;
}