From a68fff16e249e39926baaca81d1cef76bbe0b5f7 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 26 Oct 2012 13:22:48 +0200 Subject: [PATCH] issue #271: allow very simple optimisations for AQL function usage This change will create field access hints also for AQL function calls. Calls to functions will not be optimised, but meaningless usages of functions will not be optimised away. For example: FOR p IN PATHS(users, relations, "outbound") FILTER LENGTH(p.edges) > 0 && LENGTH(p.edges) < 0 RETURN p The FILTER in the above query uses the LENGTH() function two times in a way that no result will be produced. Before, function calls were excluded from expression collapsing and simplification. Now, multiple calls to the same function with the same call argument might be optimised away if the calls will lead to no results being produced. This will only work for functions that are called with exactly one argument which also must be an attribute name, and when the function calls are used in relational operations. --- arangod/Ahuacatl/ahuacatl-access-optimiser.c | 57 ++++++++++++++------ arangod/Ahuacatl/ahuacatl-ast-node.c | 4 +- arangod/Ahuacatl/ahuacatl-functions.c | 18 +++++-- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/arangod/Ahuacatl/ahuacatl-access-optimiser.c b/arangod/Ahuacatl/ahuacatl-access-optimiser.c index 3b2996c1b8..effd22ed0d 100644 --- a/arangod/Ahuacatl/ahuacatl-access-optimiser.c +++ b/arangod/Ahuacatl/ahuacatl-access-optimiser.c @@ -27,6 +27,8 @@ #include "Ahuacatl/ahuacatl-access-optimiser.h" +#include "Ahuacatl/ahuacatl-functions.h" + // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- @@ -1784,7 +1786,7 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con assert(field); assert(field->_name._buffer); assert(node); - + fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false); if (fieldAccess == NULL) { // OOM @@ -1807,8 +1809,8 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con fieldAccess->_type = TRI_AQL_ACCESS_ALL; return fieldAccess; - } - + } + if (node->_type == TRI_AQL_NODE_REFERENCE || node->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) { // create the reference access @@ -1908,13 +1910,13 @@ static TRI_aql_field_access_t* GetAttributeAccess (TRI_aql_context_t* const cont assert(context); assert(node); - if (!field || !field->_name._buffer) { + if (field == NULL || ! field->_name._buffer) { // this is ok if the node type is not supported return NULL; } fieldAccess = CreateAccessForNode(context, field, operator, node); - if (!fieldAccess) { + if (fieldAccess == NULL) { // OOM TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; @@ -1962,6 +1964,29 @@ static TRI_aql_attribute_name_t* GetAttributeName (TRI_aql_context_t* const cont return field; } + else if (node->_type == TRI_AQL_NODE_FCALL) { + TRI_aql_function_t* function = (TRI_aql_function_t*) TRI_AQL_NODE_DATA(node); + TRI_aql_node_t* args = TRI_AQL_NODE_MEMBER(node, 0); + TRI_aql_attribute_name_t* field; + + // if we have anything but 1 function call argument, we cannot optimise this + if (args->_members._length != 1) { + return NULL; + } + + field = GetAttributeName(context, TRI_AQL_NODE_MEMBER(args, 0)); + + if (field == NULL) { + return NULL; + } + + // name of generated attribute is XXX.FUNC(), e.g. for LENGTH(users.friends) => users.friends.LENGTH() + TRI_AppendCharStringBuffer(&field->_name, '.'); + TRI_AppendStringStringBuffer(&field->_name, function->_externalName); + TRI_AppendStringStringBuffer(&field->_name, "()"); + + return field; + } return NULL; } @@ -2046,28 +2071,28 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context, useBoth = false; - if ((lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) && - (TRI_IsConstantValueNodeAql(rhs) || rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) { - // collection.attribute|reference operator const value|reference|attribute access + if ((lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || lhs->_type == TRI_AQL_NODE_FCALL) && + (TRI_IsConstantValueNodeAql(rhs) || rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || rhs->_type == TRI_AQL_NODE_FCALL)) { + // collection.attribute|reference|fcall operator const value|reference|attribute access|fcall node1 = lhs; node2 = rhs; operator = node->_type; - if (rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) { - // expression of type reference|attribute access operator reference|attribute access + if (rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || rhs->_type == TRI_AQL_NODE_FCALL) { + // expression of type reference|attribute access|fcall operator reference|attribute access|fcall useBoth = true; } } - else if ((rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) && - (TRI_IsConstantValueNodeAql(lhs) || lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) { - // const value|reference|attribute access operator collection.attribute|reference + else if ((rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || rhs->_type == TRI_AQL_NODE_FCALL) && + (TRI_IsConstantValueNodeAql(lhs) || lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || lhs->_type == TRI_AQL_NODE_FCALL)) { + // const value|reference|attribute|fcall access operator collection.attribute|reference|fcall node1 = rhs; node2 = lhs; operator = TRI_ReverseOperatorRelationalAql(node->_type); assert(operator != TRI_AQL_NODE_NOP); - if (lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) { - // expression of type reference|attribute access operator reference|attribute access + if (lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS || lhs->_type == TRI_AQL_NODE_FCALL) { + // expression of type reference|attribute access|fcall operator reference|attribute access|fcall useBoth = true; } } @@ -2090,7 +2115,7 @@ again: // we'll get back here for expressions of type a.x == b.y (where both sides are references) field = GetAttributeName(context, node1); - if (field) { + if (field && node2->_type != TRI_AQL_NODE_FCALL) { TRI_aql_field_access_t* attributeAccess = GetAttributeAccess(context, field, operator, node2); TRI_vector_pointer_t* result; diff --git a/arangod/Ahuacatl/ahuacatl-ast-node.c b/arangod/Ahuacatl/ahuacatl-ast-node.c index 3b2dd13c5f..ef05cb9aea 100644 --- a/arangod/Ahuacatl/ahuacatl-ast-node.c +++ b/arangod/Ahuacatl/ahuacatl-ast-node.c @@ -59,7 +59,7 @@ #define CREATE_NODE(type) \ TRI_aql_node_t* node = (TRI_aql_node_t*) \ TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_node_t), false); \ - if (!node) { \ + if (node == NULL) { \ ABORT_OOM \ } \ \ @@ -71,7 +71,7 @@ //////////////////////////////////////////////////////////////////////////////// #define ADD_MEMBER(member) \ - if (!member) { \ + if (member == NULL) { \ ABORT_OOM \ } \ \ diff --git a/arangod/Ahuacatl/ahuacatl-functions.c b/arangod/Ahuacatl/ahuacatl-functions.c index 6e2da0bb0b..cd85428f5a 100644 --- a/arangod/Ahuacatl/ahuacatl-functions.c +++ b/arangod/Ahuacatl/ahuacatl-functions.c @@ -312,6 +312,15 @@ static bool EqualName (TRI_associative_pointer_t* array, /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- optimiser callbacks +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @brief optimise callback function for PATHS() AQL function //////////////////////////////////////////////////////////////////////////////// @@ -336,8 +345,6 @@ static void OptimisePaths (const TRI_aql_node_t* const fcallNode, return; } - assert(args->_members._length >= 3); - vertexCollection = TRI_AQL_NODE_MEMBER(args, 0); edgeCollection = TRI_AQL_NODE_MEMBER(args, 1); direction = TRI_AQL_NODE_MEMBER(args, 2); @@ -352,6 +359,7 @@ static void OptimisePaths (const TRI_aql_node_t* const fcallNode, directionValue = TRI_AQL_NODE_STRING(direction); + // try to optimise the vertex collection access if (TRI_EqualString(directionValue, "outbound")) { lookFor = ".source."; len = strlen(lookFor); @@ -375,12 +383,16 @@ static void OptimisePaths (const TRI_aql_node_t* const fcallNode, // copy trailing \0 byte as well memmove(name, name + len - 1, n - fieldAccess->_variableNameLength - len + 2); - // attach the modified fieldAccess to the collection + // attach the modified fieldaccess to the collection hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(vertexCollection)); hint->_ranges = TRI_AddAccessAql(context, hint->_ranges, fieldAccess); } } +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- public functions // -----------------------------------------------------------------------------