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; }
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; }
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; }
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; }
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; }
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; }
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; }
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(¶meters->_value._objects, i); TRI_json_t* value = TRI_AtVector(¶meters->_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; }
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 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; }
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; }
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; }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; } } } }
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; }
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; }
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; }
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; }
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); } } } }