Beispiel #1
0
static TRI_aql_node_t* OptimiseUnaryLogicalOperation (TRI_aql_context_t* const context,
                                                      TRI_aql_node_t* node) {
  TRI_aql_node_t* operand = TRI_AQL_NODE_MEMBER(node, 0);
  
  assert(node->_type == TRI_AQL_NODE_OPERATOR_UNARY_NOT);

  if (!operand || !TRI_IsConstantValueNodeAql(operand)) {
    // node is not a constant value
    return node;
  }

  if (!TRI_IsBooleanValueNodeAql(operand)) {
    // value type is not boolean => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
    return node;
  }
  
  // ! (bool value) => evaluate and replace with result
  node = TRI_CreateNodeValueBoolAql(context, ! TRI_GetBooleanNodeValueAql(operand));
  if (!node) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
  }

  LOG_TRACE("optimised away unary logical operation");

  return node;
}
Beispiel #2
0
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;
}
Beispiel #3
0
static TRI_aql_node_t* OptimiseUnaryArithmeticOperation (TRI_aql_context_t* const context,
                                                         TRI_aql_node_t* node) {
  TRI_aql_node_t* operand = TRI_AQL_NODE_MEMBER(node, 0);
  
  assert(node->_type == TRI_AQL_NODE_OPERATOR_UNARY_PLUS ||
         node->_type == TRI_AQL_NODE_OPERATOR_UNARY_MINUS);

  if (!operand || !TRI_IsConstantValueNodeAql(operand)) {
    return node;
  }

  if (!TRI_IsNumericValueNodeAql(operand)) {
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
    return node;
  }
  

  if (node->_type == TRI_AQL_NODE_OPERATOR_UNARY_PLUS) {
    // + number => number
    node = operand;
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_UNARY_MINUS) {
    // - number => eval!
    node = TRI_CreateNodeValueDoubleAql(context, - TRI_GetNumericNodeValueAql(operand));
    if (node == NULL) {
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    }
  }

  return node;
}
Beispiel #4
0
static TRI_aql_node_t* OptimiseFilter (TRI_aql_statement_walker_t* const walker,
                                       TRI_aql_node_t* node) {
  aql_optimiser_t* optimiser = (aql_optimiser_t*) walker->_data;
  TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0);
 
  while (true) {
    TRI_vector_pointer_t* oldRanges;
    TRI_vector_pointer_t* newRanges;
    bool changed;

    if (!expression) {
      return node;
    }

    if (TRI_IsConstantValueNodeAql(expression)) {
      // filter expression is a constant value
      return OptimiseConstantFilter(expression);
    }

    // filter expression is non-constant
    oldRanges = TRI_GetCurrentRangesStatementWalkerAql(walker);
    
    changed = false;
    newRanges = TRI_OptimiseRangesAql(optimiser->_context, expression, &changed, oldRanges);
    
    if (newRanges) {
      TRI_SetCurrentRangesStatementWalkerAql(walker, newRanges);
    }
    
    if (!changed) {
      break;
    }

    // expression code was changed, set pointer to new value re-optimise it
    node->_members._buffer[0] = OptimiseNode(walker, expression);
    expression = TRI_AQL_NODE_MEMBER(node, 0);

    // next iteration
  }

  return node;
}
Beispiel #5
0
static TRI_aql_node_t* OptimiseSort (TRI_aql_statement_walker_t* const walker,
                                     TRI_aql_node_t* node) {
  TRI_aql_node_t* list = TRI_AQL_NODE_MEMBER(node, 0);
  size_t i, n;
    
  if (!list) {
    return node;
  }

  i = 0;
  n = list->_members._length;
  while (i < n) {
    // sort element
    TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(list, i);
    TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(element, 0);

    // check if the sort element is constant
    if (!expression || !TRI_IsConstantValueNodeAql(expression)) {
      ++i;
      continue;
    }

    // sort element is constant so it can be removed
    TRI_RemoveVectorPointer(&list->_members, i);
    --n;

    LOG_TRACE("optimised away sort element");
  }

  if (n == 0) {
    // no members left => sort removed
    LOG_TRACE("optimised away sort");

    return TRI_GetDummyNopNodeAql();
  }

  return node;
}
Beispiel #6
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;
}
Beispiel #7
0
static TRI_aql_node_t* OptimiseTernaryOperation (TRI_aql_context_t* const context,
                                                 TRI_aql_node_t* node) {
  TRI_aql_node_t* condition = TRI_AQL_NODE_MEMBER(node, 0);
  TRI_aql_node_t* truePart = TRI_AQL_NODE_MEMBER(node, 1);
  TRI_aql_node_t* falsePart = TRI_AQL_NODE_MEMBER(node, 2);
  
  assert(node->_type == TRI_AQL_NODE_OPERATOR_TERNARY);

  if (!condition || !TRI_IsConstantValueNodeAql(condition)) {
    // node is not a constant value
    return node;
  }

  if (!TRI_IsBooleanValueNodeAql(condition)) {
    // node is not a boolean value => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
    return node;
  }
  
  if (!truePart || !falsePart) {
    // true or false parts not defined
    //  should not happen but we must not continue in this case
    return node;
  }
    
  LOG_TRACE("optimised away ternary operation");
  
  // evaluate condition
  if (TRI_GetBooleanNodeValueAql(condition)) {
    // condition is true, replace with truePart
    return truePart;
  }
  
  // condition is true, replace with falsePart
  return falsePart;
}
void TRI_GlobalFreeStatementListAql (void) { 
  if (DummyNopNode != NULL) {
    TRI_DestroyVectorPointer(&DummyNopNode->_members);
    TRI_Free(TRI_UNKNOWN_MEM_ZONE, DummyNopNode);
  }
  
  if (DummyReturnEmptyNode != NULL) {
    TRI_aql_node_t* list = TRI_AQL_NODE_MEMBER(DummyReturnEmptyNode, 0);
    
    TRI_DestroyVectorPointer(&list->_members);
    TRI_Free(TRI_UNKNOWN_MEM_ZONE, list);

    TRI_DestroyVectorPointer(&DummyReturnEmptyNode->_members);
    TRI_Free(TRI_UNKNOWN_MEM_ZONE, DummyReturnEmptyNode);
  }
}
Beispiel #9
0
static TRI_aql_node_t* OptimiseFor (TRI_aql_statement_walker_t* const walker,
                                    TRI_aql_node_t* node) {
  TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 1);

  if (expression->_type == TRI_AQL_NODE_LIST) {
    // for statement with a list expression
    if (expression->_members._length == 0) {
      // list is empty => we can eliminate the for statement
      LOG_TRACE("optimised away empty for loop");

      return TRI_GetDummyReturnEmptyNodeAql();
    }
  }

  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 bool AppendListValues (TRI_string_buffer_t* const buffer,
                              const TRI_aql_node_t* const node,
                              convert_f func) {
  size_t i, n;

  n = node->_members._length;
  for (i = 0; i < n; ++i) {
    if (i > 0) {
      if (TRI_AppendStringStringBuffer(buffer, ", ") != TRI_ERROR_NO_ERROR) {
        return false;
      }
    }

    if (! func(buffer, TRI_AQL_NODE_MEMBER(node, i))) {
      return false;
    }
  }

  return true;
}
bool TRI_NodeJavascriptAql (TRI_string_buffer_t* const buffer,
                            const TRI_aql_node_t* const node) {
  switch (node->_type) {
    case TRI_AQL_NODE_VALUE:
      return TRI_ValueJavascriptAql(buffer, &node->_value, node->_value._type);
    case TRI_AQL_NODE_ARRAY_ELEMENT:
      if (! TRI_ValueJavascriptAql(buffer, &node->_value, TRI_AQL_TYPE_STRING)) {
        return false;
      }

      if (TRI_AppendCharStringBuffer(buffer, ':') != TRI_ERROR_NO_ERROR) {
        return false;
      }

      return TRI_NodeJavascriptAql(buffer, TRI_AQL_NODE_MEMBER(node, 0));
    case TRI_AQL_NODE_LIST:
      if (TRI_AppendCharStringBuffer(buffer, '[') != TRI_ERROR_NO_ERROR) {
        return false;
      }

      if (! AppendListValues(buffer, node, &TRI_NodeJavascriptAql)) {
        return false;
      }

      return (TRI_AppendCharStringBuffer(buffer, ']') == TRI_ERROR_NO_ERROR);
    case TRI_AQL_NODE_ARRAY:
      if (TRI_AppendCharStringBuffer(buffer, '{') != TRI_ERROR_NO_ERROR) {
        return false;
      }

      if (! AppendListValues(buffer, node, &TRI_NodeJavascriptAql)) {
        return false;
      }

      return (TRI_AppendCharStringBuffer(buffer, '}') == TRI_ERROR_NO_ERROR);
    default:
      return false;
  }
}
Beispiel #14
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);
      }
    }
  } 
}
Beispiel #15
0
bool TRI_ValidateArgsFunctionAql (TRI_aql_context_t* const context,
                                  const TRI_aql_function_t* const function,
                                  const TRI_aql_node_t* const parameters) {
  param_t allowed;
  const char* pattern;
  size_t i, n;
  bool eof = false;
  bool repeat = false;

  assert(function);
  assert(parameters);
  assert(parameters->_type == TRI_AQL_NODE_LIST);

  n = parameters->_members._length;

  // validate number of arguments
  if (n < function->_minArgs || n > function->_maxArgs) {
    // invalid number of arguments
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH, function->_externalName);
    return false;
  }

  pattern = function->_argPattern;

  // validate argument types
  for (i = 0; i < n; ++i) {
    TRI_aql_node_t* parameter = (TRI_aql_node_t*) TRI_AQL_NODE_MEMBER(parameters, i);
    bool parse = true;
    bool foundArg = false;

    if (repeat) {
      // last argument is repeated
      ARG_CHECK
    }
    else {
      // last argument is not repeated
      allowed = InitParam();

      foundArg = false;

      while (parse && !eof) {
        char c = *pattern++;
        
        switch (c) {
          case '\0':
            parse = false;
            eof = true;
            if (foundArg) {
              ARG_CHECK
            }
            break;
          case '|': // optional marker
            if (foundArg) {
              parse = false;
              ARG_CHECK
              if (*pattern == '+') {
                repeat = true;
                eof = true;
              }
            }
            break;
          case ',': // next argument
            assert(foundArg);
            parse = false;
            ARG_CHECK
            break;
          case '+': // repeat last argument
            repeat = true;
            parse = false;
            eof = true;
            ARG_CHECK
            break;
          case '.': // any type except collections
            allowed._list = true;
            allowed._array = true;
            // break intentionally missing!!
          case 'p': // primitive types
            allowed._null = true;
            allowed._bool = true;
            allowed._number = true;
            allowed._string = true;
            foundArg = true;
            break;
          case 'z': // null
            allowed._null = true;
            foundArg = true;
            break;
          case 'b': // bool
            allowed._bool = true;
            foundArg = true;
            break;
          case 'n': // number
            allowed._number = true;
            foundArg = true;
            break;
          case 's': // string
            allowed._string = true;
            foundArg = true;
            break;
          case 'l': // list
            allowed._list = true;
            foundArg = true;
            break;
          case 'a': // array
            allowed._array = true;
            foundArg = true;
            break;
          case 'c': // collection name => list
            allowed._collection = true;
            foundArg = true;
            break;
          case 'h': // collection name => string
            allowed._collection = true;
            foundArg = true;
            break;
        }
      }
    }

  }
Beispiel #16
0
static TRI_aql_node_t* OptimiseBinaryLogicalOperation (TRI_aql_context_t* const context,
                                                       TRI_aql_node_t* node) {
  TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
  TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
  bool isEligibleLhs;
  bool isEligibleRhs;
  bool lhsValue;

  if (!lhs || !rhs) {
    return node;
  } 

  isEligibleLhs = TRI_IsConstantValueNodeAql(lhs);
  isEligibleRhs = TRI_IsConstantValueNodeAql(rhs);

  if (isEligibleLhs && !TRI_IsBooleanValueNodeAql(lhs)) {
    // value type is not boolean => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
    return node;
  }

  if (isEligibleRhs && !TRI_IsBooleanValueNodeAql(rhs)) {
    // value type is not boolean => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
    return node;
  }

  if (!isEligibleLhs || !isEligibleRhs) {
    // node is not a constant value
    return node;
  }

  lhsValue = TRI_GetBooleanNodeValueAql(lhs);
  
  assert(node->_type == TRI_AQL_NODE_OPERATOR_BINARY_AND ||
         node->_type == TRI_AQL_NODE_OPERATOR_BINARY_OR);

  LOG_TRACE("optimised away binary logical operation");

  if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_AND) {
    if (lhsValue) {
      // if (true && rhs) => rhs
      return rhs;
    }

    // if (false && rhs) => false
    return lhs;
  } 
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_OR) {
    if (lhsValue) {
      // if (true || rhs) => true
      return lhs;
    }

    // if (false || rhs) => rhs
    return rhs;
  }

  assert(false);

  return node;
}
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 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);
        }
      }
    }
  }
}
Beispiel #20
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;
}
Beispiel #21
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);
  }
}
Beispiel #22
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;
}
Beispiel #23
0
static TRI_aql_node_t* OptimiseBinaryRelationalOperation (TRI_aql_context_t* const context,
                                                          TRI_aql_node_t* node) {
  TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
  TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
  TRI_js_exec_context_t* execContext;
  TRI_string_buffer_t* code;
  TRI_json_t* json;
  char* func;
  
  if (!lhs || !TRI_IsConstantValueNodeAql(lhs) || !rhs || !TRI_IsConstantValueNodeAql(rhs)) {
    return node;
  } 
  
  if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_EQ) {
    func = "EQUAL";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_NE) {
    func = "UNEQUAL";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_GT) {
    func = "GREATER";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_GE) {
    func = "GREATEREQUAL";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
    func = "LESS";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LE) {
    func = "LESSEQUAL";
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_IN) {
    func = "IN";
  }
  else {
    // not what we expected, however, simply continue
    return node;
  }
  
  code = RelationCode(func, lhs, rhs); 
  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) {
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_SCRIPT, NULL);
    return NULL;
  }
  
  // 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);
  }
    
  LOG_TRACE("optimised away binary relational operation");

  TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);

  return node;
}
Beispiel #24
0
static TRI_aql_node_t* OptimiseBinaryArithmeticOperation (TRI_aql_context_t* const context,
                                                          TRI_aql_node_t* node) {
  TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
  TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
  bool isEligibleLhs;
  bool isEligibleRhs;
  double value;
  
  if (!lhs || !rhs) {
    return node;
  } 

  isEligibleLhs = TRI_IsConstantValueNodeAql(lhs);
  isEligibleRhs = TRI_IsConstantValueNodeAql(rhs);

  if (isEligibleLhs && !TRI_IsNumericValueNodeAql(lhs)) {
    // node is not a numeric value => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
    return node;
  }

  
  if (isEligibleRhs && !TRI_IsNumericValueNodeAql(rhs)) {
    // node is not a numeric value => error
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
    return node;
  }
  

  if (!isEligibleLhs || !isEligibleRhs) {
    return node;
  }
  
  assert(node->_type == TRI_AQL_NODE_OPERATOR_BINARY_PLUS ||
         node->_type == TRI_AQL_NODE_OPERATOR_BINARY_MINUS ||
         node->_type == TRI_AQL_NODE_OPERATOR_BINARY_TIMES ||
         node->_type == TRI_AQL_NODE_OPERATOR_BINARY_DIV ||
         node->_type == TRI_AQL_NODE_OPERATOR_BINARY_MOD);

  if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_PLUS) {
    value = TRI_GetNumericNodeValueAql(lhs) + TRI_GetNumericNodeValueAql(rhs);
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_MINUS) {
    value = TRI_GetNumericNodeValueAql(lhs) - TRI_GetNumericNodeValueAql(rhs);
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_TIMES) {
    value = TRI_GetNumericNodeValueAql(lhs) * TRI_GetNumericNodeValueAql(rhs);
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_DIV) {
    if (TRI_GetNumericNodeValueAql(rhs) == 0.0) {
      // division by zero
      TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISION_BY_ZERO, NULL);
      return node;
    }
    value = TRI_GetNumericNodeValueAql(lhs) / TRI_GetNumericNodeValueAql(rhs);
  }
  else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_MOD) {
    if (TRI_GetNumericNodeValueAql(rhs) == 0.0) {
      // division by zero
      TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISION_BY_ZERO, NULL);
      return node;
    }
    value = fmod(TRI_GetNumericNodeValueAql(lhs), TRI_GetNumericNodeValueAql(rhs));
  }
  else {
    value = 0.0;
  }
  
  node = TRI_CreateNodeValueDoubleAql(context, value);

  if (!node) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    return NULL;
  }
  
  LOG_TRACE("optimised away binary arithmetic operation");

  return node;
}