mirror of https://gitee.com/bigwinds/arangodb
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.
This commit is contained in:
parent
792f57b760
commit
a68fff16e2
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 \
|
||||
} \
|
||||
\
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue