//////////////////////////////////////////////////////////////////////////////// /// @brief Ahuacatl, constant folding /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2012, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Ahuacatl/ahuacatl-constant-folder.h" #include "Ahuacatl/ahuacatl-conversions.h" #include "Ahuacatl/ahuacatl-functions.h" #include "V8/v8-execution.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief hash a variable //////////////////////////////////////////////////////////////////////////////// static uint64_t HashFieldAccess (TRI_associative_pointer_t* array, void const* element) { TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) element; return TRI_FnvHashString(fieldAccess->_fieldName); } //////////////////////////////////////////////////////////////////////////////// /// @brief comparison function used to determine variable equality //////////////////////////////////////////////////////////////////////////////// static bool EqualFieldAccess (TRI_associative_pointer_t* array, void const* key, void const* element) { TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) element; return TRI_EqualString(key, fieldAccess->_fieldName); } static int TypeWeight (const TRI_json_t* const value) { switch (value->_type) { case TRI_JSON_BOOLEAN: return 1; case TRI_JSON_NUMBER: return 2; case TRI_JSON_STRING: return 3; case TRI_JSON_LIST: return 4; case TRI_JSON_ARRAY: return 5; case TRI_JSON_NULL: default: return 0; } } static int Compare (const TRI_json_t* const lhs, const TRI_json_t* const rhs) { int lWeight = TypeWeight(lhs); int rWeight = TypeWeight(rhs); if (lWeight < rWeight) { return -1; } if (lWeight > rWeight) { return 1; } // equal weight switch (lhs->_type) { case TRI_JSON_NULL: return 0; // null == null; case TRI_JSON_BOOLEAN: if (lhs->_value._boolean == rhs->_value._boolean) { return 0; } if (!lhs->_value._boolean && rhs->_value._boolean) { return -1; } return 1; case TRI_JSON_NUMBER: if (lhs->_value._number == rhs->_value._number) { return 0; } if (lhs->_value._number < rhs->_value._number) { return -1; } return 1; case TRI_JSON_STRING: return strcmp(lhs->_value._string.data, rhs->_value._string.data); case TRI_JSON_LIST: { size_t nl = lhs->_value._objects._length; size_t nr = rhs->_value._objects._length; size_t i; for (i = 0; i < nl; ++i) { int result; if (i >= nr) { // left list is longer return 1; } result = Compare(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i)); if (result != 0) { return result; } } // right list is longer if (nr > nl) { return -1; } return 0; } case TRI_JSON_ARRAY: { size_t nl = lhs->_value._objects._length; size_t nr = rhs->_value._objects._length; size_t i; for (i = 0; i < nl; i += 2) { int result; if (i > nr) { // left list is longer return 1; } // compare key result = Compare(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i)); if (result != 0) { return result; } // compare value result = Compare(TRI_AtVector(&lhs->_value._objects, i + 1), TRI_AtVector(&rhs->_value._objects, i + 1)); if (result != 0) { return result; } } // right list is longer if (nr > nl) { return -1; } return 0; } default: return 0; } } static bool IsSameValue (const TRI_json_t* const lhs, const TRI_json_t* const rhs) { if (!lhs || !rhs) { return false; } if (lhs->_type != rhs->_type) { return false; } switch (lhs->_type) { case TRI_JSON_NULL: return true; case TRI_JSON_BOOLEAN: return (lhs->_value._boolean == rhs->_value._boolean); case TRI_JSON_NUMBER: return (lhs->_value._number == rhs->_value._number); // TODO: handle epsilon case TRI_JSON_STRING: return TRI_EqualString(lhs->_value._string.data, rhs->_value._string.data); case TRI_JSON_LIST: case TRI_JSON_ARRAY: { // lists and arrays can be treated the same here size_t n = lhs->_value._objects._length; size_t i; if (n != rhs->_value._objects._length) { return false; } for (i = 0; i < n; ++i) { if (!IsSameValue(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i))) { return false; } } return true; } default: return false; } } static bool IsInList (const TRI_json_t* const lhs, const TRI_json_t* const rhs) { size_t n; size_t i; if (!lhs || !rhs || !rhs->_type == TRI_JSON_LIST) { return false; } n = rhs->_value._objects._length; for (i = 0; i < n; ++i) { TRI_json_t* r = (TRI_json_t*) TRI_AtVector(&rhs->_value._objects, i); if (IsSameValue(lhs, r)) { return true; } } return false; } static char* AccessName (const TRI_aql_access_e type) { switch (type) { case TRI_AQL_ACCESS_ALL: return "all"; case TRI_AQL_ACCESS_IMPOSSIBLE: return "impossible"; case TRI_AQL_ACCESS_EXACT: return "exact"; case TRI_AQL_ACCESS_LIST: return "list"; case TRI_AQL_ACCESS_SINGLE_RANGE: return "single range"; case TRI_AQL_ACCESS_DOUBLE_RANGE: return "double range"; default: return "unknown"; } } static TRI_aql_field_access_t* MergeAccess (TRI_aql_context_t* const context, TRI_aql_field_access_t* lhs, TRI_aql_field_access_t* rhs) { assert(lhs); assert(rhs); assert(lhs->_fieldName != NULL); assert(rhs->_fieldName != NULL); if (lhs->_type > rhs->_type) { // swap operands so they are always sorted TRI_aql_field_access_t* tmp = lhs; lhs = rhs; rhs = tmp; } if (lhs->_type == TRI_AQL_ACCESS_ALL) { // TODO: free lhs return rhs; } if (lhs->_type == TRI_AQL_ACCESS_IMPOSSIBLE) { // TODO: free rhs return lhs; } if (lhs->_type == TRI_AQL_ACCESS_EXACT) { if (rhs->_type == TRI_AQL_ACCESS_EXACT) { if (!IsSameValue(lhs->_value._exactValue, rhs->_value._exactValue)) { // lhs and rhs values are non-identical lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue); // TODO: free rhs return lhs; } // lhs and rhs values are identical // TODO: free rhs return lhs; } else if (rhs->_type == TRI_AQL_ACCESS_LIST) { if (!IsInList(lhs->_value._exactValue, rhs->_value._list)) { // lhs value is not in rhs list lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue); // TODO: free rhs return lhs; } // lhs value is contained in rhs // TODO: free rhs return lhs; } else if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { // check if value is in range int result = Compare(lhs->_value._exactValue, rhs->_value._singleRange._value); bool contained = ((rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_EXCLUDED && result > 0) || (rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED && result >= 0) || (rhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_EXCLUDED && result < 0) || (rhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_INCLUDED && result <= 0)); if (!contained) { // lhs value is not contained in rhs range lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue); // TODO: free rhs return lhs; } // lhs value is contained in rhs range // TODO: free lhs return rhs; } else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { // check if value is in range int result; bool contained; result = Compare(lhs->_value._exactValue, rhs->_value._between._lower._value); contained = ((rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_EXCLUDED && result > 0) || (rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_INCLUDED && result >= 0)); if (!contained) { // lhs value is not contained in rhs range lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue); // TODO: free rhs return lhs; } result = Compare(lhs->_value._exactValue, rhs->_value._between._upper._value); contained = ((rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_EXCLUDED && result < 0) || (rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_INCLUDED && result <= 0)); if (!contained) { // lhs value is not contained in rhs range lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue); // TODO: free rhs return lhs; } // lhs value is contained in rhs range // TODO: free lhs return rhs; } } if (lhs->_type == TRI_AQL_ACCESS_LIST) { if (rhs->_type == TRI_AQL_ACCESS_LIST) { // make a list of both } else if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { // check if value in range } else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { // check if value in range } } if (lhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { // check if value in range } else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { // check if value in range } } if (lhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { } return NULL; } static TRI_aql_field_access_t* CreateAccess (TRI_aql_context_t* const context, const TRI_aql_field_name_t* const field, const TRI_aql_node_type_e operator, const TRI_aql_node_t* const node) { TRI_aql_field_access_t* fieldAccess; TRI_json_t* value; value = TRI_NodeJsonAql(context, node); if (!value) { return NULL; } fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false); if (fieldAccess == NULL) { return NULL; } fieldAccess->_fieldName = TRI_DuplicateString(field->_name._buffer); if (fieldAccess->_fieldName == NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess); return NULL; } if (operator == AQL_NODE_OPERATOR_BINARY_EQ) { fieldAccess->_type = TRI_AQL_ACCESS_EXACT; fieldAccess->_value._exactValue = value; } else if (operator == AQL_NODE_OPERATOR_BINARY_LT) { fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE; fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_EXCLUDED; fieldAccess->_value._singleRange._value = value; } else if (operator == AQL_NODE_OPERATOR_BINARY_LE) { fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE; fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_INCLUDED; fieldAccess->_value._singleRange._value = value; } else if (operator == AQL_NODE_OPERATOR_BINARY_GT) { fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE; fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_EXCLUDED; fieldAccess->_value._singleRange._value = value; } else if (operator == AQL_NODE_OPERATOR_BINARY_GE) { fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE; fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_INCLUDED; fieldAccess->_value._singleRange._value = value; } else { assert(false); } return fieldAccess; } static void CreateFieldAccess (TRI_aql_context_t* const context, const TRI_aql_field_name_t* const field, const TRI_aql_node_type_e operator, const TRI_aql_node_t* const node) { TRI_aql_field_access_t* previous; TRI_aql_field_access_t* fieldAccess; if (!field || !field->_name._buffer) { return; } fieldAccess = CreateAccess(context, field, operator, node); if (!fieldAccess) { // TODO: mark failure return; } previous = (TRI_aql_field_access_t*) TRI_LookupByKeyAssociativePointer(&context->_ranges, field->_name._buffer); if (previous) { printf("ENTER MERGE\n"); TRI_aql_field_access_t* merged = MergeAccess(context, fieldAccess, previous); printf("LEAVE MERGE\n"); // TODO: // free previous // free fieldAccess if (merged) { TRI_InsertKeyAssociativePointer(&context->_ranges, fieldAccess->_fieldName, merged, true); printf("MERGE1\n"); } // previous access exists, must merge // merge(previous, new) // TRI_InsertKeyAssociativePointer(&context } else { // no previous access exists, no need to merge TRI_InsertKeyAssociativePointer(&context->_ranges, fieldAccess->_fieldName, fieldAccess, false); printf("INSERT1\n"); } } //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// static TRI_aql_field_name_t* GetFieldName (TRI_aql_context_t* const context, const TRI_aql_node_t* const node) { if (node->_type == AQL_NODE_ATTRIBUTE_ACCESS) { TRI_aql_field_name_t* field = GetFieldName(context, TRI_AQL_NODE_MEMBER(node, 0)); if (!field) { return NULL; } TRI_AppendCharStringBuffer(&field->_name, '.'); TRI_AppendStringStringBuffer(&field->_name, TRI_AQL_NODE_STRING(node)); return field; } else if (node->_type == AQL_NODE_REFERENCE) { TRI_aql_field_name_t* field; field = (TRI_aql_field_name_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_name_t), false); if (!field) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } field->_variable = TRI_AQL_NODE_STRING(node); TRI_InitStringBuffer(&field->_name, TRI_UNKNOWN_MEM_ZONE); TRI_AppendStringStringBuffer(&field->_name, TRI_AQL_NODE_STRING(node)); return field; } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief inspect a filter expression //////////////////////////////////////////////////////////////////////////////// static void InspectFilter (TRI_aql_context_t* const context, TRI_aql_node_t* node) { printf("ITERATION SOMETHING %s\n", TRI_NodeNameAql(node->_type)); if (node->_type == AQL_NODE_OPERATOR_UNARY_NOT) { return; } if (node->_type == AQL_NODE_OPERATOR_BINARY_OR) { TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1); InspectFilter(context, lhs); InspectFilter(context, rhs); } if (node->_type == AQL_NODE_OPERATOR_BINARY_AND) { TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1); InspectFilter(context, lhs); InspectFilter(context, rhs); } if (node->_type == AQL_NODE_OPERATOR_BINARY_EQ || // node->_type == AQL_NODE_OPERATOR_BINARY_NE || node->_type == AQL_NODE_OPERATOR_BINARY_LT || node->_type == AQL_NODE_OPERATOR_BINARY_LE || node->_type == AQL_NODE_OPERATOR_BINARY_GT || node->_type == AQL_NODE_OPERATOR_BINARY_GE) { TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1); if (lhs->_type == AQL_NODE_ATTRIBUTE_ACCESS) { TRI_aql_field_name_t* field = GetFieldName(context, lhs); if (field) { CreateFieldAccess(context, field, node->_type, rhs); TRI_DestroyStringBuffer(&field->_name); TRI_Free(TRI_UNKNOWN_MEM_ZONE, field); } } else if (lhs->_type == AQL_NODE_ATTRIBUTE_ACCESS) { TRI_aql_field_name_t* field = GetFieldName(context, rhs); if (field) { CreateFieldAccess(context, field, node->_type, lhs); TRI_DestroyStringBuffer(&field->_name); TRI_Free(TRI_UNKNOWN_MEM_ZONE, field); } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief create javascript function code for a relational operation //////////////////////////////////////////////////////////////////////////////// static TRI_string_buffer_t* RelationCode (const char* const name, const TRI_aql_node_t* const lhs, const TRI_aql_node_t* const rhs) { TRI_string_buffer_t* buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE); if (!lhs || !rhs) { return NULL; } if (TRI_AppendStringStringBuffer(buffer, "(function(){return AHUACATL_RELATIONAL_") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendStringStringBuffer(buffer, name) != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendStringStringBuffer(buffer, "(") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (!TRI_NodeJavascriptAql(buffer, lhs)) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendCharStringBuffer(buffer, ',') != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (!TRI_NodeJavascriptAql(buffer, rhs)) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendStringStringBuffer(buffer, ");})") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } return buffer; } //////////////////////////////////////////////////////////////////////////////// /// @brief create javascript function code for a function call //////////////////////////////////////////////////////////////////////////////// static TRI_string_buffer_t* FcallCode (const char* const name, const TRI_aql_node_t* const args) { TRI_string_buffer_t* buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE); size_t i; size_t n; if (!buffer) { return NULL; } if (TRI_AppendStringStringBuffer(buffer, "(function(){return AHUACATL_FCALL(") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendStringStringBuffer(buffer, name) != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } if (TRI_AppendStringStringBuffer(buffer, ",[") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } n = args->_members._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* arg = (TRI_aql_node_t*) args->_members._buffer[i]; if (i > 0) { if (TRI_AppendCharStringBuffer(buffer, ',') != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } } if (!TRI_NodeJavascriptAql(buffer, arg)) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } } if (TRI_AppendStringStringBuffer(buffer, "]);})") != TRI_ERROR_NO_ERROR) { TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer); return NULL; } return buffer; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a function call //////////////////////////////////////////////////////////////////////////////// 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) { 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); } TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); LOG_TRACE("optimised function call"); return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a sort expression //////////////////////////////////////////////////////////////////////////////// static TRI_aql_node_t* OptimiseSort (TRI_aql_context_t* const context, 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 NULL; } return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a filter expression //////////////////////////////////////////////////////////////////////////////// static TRI_aql_node_t* OptimiseFilter (TRI_aql_context_t* const context, TRI_aql_node_t* node) { TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0); bool result; if (!expression) { return node; } if (!TRI_IsConstantValueNodeAql(expression)) { //InspectFilter(context, expression); return node; } result = TRI_GetBooleanNodeValueAql(expression); if (result) { // filter expression is always true => remove it LOG_TRACE("optimised away constant filter"); return NULL; } return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise an arithmetic operation with one operand //////////////////////////////////////////////////////////////////////////////// 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); if (!operand || !TRI_IsConstantValueNodeAql(operand)) { return node; } if (!TRI_IsNumericValueNodeAql(operand)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL); return node; } assert(node->_type == AQL_NODE_OPERATOR_UNARY_PLUS || node->_type == AQL_NODE_OPERATOR_UNARY_MINUS); if (node->_type == AQL_NODE_OPERATOR_UNARY_PLUS) { // + number => number node = operand; } else if (node->_type == AQL_NODE_OPERATOR_UNARY_MINUS) { // - number => eval! node = TRI_CreateNodeValueDoubleAql(context, - TRI_GetNumericNodeValueAql(operand)); if (!node) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); } } return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a boolean operation with one operand //////////////////////////////////////////////////////////////////////////////// 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); if (!operand || !TRI_IsConstantValueNodeAql(operand)) { return node; } if (!TRI_IsBooleanValueNodeAql(operand)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL); return node; } assert(node->_type == AQL_NODE_OPERATOR_UNARY_NOT); if (node->_type == AQL_NODE_OPERATOR_UNARY_NOT) { // ! bool => evaluate 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; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a boolean operation with two operands //////////////////////////////////////////////////////////////////////////////// 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)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL); return node; } if (isEligibleRhs && !TRI_IsBooleanValueNodeAql(rhs)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL); return node; } if (!isEligibleLhs || !isEligibleRhs) { return node; } lhsValue = TRI_GetBooleanNodeValueAql(lhs); assert(node->_type == AQL_NODE_OPERATOR_BINARY_AND || node->_type == AQL_NODE_OPERATOR_BINARY_OR); LOG_TRACE("optimised away binary logical operation"); if (node->_type == AQL_NODE_OPERATOR_BINARY_AND) { if (lhsValue) { // if (true && rhs) => rhs return rhs; } // if (false && rhs) => false return lhs; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_OR) { if (lhsValue) { // if (true || rhs) => true return lhs; } // if (false || rhs) => rhs return rhs; } assert(false); return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise a relational operation with two operands //////////////////////////////////////////////////////////////////////////////// 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 == AQL_NODE_OPERATOR_BINARY_EQ) { func = "EQUAL"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_NE) { func = "UNEQUAL"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_GT) { func = "GREATER"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_GE) { func = "GREATER_EQUAL"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_LT) { func = "LESS"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_LE) { func = "LESS_EQUAL"; } else if (node->_type == AQL_NODE_OPERATOR_BINARY_IN) { func = "IN"; } 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"); return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimise an arithmetic operation with two operands //////////////////////////////////////////////////////////////////////////////// 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)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL); return node; } if (isEligibleRhs && !TRI_IsNumericValueNodeAql(rhs)) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL); return node; } if (!isEligibleLhs || !isEligibleRhs) { return node; } assert(node->_type == AQL_NODE_OPERATOR_BINARY_PLUS || node->_type == AQL_NODE_OPERATOR_BINARY_MINUS || node->_type == AQL_NODE_OPERATOR_BINARY_TIMES || node->_type == AQL_NODE_OPERATOR_BINARY_DIV || node->_type == AQL_NODE_OPERATOR_BINARY_MOD); if (node->_type == AQL_NODE_OPERATOR_BINARY_PLUS) { value = TRI_GetNumericNodeValueAql(lhs) + TRI_GetNumericNodeValueAql(rhs); } else if (node->_type == AQL_NODE_OPERATOR_BINARY_MINUS) { value = TRI_GetNumericNodeValueAql(lhs) - TRI_GetNumericNodeValueAql(rhs); } else if (node->_type == AQL_NODE_OPERATOR_BINARY_TIMES) { value = TRI_GetNumericNodeValueAql(lhs) * TRI_GetNumericNodeValueAql(rhs); } else if (node->_type == AQL_NODE_OPERATOR_BINARY_DIV) { if (TRI_GetNumericNodeValueAql(rhs) == 0.0) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISON_BY_ZERO, NULL); return node; } value = TRI_GetNumericNodeValueAql(lhs) / TRI_GetNumericNodeValueAql(rhs); } else if (node->_type == AQL_NODE_OPERATOR_BINARY_MOD) { if (TRI_GetNumericNodeValueAql(rhs) == 0.0) { TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISON_BY_ZERO, NULL); return node; } value = fmod(TRI_GetNumericNodeValueAql(lhs), TRI_GetNumericNodeValueAql(rhs)); } 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; } //////////////////////////////////////////////////////////////////////////////// /// @brief mark a for node //////////////////////////////////////////////////////////////////////////////// static TRI_aql_node_t* MarkFor (TRI_aql_context_t* const context, TRI_aql_node_t* node) { TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1); if (!nameNode || !expressionNode) { return node; } if (expressionNode->_type == AQL_NODE_COLLECTION) { char* varName = TRI_AQL_NODE_STRING(nameNode); char* collectionName = TRI_AQL_NODE_STRING(expressionNode); if (!varName || !collectionName) { return node; } } return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief fold constants in a node //////////////////////////////////////////////////////////////////////////////// static TRI_aql_node_t* ModifyNode (void* data, TRI_aql_node_t* node) { TRI_aql_context_t* context = (TRI_aql_context_t*) data; if (!node) { return NULL; } switch (node->_type) { case AQL_NODE_OPERATOR_UNARY_PLUS: case AQL_NODE_OPERATOR_UNARY_MINUS: return OptimiseUnaryArithmeticOperation(context, node); case AQL_NODE_OPERATOR_UNARY_NOT: return OptimiseUnaryLogicalOperation(context, node); case AQL_NODE_OPERATOR_BINARY_AND: case AQL_NODE_OPERATOR_BINARY_OR: return OptimiseBinaryLogicalOperation(context, node); case AQL_NODE_OPERATOR_BINARY_EQ: case AQL_NODE_OPERATOR_BINARY_NE: case AQL_NODE_OPERATOR_BINARY_LT: case AQL_NODE_OPERATOR_BINARY_LE: case AQL_NODE_OPERATOR_BINARY_GT: case AQL_NODE_OPERATOR_BINARY_GE: case AQL_NODE_OPERATOR_BINARY_IN: return OptimiseBinaryRelationalOperation(context, node); case AQL_NODE_OPERATOR_BINARY_PLUS: case AQL_NODE_OPERATOR_BINARY_MINUS: case AQL_NODE_OPERATOR_BINARY_TIMES: case AQL_NODE_OPERATOR_BINARY_DIV: case AQL_NODE_OPERATOR_BINARY_MOD: return OptimiseBinaryArithmeticOperation(context, node); case AQL_NODE_SORT: return OptimiseSort(context, node); case AQL_NODE_FILTER: return OptimiseFilter(context, node); case AQL_NODE_FCALL: return OptimiseFcall(context, node); case AQL_NODE_FOR: // return MarkFor(context, node); default: break; } return node; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief fold constants recursively //////////////////////////////////////////////////////////////////////////////// TRI_aql_node_t* TRI_FoldConstantsAql (TRI_aql_context_t* const context, TRI_aql_node_t* node) { TRI_aql_modify_tree_walker_t* walker; // todo: must free TRI_InitAssociativePointer(&context->_ranges, TRI_UNKNOWN_MEM_ZONE, &TRI_HashStringKeyAssociativePointer, &HashFieldAccess, &EqualFieldAccess, NULL); walker = TRI_CreateModifyTreeWalkerAql((void*) context, &ModifyNode); if (!walker) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return node; } node = TRI_ModifyWalkTreeAql(walker, node); TRI_FreeModifyTreeWalkerAql(walker); /* size_t i; for (i = 0; i < context->_ranges._nrAlloc; ++i) { TRI_aql_field_access_t* fieldAccess = context->_ranges._table[i]; if (!fieldAccess) { continue; } printf("\nFIELD ACCESS\n- FIELD: %s\n",fieldAccess->_fieldName); printf("- TYPE: %s\n", AccessName(fieldAccess->_type)); if (fieldAccess->_type == TRI_AQL_ACCESS_EXACT) { TRI_string_buffer_t b; TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); TRI_StringifyJson(&b, fieldAccess->_value._exactValue); printf("- VALUE: %s\n", b._buffer); } else if (fieldAccess->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { TRI_string_buffer_t b; TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); TRI_StringifyJson(&b, fieldAccess->_value._singleRange._value); printf("- VALUE: %s\n", b._buffer); } } */ return node; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: