diff --git a/arangod/Ahuacatl/ahuacatl-access-optimiser.c b/arangod/Ahuacatl/ahuacatl-access-optimiser.c index 6028345432..fe4d6f232e 100644 --- a/arangod/Ahuacatl/ahuacatl-access-optimiser.c +++ b/arangod/Ahuacatl/ahuacatl-access-optimiser.c @@ -49,6 +49,8 @@ static char* AccessName (const TRI_aql_access_e type) { switch (type) { case TRI_AQL_ACCESS_ALL: return "all"; + case TRI_AQL_ACCESS_REFERENCE: + return "reference"; case TRI_AQL_ACCESS_IMPOSSIBLE: return "impossible"; case TRI_AQL_ACCESS_EXACT: @@ -59,9 +61,10 @@ static char* AccessName (const TRI_aql_access_e type) { return "single range"; case TRI_AQL_ACCESS_RANGE_DOUBLE: return "double range"; - default: - return "unknown"; } + + assert(false); + return NULL; } #endif @@ -94,17 +97,21 @@ static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) { switch (fieldAccess->_type) { case TRI_AQL_ACCESS_EXACT: - case TRI_AQL_ACCESS_LIST: + case TRI_AQL_ACCESS_LIST: { if (fieldAccess->_value._value) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value); } break; - case TRI_AQL_ACCESS_RANGE_SINGLE: + } + + case TRI_AQL_ACCESS_RANGE_SINGLE: { if (fieldAccess->_value._singleRange._value) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._singleRange._value); } break; - case TRI_AQL_ACCESS_RANGE_DOUBLE: + } + + case TRI_AQL_ACCESS_RANGE_DOUBLE: { if (fieldAccess->_value._between._lower._value) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._lower._value); } @@ -112,11 +119,19 @@ static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._upper._value); } break; + } + + case TRI_AQL_ACCESS_REFERENCE: { + if (fieldAccess->_value._value) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value); + } + break; + } case TRI_AQL_ACCESS_ALL: - case TRI_AQL_ACCESS_IMPOSSIBLE: - default: { + case TRI_AQL_ACCESS_IMPOSSIBLE: { // nada + break; } } } @@ -130,7 +145,7 @@ static TRI_aql_field_access_t* CreateFieldAccess (TRI_aql_context_t* const conte const char* const fullName) { TRI_aql_field_access_t* fieldAccess; - fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false); + fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false); if (fieldAccess == NULL) { // OOM TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); @@ -198,6 +213,23 @@ static TRI_aql_field_access_t* MergeAndAll (TRI_aql_context_t* const context, return rhs; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is a reference, so it will always be returned +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndReference (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE); + + // reference always wins + TRI_FreeAccessAql(rhs); + + return lhs; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief merge two access structures using a logical AND /// @@ -808,6 +840,35 @@ static TRI_aql_field_access_t* MergeOrAll (TRI_aql_context_t* const context, return lhs; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is reference access +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrReference (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE); + + if (rhs->_type == TRI_AQL_ACCESS_REFERENCE) { + // if rhs is also a reference, we can keep it if both refer to the same value + if (TRI_EqualString(lhs->_value._value->_value._string.data, rhs->_value._value->_value._string.data)) { + TRI_FreeAccessAql(rhs); + + return lhs; + } + } + + // for everything else, we have to use the ALL range unfortunately + + TRI_FreeAccessAql(rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief merge two access structures using a logical OR /// @@ -1131,6 +1192,8 @@ static TRI_aql_field_access_t* MergeAttributeAccessAnd (TRI_aql_context_t* const return MergeAndImpossible(context, lhs, rhs); case TRI_AQL_ACCESS_ALL: return MergeAndAll(context, lhs, rhs); + case TRI_AQL_ACCESS_REFERENCE: + return MergeAndReference(context, lhs, rhs); case TRI_AQL_ACCESS_EXACT: return MergeAndExact(context, lhs, rhs); case TRI_AQL_ACCESS_LIST: @@ -1172,6 +1235,8 @@ static TRI_aql_field_access_t* MergeAttributeAccessOr (TRI_aql_context_t* const return MergeOrImpossible(context, lhs, rhs); case TRI_AQL_ACCESS_ALL: return MergeOrAll(context, lhs, rhs); + case TRI_AQL_ACCESS_REFERENCE: + return MergeOrReference(context, lhs, rhs); case TRI_AQL_ACCESS_EXACT: return MergeOrExact(context, lhs, rhs); case TRI_AQL_ACCESS_LIST: @@ -1451,6 +1516,7 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con if (fieldAccess == NULL) { // OOM TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return NULL; } @@ -1458,6 +1524,7 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con if (fieldAccess->_fullName == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess); + return NULL; } SetNameLength(fieldAccess); @@ -1465,13 +1532,15 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con if (operator == TRI_AQL_NODE_OPERATOR_BINARY_NE) { // create an all items access, and we're done fieldAccess->_type = TRI_AQL_ACCESS_ALL; + return fieldAccess; } // all other operation types require a value... value = TRI_NodeJsonAql(context, node); - if (!value) { + if (value == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return NULL; } @@ -1859,14 +1928,16 @@ TRI_aql_field_access_t* TRI_CloneAccessAql (TRI_aql_context_t* const context, switch (source->_type) { case TRI_AQL_ACCESS_EXACT: - case TRI_AQL_ACCESS_LIST: + case TRI_AQL_ACCESS_LIST: { fieldAccess->_value._value = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, source->_value._value); if (fieldAccess->_value._value == NULL) { // OOM TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); } break; - case TRI_AQL_ACCESS_RANGE_SINGLE: + } + + case TRI_AQL_ACCESS_RANGE_SINGLE: { fieldAccess->_value._singleRange._type = source->_value._singleRange._type; fieldAccess->_value._singleRange._value = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, source->_value._singleRange._value); if (fieldAccess->_value._singleRange._value == NULL) { @@ -1874,7 +1945,9 @@ TRI_aql_field_access_t* TRI_CloneAccessAql (TRI_aql_context_t* const context, TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); } break; - case TRI_AQL_ACCESS_RANGE_DOUBLE: + } + + case TRI_AQL_ACCESS_RANGE_DOUBLE: { fieldAccess->_value._between._lower._type = source->_value._between._lower._type; fieldAccess->_value._between._upper._type = source->_value._between._upper._type; fieldAccess->_value._between._lower._value = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, source->_value._between._lower._value); @@ -1885,11 +1958,18 @@ TRI_aql_field_access_t* TRI_CloneAccessAql (TRI_aql_context_t* const context, TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); } break; + } + + case TRI_AQL_ACCESS_REFERENCE: { + // TODO + break; + } + case TRI_AQL_ACCESS_ALL: - case TRI_AQL_ACCESS_IMPOSSIBLE: - default: + case TRI_AQL_ACCESS_IMPOSSIBLE: { // nada break; + } } return fieldAccess; diff --git a/arangod/Ahuacatl/ahuacatl-access-optimiser.h b/arangod/Ahuacatl/ahuacatl-access-optimiser.h index d8e1c01031..b5052933ac 100644 --- a/arangod/Ahuacatl/ahuacatl-access-optimiser.h +++ b/arangod/Ahuacatl/ahuacatl-access-optimiser.h @@ -68,12 +68,13 @@ TRI_aql_logical_e; //////////////////////////////////////////////////////////////////////////////// typedef enum { - TRI_AQL_ACCESS_IMPOSSIBLE, // no values must be accessed - TRI_AQL_ACCESS_EXACT, // one value must be accessed - TRI_AQL_ACCESS_LIST, // a list of values must be accessed - TRI_AQL_ACCESS_RANGE_SINGLE, // a range with one bound must be accessed - TRI_AQL_ACCESS_RANGE_DOUBLE, // a two bounded range must be accessed - TRI_AQL_ACCESS_ALL // all values must be accessed + TRI_AQL_ACCESS_IMPOSSIBLE, // no value needs to be accessed (impossible range) + TRI_AQL_ACCESS_EXACT, // one value is accessed + TRI_AQL_ACCESS_LIST, // a list of values is accessed + TRI_AQL_ACCESS_RANGE_SINGLE, // a range with one boundary is accessed + TRI_AQL_ACCESS_RANGE_DOUBLE, // a two bounded range is accessed + TRI_AQL_ACCESS_REFERENCE, // a reference can be used for access (a.x == b.x) + TRI_AQL_ACCESS_ALL // all values must be accessed (full scan) } TRI_aql_access_e; diff --git a/arangod/Ahuacatl/ahuacatl-explain.c b/arangod/Ahuacatl/ahuacatl-explain.c index 4e1143b83a..37f40af7fd 100644 --- a/arangod/Ahuacatl/ahuacatl-explain.c +++ b/arangod/Ahuacatl/ahuacatl-explain.c @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "Ahuacatl/ahuacatl-explain.h" +#include "Ahuacatl/ahuacatl-collections.h" #include "Ahuacatl/ahuacatl-conversions.h" #include "Ahuacatl/ahuacatl-scope.h" #include "Ahuacatl/ahuacatl-statement-walker.h"