diff --git a/QL/ast-node.c b/QL/ast-node.c index 04498bfa53..757dc6c9e0 100644 --- a/QL/ast-node.c +++ b/QL/ast-node.c @@ -208,6 +208,32 @@ QL_ast_node_type_group_e QLAstNodeGetTypeGroup (const QL_ast_node_type_e type) { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the reverse of a relational operator +//////////////////////////////////////////////////////////////////////////////// + +QL_ast_node_type_e QLAstNodeGetReversedRelationalOperator (const QL_ast_node_type_e type) { + if (type == QLNodeBinaryOperatorIdentical || + type == QLNodeBinaryOperatorEqual || + type == QLNodeBinaryOperatorUnidentical || + type == QLNodeBinaryOperatorUnequal) { + return type; + } + if (type == QLNodeBinaryOperatorLess) { + return QLNodeBinaryOperatorGreaterEqual; + } + if (type == QLNodeBinaryOperatorLessEqual) { + return QLNodeBinaryOperatorGreater; + } + if (type == QLNodeBinaryOperatorGreater) { + return QLNodeBinaryOperatorLessEqual; + } + if (type == QLNodeBinaryOperatorGreaterEqual) { + return QLNodeBinaryOperatorLess; + } + assert(false); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief get the label string for a unary operator //////////////////////////////////////////////////////////////////////////////// diff --git a/QL/ast-node.h b/QL/ast-node.h index 9318b219ac..98c24deb1e 100644 --- a/QL/ast-node.h +++ b/QL/ast-node.h @@ -169,6 +169,12 @@ const char* QLAstNodeGetName (const QL_ast_node_type_e); QL_ast_node_type_group_e QLAstNodeGetTypeGroup (const QL_ast_node_type_e); +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the reverse of a relational operator +//////////////////////////////////////////////////////////////////////////////// + +QL_ast_node_type_e QLAstNodeGetReversedRelationalOperator (const QL_ast_node_type_e); + //////////////////////////////////////////////////////////////////////////////// /// @brief get the label string for a unary operator //////////////////////////////////////////////////////////////////////////////// diff --git a/QL/ast-query.c b/QL/ast-query.c index 0c4a6ae312..5eb79bdb2d 100644 --- a/QL/ast-query.c +++ b/QL/ast-query.c @@ -158,6 +158,22 @@ bool QLAstQueryIsValidAlias (QL_ast_query_t* query, const char* alias) { return (0 != TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias)); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief Return the collection name for its alias +//////////////////////////////////////////////////////////////////////////////// + +char* QLAstQueryGetCollectionNameForAlias (QL_ast_query_t* query, + const char* alias) { + QL_ast_query_collection_t* collection; + + collection = (QL_ast_query_collection_t*) + TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); + if (!collection) { + return NULL; + } + return collection->_name; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief Add a collection to the query //////////////////////////////////////////////////////////////////////////////// diff --git a/QL/ast-query.h b/QL/ast-query.h index 0d1f7bc134..96edcc6e40 100644 --- a/QL/ast-query.h +++ b/QL/ast-query.h @@ -214,6 +214,12 @@ void QLAstQueryAddRefCount (QL_ast_query_t*, const char*); bool QLAstQueryIsValidAlias (QL_ast_query_t*, const char*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief Return the collection name for its alias +//////////////////////////////////////////////////////////////////////////////// + +char* QLAstQueryGetCollectionNameForAlias (QL_ast_query_t*, const char*); + //////////////////////////////////////////////////////////////////////////////// /// @brief Add a collection to the query //////////////////////////////////////////////////////////////////////////////// diff --git a/QL/optimize.c b/QL/optimize.c index 8b29f75aa1..416feaabd3 100644 --- a/QL/optimize.c +++ b/QL/optimize.c @@ -208,6 +208,46 @@ double QLOptimizeGetDouble (const QL_ast_node_t const* node) { return 0.0; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief check if a document declaration is static or dynamic +//////////////////////////////////////////////////////////////////////////////// + +bool QLOptimizeIsStaticDocument (QL_ast_node_t* node) { + bool result; + + if (node->_next) { + while (node->_next) { + result = QLOptimizeIsStaticDocument(node->_next); + if (!result) { + return false; + } + node = node->_next; + } + return true; + } + + if (node->_lhs) { + result = QLOptimizeIsStaticDocument(node->_lhs); + if (!result) { + return false; + } + } + if (node->_rhs) { + result = QLOptimizeIsStaticDocument(node->_rhs); + if (!result) { + return false; + } + } + if (node->_type == QLNodeReferenceCollectionAlias || + node->_type == QLNodeControlFunctionCall || + node->_type == QLNodeControlTernary || + node->_type == QLNodeContainerMemberAccess) { + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief convert a node to a null value node //////////////////////////////////////////////////////////////////////////////// @@ -870,6 +910,10 @@ void QLOptimizeFreeRangeVector (TRI_vector_pointer_t* vector) { if (range->_field) { TRI_Free(range->_field); } + + if (range->_refValue._field) { + TRI_FreeString(range->_refValue._field); + } TRI_Free(range); } @@ -922,9 +966,20 @@ static TRI_vector_pointer_t* QLOptimizeCombineRanges (const QL_ast_node_type_e t } } + previous = QLOptimizeGetRangeByHash(range->_hash, vector); + if (type == QLNodeBinaryOperatorOr) { + // only use logical || operator for same field. if field name differs, an || + // effectively kills all ranges + if (vector->_length >0 && !previous) { + QLOptimizeFreeRangeVector(vector); + TRI_InitVectorPointer(vector); + goto EXIT; + } + } + if (!previous) { - // push range on stack + // push range into result vector TRI_PushBackVectorPointer(vector, range); // remove range from original vector to avoid double freeing @@ -1298,6 +1353,7 @@ static QL_optimize_range_t* QLOptimizeCreateRange (QL_ast_node_t* memberNode, QL_optimize_range_t* range; TRI_string_buffer_t* name; QL_ast_node_t* lhs; + QL_javascript_conversion_t* documentJs; // get the field name name = QLOptimizeGetMemberNameString(memberNode, false); @@ -1313,6 +1369,9 @@ static QL_optimize_range_t* QLOptimizeCreateRange (QL_ast_node_t* memberNode, return NULL; } + range->_refValue._field = NULL; + range->_refValue._collection = NULL; + // get value if (valueNode->_type == QLNodeValueNumberDouble || valueNode->_type == QLNodeValueNumberDoubleString) { @@ -1322,7 +1381,13 @@ static QL_optimize_range_t* QLOptimizeCreateRange (QL_ast_node_t* memberNode, else if (valueNode->_type == QLNodeValueString) { // range is of type string range->_valueType = RANGE_TYPE_STRING; - } + } + else if (valueNode->_type == QLNodeValueDocument) { + range->_valueType = RANGE_TYPE_JSON; + } + else if (valueNode->_type == QLNodeContainerMemberAccess) { + range->_valueType = RANGE_TYPE_FIELD; + } else { assert(false); } @@ -1340,14 +1405,37 @@ static QL_optimize_range_t* QLOptimizeCreateRange (QL_ast_node_t* memberNode, if (type == QLNodeBinaryOperatorIdentical || type == QLNodeBinaryOperatorEqual) { // === and == , range is [ value (inc) ... value (inc) ] - if (range->_valueType == RANGE_TYPE_DOUBLE) { + if (range->_valueType == RANGE_TYPE_FIELD) { + range->_refValue._collection = + ((QL_ast_node_t*) valueNode->_lhs)->_value._stringValue; + name = QLOptimizeGetMemberNameString(valueNode, false); + if (name) { + range->_refValue._field = TRI_DuplicateString(name->_buffer); + TRI_FreeStringBuffer(name); + TRI_Free(name); + } + } + else if (range->_valueType == RANGE_TYPE_DOUBLE) { range->_minValue._doubleValue = QLOptimizeGetDouble(valueNode); range->_maxValue._doubleValue = range->_minValue._doubleValue; } - else if (range->_valueType == RANGE_TYPE_STRING) { + else if (range->_valueType == RANGE_TYPE_STRING) { range->_minValue._stringValue = valueNode->_value._stringValue; range->_maxValue._stringValue = range->_minValue._stringValue; } + else if (range->_valueType == RANGE_TYPE_JSON) { + documentJs = QLJavascripterInit(); + if (!documentJs) { + TRI_FreeStringBuffer(name); + TRI_Free(name); + TRI_Free(range); + return NULL; + } + QLJavascripterConvert(documentJs, valueNode); + range->_minValue._stringValue = documentJs->_buffer->_buffer; + range->_maxValue._stringValue = range->_minValue._stringValue; + QLJavascripterFree(documentJs); + } range->_minStatus = RANGE_VALUE_INCLUDED; range->_maxStatus = RANGE_VALUE_INCLUDED; } @@ -1449,19 +1537,43 @@ TRI_vector_pointer_t* QLOptimizeCondition (QL_ast_node_t* node) { type == QLNodeBinaryOperatorLessEqual || type == QLNodeBinaryOperatorGreaterEqual) { // comparison operator - if (lhs->_type == QLNodeContainerMemberAccess && + rhs->_type == QLNodeContainerMemberAccess) { + // collection.attribute relop collection.attribute + return QLOptimizeMergeRangeVectors( + QLOptimizeCreateRangeVector(QLOptimizeCreateRange(lhs, rhs, type)), + QLOptimizeCreateRangeVector(QLOptimizeCreateRange(rhs, lhs, type)) + ); + } + else if (lhs->_type == QLNodeContainerMemberAccess && + (type == QLNodeBinaryOperatorIdentical || + type == QLNodeBinaryOperatorEqual) && + rhs->_type == QLNodeValueDocument && + QLOptimizeIsStaticDocument(rhs)) { + // collection.attribute == document + return QLOptimizeCreateRangeVector(QLOptimizeCreateRange(lhs, rhs, type)); + } + else if (lhs->_type == QLNodeContainerMemberAccess && (rhs->_type == QLNodeValueNumberDouble || rhs->_type == QLNodeValueNumberDoubleString || rhs->_type == QLNodeValueString)) { - // collection.attrbiute relop value + // collection.attribute relop value return QLOptimizeCreateRangeVector(QLOptimizeCreateRange(lhs, rhs, type)); + } + else if (rhs->_type == QLNodeContainerMemberAccess && + (type == QLNodeBinaryOperatorIdentical || + type == QLNodeBinaryOperatorEqual) && + lhs->_type == QLNodeValueDocument && + QLOptimizeIsStaticDocument(lhs)) { + // document == collection.attribute + return QLOptimizeCreateRangeVector(QLOptimizeCreateRange(rhs, lhs, type)); } else if (rhs->_type == QLNodeContainerMemberAccess && (lhs->_type == QLNodeValueNumberDouble || lhs->_type == QLNodeValueNumberDoubleString || - lhs->_type == QLNodeValueString)) { + lhs->_type == QLNodeValueString)) { // value relop collection.attrbiute - return QLOptimizeCreateRangeVector(QLOptimizeCreateRange(rhs, lhs, type)); + return QLOptimizeCreateRangeVector( + QLOptimizeCreateRange(rhs, lhs, QLAstNodeGetReversedRelationalOperator(type))); } } @@ -1555,22 +1667,25 @@ void QLOptimizeDetermineIndexes (QL_ast_query_t* query) { TRI_index_definition_t* indexDefinition; QL_ast_node_t* node; char* collectionName; + char* alias; size_t i, j, k, matches; size_t count = 0; return; node = (QL_ast_node_t*) query->_from._base->_next; - assert(node != 0); - + + // enum all collections used in query while (node != 0) { ranges = 0; if (count++ == 0) { collectionName = ((QL_ast_node_t*) node->_lhs)->_value._stringValue; + alias = ((QL_ast_node_t*) node->_rhs)->_value._stringValue; ranges = QLOptimizeCondition(query->_where._base); } else { collectionName = ((QL_ast_node_t*) ((QL_ast_node_t*) node->_lhs)->_lhs)->_value._stringValue; + alias = ((QL_ast_node_t*) ((QL_ast_node_t*) node->_lhs)->_rhs)->_value._stringValue; } // accessType = TABLE_SCAN; @@ -1578,6 +1693,7 @@ return; if (ranges) { indexDefinitions = TRI_GetCollectionIndexes(query->_vocbase, collectionName); + // enum all indexes for (i = 0; i < indexDefinitions._length; i++) { indexDefinition = (TRI_index_definition_t*) indexDefinitions._buffer[i]; @@ -1585,8 +1701,8 @@ return; for (j = 0 ; j < indexDefinition->_fields._length; j++) { for (k = 0; k < ranges->_length; k++) { range = (QL_optimize_range_t*) ranges->_buffer[k]; - // check if collection is the same - if (strcmp(range->_collection, collectionName) != 0) { + // check if collection name matches + if (strcmp(range->_collection, alias) != 0) { continue; } @@ -1606,7 +1722,8 @@ return; range->_minValue._doubleValue != range->_maxValue._doubleValue) { continue; } - if (range->_valueType == RANGE_TYPE_STRING && + if ((range->_valueType == RANGE_TYPE_STRING || + range->_valueType == RANGE_TYPE_JSON) && strcmp(range->_minValue._stringValue, range->_maxValue._stringValue) != 0) { continue; } diff --git a/QL/optimize.h b/QL/optimize.h index 0b5cfed6b5..de8b5bfe0c 100644 --- a/QL/optimize.h +++ b/QL/optimize.h @@ -38,6 +38,7 @@ #include "QL/ast-query.h" #include "QL/parser-context.h" #include "QL/formatter.h" +#include "QL/javascripter.h" #include "VocBase/index.h" #ifdef __cplusplus @@ -98,12 +99,16 @@ QL_optimize_range_type_e; //////////////////////////////////////////////////////////////////////////////// /// @brief Range value types /// -/// Currently supported types are doubles (numbers) and strings +/// Currently supported types are collection attributes (fields), doubles +/// (numbers), strings, and JSON documents. //////////////////////////////////////////////////////////////////////////////// typedef enum { - RANGE_TYPE_DOUBLE = 0, - RANGE_TYPE_STRING = 1 + RANGE_TYPE_FIELD = 1, + RANGE_TYPE_DOUBLE = 2, + RANGE_TYPE_STRING = 3, + RANGE_TYPE_JSON = 4 + } QL_optimize_range_value_type_e; @@ -146,6 +151,10 @@ typedef struct QL_optimize_range_s { double _doubleValue; char* _stringValue; } _maxValue; + struct { + char* _collection; + char *_field; + } _refValue; uint64_t _hash; QL_optimize_range_type_e _minStatus; QL_optimize_range_type_e _maxStatus;