From 9a6dfa1db29de2a8c68c31e6d8f4bf5b23eb16e7 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 19 Sep 2014 15:21:01 +0200 Subject: [PATCH] execute several types of expressions in C++ --- arangod/Aql/AqlValue.cpp | 88 +++++++++++++++++++++++++++++++++++--- arangod/Aql/AqlValue.h | 6 +++ arangod/Aql/AstNode.cpp | 12 ++++++ arangod/Aql/Expression.cpp | 79 ++++++++++++++++++++++++++++++++-- lib/Basics/json.h | 2 +- 5 files changed, 178 insertions(+), 9 deletions(-) diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index 27c1cdf0d4..93d07a42fd 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -263,6 +263,43 @@ bool AqlValue::isArray () const { THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the length of an AqlValue containing a list +//////////////////////////////////////////////////////////////////////////////// + +size_t AqlValue::listSize () const { + switch (_type) { + case JSON: { + TRI_json_t const* json = _json->json(); + if (TRI_IsListJson(json)) { + return TRI_LengthListJson(json); + } + return 0; + } + + case DOCVEC: { + TRI_ASSERT(_vector != nullptr); + // calculate the result list length + size_t totalSize = 0; + for (auto it = _vector->begin(); it != _vector->end(); ++it) { + totalSize += (*it)->size(); + } + return totalSize; + } + + case RANGE: { + TRI_ASSERT(_range != nullptr); + return _range->size(); + } + + case SHAPED: + case EMPTY: { + } + } + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief get a string representation of the AqlValue //////////////////////////////////////////////////////////////////////////////// @@ -690,14 +727,55 @@ int AqlValue::Compare (AQL_TRANSACTION_V8* trx, return 1; } - if (left._type == AqlValue::JSON && right._type == AqlValue::SHAPED) { - triagens::basics::Json rjson = right.toJson(trx, rightcoll); + // JSON against x + if (left._type == AqlValue::JSON && + (right._type == AqlValue::SHAPED || + right._type == AqlValue::RANGE || + right._type == AqlValue::DOCVEC)) { + triagens::basics::Json rjson = right.toJson(trx, rightcoll); return TRI_CompareValuesJson(left._json->json(), rjson.json(), true); } - - if (left._type == AqlValue::SHAPED && right._type == AqlValue::JSON) { + + // SHAPED against x + if (left._type == AqlValue::SHAPED) { triagens::basics::Json ljson = left.toJson(trx, leftcoll); - return TRI_CompareValuesJson(ljson.json(), right._json->json(), true); + + if (right._type == AqlValue::JSON) { + return TRI_CompareValuesJson(ljson.json(), right._json->json(), true); + } + else if (right._type == AqlValue::RANGE || + right._type == AqlValue::DOCVEC) { + triagens::basics::Json rjson = right.toJson(trx, rightcoll); + return TRI_CompareValuesJson(ljson.json(), rjson.json(), true); + } + } + + // RANGE against x + if (left._type == AqlValue::RANGE) { + triagens::basics::Json ljson = left.toJson(trx, leftcoll); + + if (right._type == AqlValue::JSON) { + return TRI_CompareValuesJson(ljson.json(), right._json->json(), true); + } + else if (right._type == AqlValue::SHAPED || + right._type == AqlValue::DOCVEC) { + triagens::basics::Json rjson = right.toJson(trx, rightcoll); + return TRI_CompareValuesJson(ljson.json(), rjson.json(), true); + } + } + + // DOCVEC against x + if (left._type == AqlValue::DOCVEC) { + triagens::basics::Json ljson = left.toJson(trx, leftcoll); + + if (right._type == AqlValue::JSON) { + return TRI_CompareValuesJson(ljson.json(), right._json->json(), true); + } + else if (right._type == AqlValue::SHAPED || + right._type == AqlValue::RANGE) { + triagens::basics::Json rjson = right.toJson(trx, rightcoll); + return TRI_CompareValuesJson(ljson.json(), rjson.json(), true); + } } // No other comparisons are defined diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index df1cc371f7..fc391c0976 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -180,6 +180,12 @@ namespace triagens { bool isArray () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the length of an AqlValue containing a list +//////////////////////////////////////////////////////////////////////////////// + + size_t listSize () const; + //////////////////////////////////////////////////////////////////////////////// /// @brief get a string representation of the AqlValue /// this will fail if the value is not a string diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index 778083a7d4..0c63423636 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -672,6 +672,18 @@ bool AstNode::isSimple () const { return (getMember(0)->isSimple() && getMember(1)->isSimple()); } + if (type == NODE_TYPE_OPERATOR_BINARY_EQ || + type == NODE_TYPE_OPERATOR_BINARY_NE || + type == NODE_TYPE_OPERATOR_BINARY_LT || + type == NODE_TYPE_OPERATOR_BINARY_LE || + type == NODE_TYPE_OPERATOR_BINARY_GT || + type == NODE_TYPE_OPERATOR_BINARY_GE || + type == NODE_TYPE_OPERATOR_BINARY_IN || + type == NODE_TYPE_OPERATOR_BINARY_NIN) { + // a comparison operator is simple if both bounds are simple + return (getMember(0)->isSimple() && getMember(1)->isSimple()); + } + return false; } diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index de12885edf..eddd6bb058 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -382,12 +382,13 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, } else if (node->type == NODE_TYPE_RANGE) { - TRI_document_collection_t const* myCollection = nullptr; + TRI_document_collection_t const* leftCollection = nullptr; + TRI_document_collection_t const* rightCollection = nullptr; auto low = node->getMember(0); auto high = node->getMember(1); - AqlValue resultLow = executeSimpleExpression(low, &myCollection, trx, docColls, argv, startPos, vars, regs); - AqlValue resultHigh = executeSimpleExpression(high, &myCollection, trx, docColls, argv, startPos, vars, regs); + AqlValue resultLow = executeSimpleExpression(low, &leftCollection, trx, docColls, argv, startPos, vars, regs); + AqlValue resultHigh = executeSimpleExpression(high, &rightCollection, trx, docColls, argv, startPos, vars, regs); if (! resultLow.isNumber() || ! resultHigh.isNumber()) { resultLow.destroy(); @@ -403,6 +404,78 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, return res; } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ || + node->type == NODE_TYPE_OPERATOR_BINARY_NE || + node->type == NODE_TYPE_OPERATOR_BINARY_LT || + node->type == NODE_TYPE_OPERATOR_BINARY_LE || + node->type == NODE_TYPE_OPERATOR_BINARY_GT || + node->type == NODE_TYPE_OPERATOR_BINARY_GE || + node->type == NODE_TYPE_OPERATOR_BINARY_IN || + node->type == NODE_TYPE_OPERATOR_BINARY_NIN) { + TRI_document_collection_t const* leftCollection = nullptr; + AqlValue left = executeSimpleExpression(node->getMember(0), &leftCollection, trx, docColls, argv, startPos, vars, regs); + TRI_document_collection_t const* rightCollection = nullptr; + AqlValue right = executeSimpleExpression(node->getMember(1), &rightCollection, trx, docColls, argv, startPos, vars, regs); + + if (node->type == NODE_TYPE_OPERATOR_BINARY_IN || + node->type == NODE_TYPE_OPERATOR_BINARY_NIN) { + // IN and NOT IN + if (! right.isList()) { + // right operand must be a list + THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_LIST_EXPECTED); + } + + size_t const n = right.listSize(); + + for (size_t i = 0; i < n; ++i) { + auto listItem = right.extractListMember(trx, rightCollection, i); + AqlValue listItemValue(&listItem); + + int compareResult = AqlValue::Compare(trx, left, leftCollection, listItemValue, nullptr); + + if (compareResult == 0) { + // item found in the list + left.destroy(); + right.destroy(); + + // found + return AqlValue(new triagens::basics::Json(node->type == NODE_TYPE_OPERATOR_BINARY_IN)); + } + } + + left.destroy(); + right.destroy(); + + // not found + return AqlValue(new triagens::basics::Json(node->type != NODE_TYPE_OPERATOR_BINARY_IN)); + } + + // all other comparison operators + int compareResult = AqlValue::Compare(trx, left, leftCollection, right, rightCollection); + left.destroy(); + right.destroy(); + + if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ) { + return AqlValue(new triagens::basics::Json(compareResult == 0)); + } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_NE) { + return AqlValue(new triagens::basics::Json(compareResult != 0)); + } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_LT) { + return AqlValue(new triagens::basics::Json(compareResult < 0)); + } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_LE) { + return AqlValue(new triagens::basics::Json(compareResult <= 0)); + } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_GT) { + return AqlValue(new triagens::basics::Json(compareResult > 0)); + } + else if (node->type == NODE_TYPE_OPERATOR_BINARY_GE) { + return AqlValue(new triagens::basics::Json(compareResult >= 0)); + } + // fall-through intentional + } + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unhandled type in simple expression"); } diff --git a/lib/Basics/json.h b/lib/Basics/json.h index 2fbba389df..bdae374104 100644 --- a/lib/Basics/json.h +++ b/lib/Basics/json.h @@ -250,7 +250,7 @@ void TRI_FreeJson (TRI_memory_zone_t*, TRI_json_t*); /// @brief determines the length of a list json //////////////////////////////////////////////////////////////////////////////// -size_t TRI_LengthListJson ( TRI_json_t const*); +size_t TRI_LengthListJson (TRI_json_t const*); //////////////////////////////////////////////////////////////////////////////// /// @brief determines whether the JSON passed is of type array