Exemple #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;
}
Exemple #2
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;
}
Exemple #3
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;
}
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;
}
Exemple #5
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;
}
Exemple #6
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;
}
Exemple #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;
}
Exemple #8
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;
}
Exemple #9
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;
}
Exemple #10
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;
}
Exemple #11
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;
}