예제 #1
0
TRI_json_t* TRI_ExplainAql (TRI_aql_context_t* const context) {
  TRI_aql_statement_walker_t* walker;
  TRI_aql_explain_t* explain;
  TRI_json_t* result;

  explain = CreateExplain(context);

  if (explain == NULL) {
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return NULL;
  }

  walker = TRI_CreateStatementWalkerAql((void*) explain,
                                        false,
                                        NULL,
                                        &ProcessStatement,
                                        NULL);

  if (walker == NULL) {
    FreeExplain(explain);
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return NULL;
  }

  TRI_WalkStatementsAql(walker, context->_statements);
  result = explain->_result;

  FreeExplain(explain);

  TRI_FreeStatementWalkerAql(walker);

  return result;
}
예제 #2
0
bool TRI_InjectBindParametersAql (TRI_aql_context_t* const context) {
  TRI_aql_statement_walker_t* walker;

  assert(context);

  if (TRI_GetLengthAssociativePointer(&context->_parameters._names) == 0) {
    // no bind parameters used in query, instantly return
    return true;
  }

  walker = TRI_CreateStatementWalkerAql(context, true, &InjectParameter, NULL, NULL);

  if (walker == NULL) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return false;
  }

  TRI_WalkStatementsAql(walker, context->_statements);
  TRI_FreeStatementWalkerAql(walker);
  

  walker = TRI_CreateStatementWalkerAql(context, true, &FixAttributeAccess, NULL, NULL);

  if (walker == NULL) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return false;
  }

  TRI_WalkStatementsAql(walker, context->_statements);
  TRI_FreeStatementWalkerAql(walker);

  return true;
}
예제 #3
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;
}
예제 #4
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;
}
예제 #5
0
static TRI_aql_node_t* InjectParameter (TRI_aql_statement_walker_t* const walker,
                                        TRI_aql_node_t* node) {
  TRI_aql_bind_parameter_t* bind;
  TRI_associative_pointer_t* bindValues;
  TRI_aql_context_t* context;
  char* name;

  if (node == NULL || 
      node->_type != TRI_AQL_NODE_PARAMETER) {
    return node;
  }

  // we found a parameter node
  context = (TRI_aql_context_t*) walker->_data;
  assert(context);

  bindValues = (TRI_associative_pointer_t*) &context->_parameters._values;
  assert(bindValues);

  name = TRI_AQL_NODE_STRING(node);
  assert(name);

  bind = (TRI_aql_bind_parameter_t*) TRI_LookupByKeyAssociativePointer(bindValues, name);

  if (bind) {
    if (*name == '@') {
      // a collection name bind parameter
      if (TRI_IsStringJson(bind->_value)) {
        char* collectionName = TRI_RegisterStringAql(context,
                                                     bind->_value->_value._string.data,
                                                     bind->_value->_value._string.length - 1,
                                                     false);

        node = TRI_CreateNodeCollectionAql(context, collectionName);
      }
      else {
        TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, name);
        node = NULL;
      }
    }
    else {
      node = TRI_JsonNodeAql(context, bind->_value);
    }

    if (node == NULL) {
      TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETERS_INVALID, NULL);
    }
  }

  return node;
}
예제 #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;
}
예제 #7
0
bool TRI_VariableExistsScopeAql (TRI_aql_context_t* const context,
                                 const char* const name) {
  size_t n;

  if (name == NULL) {
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    return false;
  }

  n = context->_currentScopes._length;
  assert(n > 0);

  while (n > 0) {
    TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, --n);
    assert(scope);

    if (TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name)) {
      // duplicate variable
      return true;
    }

    if (n == 0) {
      // reached the outermost scope
      break;
    }
  }

  return false;
}
예제 #8
0
bool TRI_AddParameterValuesAql (TRI_aql_context_t* const context,
                                const TRI_json_t* const parameters) {
  size_t i;
  size_t n;

  assert(context);

  if (parameters == NULL) {
    // no bind parameters, direclty return
    return true;
  }

  if (parameters->_type != TRI_JSON_ARRAY) {
    // parameters must be a list
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETERS_INVALID, NULL);
    return false;
  }

  n = parameters->_value._objects._length;
  if (n == 0) {
    // empty list, this is ok
    return true;
  }

  for (i = 0; i < n; i += 2) {
    TRI_json_t* name = TRI_AtVector(&parameters->_value._objects, i);
    TRI_json_t* value = TRI_AtVector(&parameters->_value._objects, i + 1);
    TRI_aql_bind_parameter_t* parameter;

    assert(TRI_IsStringJson(name));
    assert(value);

    parameter = CreateParameter(name->_value._string.data, 
                                name->_value._string.length - 1,
                                value);

    if (parameter == NULL) {
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);

      return false;
    }

    TRI_InsertKeyAssociativePointer(&context->_parameters._values, parameter->_name, parameter, false);
  }

  return true;
}
예제 #9
0
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;
}
예제 #10
0
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;
}
예제 #11
0
bool TRI_ValidateBindParametersAql (TRI_aql_context_t* const context) {
  size_t i;
  size_t n;

  // iterate thru all parameter names used in the query
  n = context->_parameters._names._nrAlloc;
  for (i = 0; i < n; ++i) {
    char* name = (char*) context->_parameters._names._table[i];

    if (!name) {
      continue;
    }

    if (!TRI_LookupByKeyAssociativePointer(&context->_parameters._values, name)) {
      TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_MISSING, name);
      return false;
    }
  }

  // iterate thru all parameters that we have values for
  n = context->_parameters._values._nrAlloc;
  for (i = 0; i < n; ++i) {
    TRI_aql_bind_parameter_t* parameter;

    parameter = (TRI_aql_bind_parameter_t*) context->_parameters._values._table[i];

    if (!parameter) {
      continue;
    }

    assert(parameter->_name);

    if (!TRI_LookupByKeyAssociativePointer(&context->_parameters._names, parameter->_name)) {
      TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_BIND_PARAMETER_UNDECLARED, parameter->_name);
      return false;
    }
  }

  return true;
}
예제 #12
0
bool TRI_PushStackParseAql (TRI_aql_context_t* const context,
                            const void* const value) {
  assert(context);

  if (value == NULL) {
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL); 

    return false;
  }

  TRI_PushBackVectorPointer(&context->_parser->_stack, (void*) value);

  return true;
}
예제 #13
0
bool TRI_OptimiseAql (TRI_aql_context_t* const context) {
  aql_optimiser_t* optimiser;
  bool result;
  
  optimiser = CreateOptimiser(context);
  if (optimiser == NULL) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    return false;
  }

  result = (OptimiseAst(optimiser) && DetermineIndexes(optimiser));
  FreeOptimiser(optimiser);

  return result;
}
예제 #14
0
bool TRI_AddCollectionAql (TRI_aql_context_t* const context, const char* const name) {
  assert(context);
  assert(name);

  // duplicates are not a problem here, we simply ignore them
  TRI_InsertKeyAssociativePointer(&context->_collectionNames, name, (void*) name, false);

  if (context->_collectionNames._nrUsed > AQL_MAX_COLLECTIONS) {
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_TOO_MANY_COLLECTIONS, NULL);

    return false;
  }

  return true;
}
예제 #15
0
void TRI_SetErrorParseAql (TRI_aql_context_t* const context,
                           const char* const message,
                           const int line,
                           const int column) {
  char buffer[256];
  char* region;

  assert(context);
  assert(context->_query);
  assert(message);

  region = TRI_GetContextErrorAql(context->_query, 
                                  context->_parser->_queryLength, 
                                  (size_t) line, 
                                  (size_t) column);

  if (region == NULL) {
    // OOM
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return;
  }

  // note: line numbers reported by bison/flex start at 1, columns start at 0
  snprintf(buffer,
           sizeof(buffer),
           "%s near '%s' at position %d:%d",
           message,
           region,
           line,
           column + 1);

  TRI_Free(TRI_UNKNOWN_MEM_ZONE, region);

  TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_QUERY_PARSE, buffer);
}
예제 #16
0
TRI_aql_for_hint_t* TRI_CreateForHintScopeAql (TRI_aql_context_t* const context) {
  TRI_aql_for_hint_t* hint;

  assert(context);

  hint = (TRI_aql_for_hint_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_for_hint_t), false);
  if (hint == NULL) {
    TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    return NULL;
  }

  // initialise
  hint->_limit._offset = 0;
  hint->_limit._limit  = 0;
  hint->_limit._status = TRI_AQL_LIMIT_UNDEFINED;
  hint->_isIncremental = false;

  return hint;
}
예제 #17
0
bool TRI_ValidateQueryContextAql (TRI_aql_context_t* const context) {
  if (context->_parser->_length == 0) {
    // query is empty, no need to parse it
    TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_EMPTY, NULL);
    return false;
  }

  // parse the query
  if (! TRI_ParseAql(context)) {
    // lexing/parsing failed
    return false;
  }

  if (context->_error._code) {
    return false;
  }

  return true;
}
예제 #18
0
static bool OptimiseAst (aql_optimiser_t* const optimiser) {
  TRI_aql_statement_walker_t* walker;

  walker = TRI_CreateStatementWalkerAql((void*) optimiser, 
                                        true,
                                        &OptimiseNode,
                                        NULL, 
                                        &ProcessStatement);
  if (walker == NULL) {
    TRI_SetErrorContextAql(optimiser->_context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return false;
  }

  TRI_WalkStatementsAql(walker, optimiser->_context->_statements); 

  TRI_FreeStatementWalkerAql(walker);

  return true;
}
예제 #19
0
static bool DetermineIndexes (aql_optimiser_t* const optimiser) {
  TRI_aql_statement_walker_t* walker;

  walker = TRI_CreateStatementWalkerAql((void*) optimiser, 
                                        false,
                                        &AnnotateNode, 
                                        NULL, 
                                        NULL);
  if (walker == NULL) {
    TRI_SetErrorContextAql(optimiser->_context, TRI_ERROR_OUT_OF_MEMORY, NULL);

    return false;
  }

  TRI_WalkStatementsAql(walker, optimiser->_context->_statements); 

  TRI_FreeStatementWalkerAql(walker);

  return true;
}
예제 #20
0
bool SetupCollections (TRI_aql_context_t* const context) {
  size_t i;
  size_t n;
  bool result = true;

  // each collection used is contained once in the assoc. array
  // so we do not have to care about duplicate names here
  n = context->_collectionNames._nrAlloc;
  for (i = 0; i < n; ++i) {
    char* name = context->_collectionNames._table[i];
    TRI_aql_collection_t* collection;

    if (! name) {
      continue;
    }

    collection = CreateCollectionContainer(name);
    if (! collection) {
      result = false;
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
      break;
    }

    TRI_PushBackVectorPointer(&context->_collections, (void*) collection);
  }

  if (result && n > 0) {
    qsort(context->_collections._buffer,
          context->_collections._length,
          sizeof(void*),
          &CollectionNameComparator);
  }

  // now collections contains the sorted list of collections

  return result;
}
예제 #21
0
bool TRI_AddBarrierCollectionsAql (TRI_aql_context_t* const context) {
  size_t i;
  size_t n;
  bool result = true;

  // iterate in forward order
  n = context->_collections._length;
  for (i = 0; i < n; ++i) {
    TRI_barrier_t* ce;

    TRI_aql_collection_t* collection = (TRI_aql_collection_t*) context->_collections._buffer[i];
    TRI_primary_collection_t* primaryCollection;

    assert(collection);
    assert(collection->_name);
    assert(collection->_collection);
    assert(collection->_collection->_collection);
    assert(! collection->_barrier);

    primaryCollection = (TRI_primary_collection_t*) collection->_collection->_collection;

    LOG_TRACE("adding barrier for collection '%s'", collection->_name);

    ce = TRI_CreateBarrierElement(&primaryCollection->_barrierList);
    if (!ce) {
      // couldn't create the barrier
      result = false;
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
      break;
    }
    else {
      collection->_barrier = ce;
    }
  }

  return result;
}
예제 #22
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;
}
예제 #23
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;
}
예제 #24
0
bool OpenCollections (TRI_aql_context_t* const context) {
  size_t i;
  size_t n;

  n = context->_collections._length;
  for (i = 0; i < n; ++i) {
    TRI_aql_collection_t* collection = context->_collections._buffer[i];

    assert(collection);
    assert(collection->_name);
    assert(! collection->_collection);
    assert(! collection->_barrier);

    LOG_TRACE("locking collection '%s'", collection->_name);
    collection->_collection = TRI_UseCollectionByNameVocBase(context->_vocbase, collection->_name);
    if (collection->_collection == NULL) {
      TRI_SetErrorContextAql(context, TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, collection->_name);

      return false;
    }
  }

  return true;
}
예제 #25
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;
        }
      }
    }

  }
예제 #26
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;
}
예제 #27
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;
}
예제 #28
0
static TRI_aql_index_t* PickIndex (TRI_aql_context_t* const context,
                                   TRI_aql_index_t* pickedIndex,
                                   const TRI_index_t* const idx,
                                   TRI_vector_pointer_t* fieldAccesses) {
  bool isBetter = false;

  assert(idx);
  assert(fieldAccesses);

  if (pickedIndex == NULL) {
    pickedIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_index_t), false);

    if (pickedIndex == NULL) {
      // OOM
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
      return NULL;
    }

    pickedIndex->_idx = NULL;
    pickedIndex->_fieldAccesses = NULL;
  }

  if (pickedIndex == NULL) {
    // OOM
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
    return NULL;
  }


  // ...........................................................................
  // If we do not have an index yet, then this index will do. As has been said
  // before 'any index is better than none'
  // ...........................................................................

  if (pickedIndex->_idx == NULL) {
    pickedIndex->_idx = (TRI_index_t*) idx;
    pickedIndex->_fieldAccesses = TRI_CopyVectorPointer(TRI_UNKNOWN_MEM_ZONE, fieldAccesses);

    if (pickedIndex->_fieldAccesses == NULL) {
      TRI_Free(TRI_UNKNOWN_MEM_ZONE, pickedIndex);
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
      return NULL;
    }

    return pickedIndex;
  }


  // ...........................................................................
  // We have previously selected an index, if it happens to be the primary then
  // we stick with it.
  // ...........................................................................

  if (pickedIndex->_idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) {
    return pickedIndex;
  }


  // ...........................................................................
  // Now go through the various possibilities if we have not located something
  // better.
  // ...........................................................................

  if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) ) {
    // .........................................................................
    // If we can used the primary index, then this is better than any other
    // index so use it.
    // .........................................................................
    isBetter = true;
  }


  if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_HASH_INDEX) ) {
    // .........................................................................
    // If the index type is a hash index, use this -- but only if we have NOT
    // located something better BEFORE.
    // .........................................................................
    isBetter = true;
  }


  if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) &&
       (pickedIndex->_idx->_type != TRI_IDX_TYPE_HASH_INDEX) ) {
    // .........................................................................
    // If the index type is a skiplist index, use this -- but only if we have NOT
    // located something better BEFORE.
    // .........................................................................
    isBetter = true;
  }


  if ( (isBetter == false) && (idx->_type == TRI_IDX_TYPE_BITARRAY_INDEX) &&
       (pickedIndex->_idx->_type != TRI_IDX_TYPE_HASH_INDEX)              &&
       (pickedIndex->_idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) ) {
    // .........................................................................
    // If the index type is a bitarray index, use this -- but only if we have NOT
    // located something better BEFORE.
    // .........................................................................
    isBetter = true;
  }


  if ( (isBetter == false) && (idx->_unique == true) && (pickedIndex->_idx->_unique == false) ) {
    // .........................................................................
    // If the index is a unique one and the picked index is non-unique, then
    // replace it with the unique overriding the preferences above. E.g. if
    // we have a non-unique hash index (which we have chosen) and now we are
    // testing a unique skiplist, replace it with the skiplist.
    // .........................................................................
    isBetter = true;
  }


  if ( (isBetter == false) &&
       (fieldAccesses->_length < pickedIndex->_fieldAccesses->_length ) &&
       (idx->_unique == true) ) {
    isBetter = true;
  }


  if ( (isBetter == false) &&
       (fieldAccesses->_length >  pickedIndex->_fieldAccesses->_length ) &&
       (idx->_unique == false) ) {
    isBetter = true;
  }


  if (isBetter) {
    if (pickedIndex->_fieldAccesses != NULL) {
      TRI_FreeVectorPointer(TRI_UNKNOWN_MEM_ZONE, pickedIndex->_fieldAccesses);
    }

    pickedIndex->_idx = (TRI_index_t*) idx;

    pickedIndex->_fieldAccesses = TRI_CopyVectorPointer(TRI_UNKNOWN_MEM_ZONE, fieldAccesses);
    
    if (pickedIndex->_fieldAccesses == NULL) {
      TRI_Free(TRI_UNKNOWN_MEM_ZONE, pickedIndex);
      TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
      return NULL;
    }
  }

  return pickedIndex;
}
예제 #29
0
TRI_aql_node_t* TRI_JsonNodeAql (TRI_aql_context_t* const context,
                                 const TRI_json_t* const json) {
  TRI_aql_node_t* node = NULL;
  char* value;

  switch (json->_type) {
    case TRI_JSON_UNUSED:
      break;

    case TRI_JSON_NULL:
      node = TRI_CreateNodeValueNullAql(context);
      break;

    case TRI_JSON_BOOLEAN:
      node = TRI_CreateNodeValueBoolAql(context, json->_value._boolean);
      break;

    case TRI_JSON_NUMBER:
      node = TRI_CreateNodeValueDoubleAql(context, json->_value._number);
      break;

    case TRI_JSON_STRING:
      value = TRI_RegisterStringAql(context, json->_value._string.data, strlen(json->_value._string.data), true);
      node = TRI_CreateNodeValueStringAql(context, value);
      break;

    case TRI_JSON_LIST: {
      size_t i;
      size_t n;

      node = TRI_CreateNodeListAql(context);
      n = json->_value._objects._length;

      for (i = 0; i < n; ++i) {
        TRI_json_t* subJson;
        TRI_aql_node_t* member;

        subJson = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i);
        assert(subJson);

        member = TRI_JsonNodeAql(context, subJson);
        if (member) {
          TRI_PushBackVectorPointer(&node->_members, (void*) member);
        }
        else {
          TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
          return NULL;
        }
      }
      break;
    }
    case TRI_JSON_ARRAY: {
      size_t i;
      size_t n;

      node = TRI_CreateNodeArrayAql(context);
      n = json->_value._objects._length;

      for (i = 0; i < n; i += 2) {
        TRI_json_t* nameJson;
        TRI_json_t* valueJson;
        TRI_aql_node_t* member;
        TRI_aql_node_t* valueNode;
        char* name;

        // json_t containing the array element name
        nameJson = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i);
        assert(nameJson);
        assert(nameJson->_value._string.data);
        name = TRI_RegisterStringAql(context, nameJson->_value._string.data, strlen(nameJson->_value._string.data), false);
        if (! name) {
          TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
          return NULL;
        }

        // json_t containing the array element value
        valueJson = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1);
        assert(valueJson);

        valueNode = TRI_JsonNodeAql(context, valueJson);
        if (! valueNode) {
          TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
          return NULL;
        }

        member = TRI_CreateNodeArrayElementAql(context, name, valueNode);
        if (member) {
          TRI_PushBackVectorPointer(&node->_members, (void*) member);
        }
        else {
          TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
          return NULL;
        }
      }
      break;
    }
  }

  if (! node) {
    TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
  }

  return node;
}
예제 #30
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);
      }
    }
  } 
}