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* 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; }
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; }
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; }
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; }
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_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* 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; }
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; }