diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index eba0764b20..8e63527a92 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -92,7 +92,7 @@ void AstNode::toJson (TRI_json_t* json, TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateStringCopyJson(zone, "null")); break; case VALUE_TYPE_BOOL: - TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateStringCopyJson(zone, value.value._bool ? "true" : "false")); + TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateBooleanJson(zone, value.value._bool)); break; case VALUE_TYPE_INT: TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateNumberJson(zone, static_cast(value.value._int))); diff --git a/arangod/Aql/Parser.cpp b/arangod/Aql/Parser.cpp index 446ec3f1dc..bf501943c8 100644 --- a/arangod/Aql/Parser.cpp +++ b/arangod/Aql/Parser.cpp @@ -49,7 +49,6 @@ Parser::Parser (Query* query) _remainingLength(query->queryLength()), _offset(0), _marker(nullptr), - _subQueryCount(0), _uniqueId(0), _stack() { @@ -87,7 +86,7 @@ bool Parser::configureWriteQuery (QueryType type, TRI_ASSERT(type != AQL_QUERY_READ); // check if we currently are in a subquery - if (isInSubQuery()) { + if (_ast->isInSubQuery()) { // data modification not allowed in sub-queries registerError(TRI_ERROR_QUERY_MODIFY_IN_SUBQUERY); return false; diff --git a/arangod/Aql/Parser.h b/arangod/Aql/Parser.h index 83151cf3f3..15d90cdb7a 100644 --- a/arangod/Aql/Parser.h +++ b/arangod/Aql/Parser.h @@ -117,31 +117,6 @@ namespace triagens { return _ast; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief start a subquery -//////////////////////////////////////////////////////////////////////////////// - - inline void startSubQuery () { - ++_subQueryCount; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief end a subquery -//////////////////////////////////////////////////////////////////////////////// - - inline void endSubQuery () { - TRI_ASSERT(_subQueryCount > 0); - --_subQueryCount; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief whether or not we are in a subquery -//////////////////////////////////////////////////////////////////////////////// - - inline bool isInSubQuery () const { - return (_subQueryCount > 0); - } - //////////////////////////////////////////////////////////////////////////////// /// @brief return the scanner //////////////////////////////////////////////////////////////////////////////// @@ -290,7 +265,6 @@ namespace triagens { size_t _offset; // current parse position char const* _marker; // a position used temporarily during parsing - size_t _subQueryCount; // number of active subqueries size_t _uniqueId; // a counter to generate unique (temporary) variable names diff --git a/arangod/Aql/QueryAst.cpp b/arangod/Aql/QueryAst.cpp index 1175d60819..c505a8f5e0 100644 --- a/arangod/Aql/QueryAst.cpp +++ b/arangod/Aql/QueryAst.cpp @@ -64,6 +64,7 @@ QueryAst::QueryAst (Query* query, _bindParameters(), _collectionNames(), _root(nullptr), + _queries(), _writeCollection(nullptr), _writeOptions(nullptr) { @@ -71,7 +72,11 @@ QueryAst::QueryAst (Query* query, TRI_ASSERT(_parser != nullptr); _nodes.reserve(32); - _root = createNode(NODE_TYPE_ROOT); + _strings.reserve(32); + + startSubQuery(); + + TRI_ASSERT(_root != nullptr); } //////////////////////////////////////////////////////////////////////////////// @@ -496,14 +501,16 @@ AstNode* QueryAst::createNodeTernaryOperator (AstNode const* condition, /// @brief create an AST subquery node //////////////////////////////////////////////////////////////////////////////// -AstNode* QueryAst::createNodeSubquery (char const* tempName) { - if (tempName == nullptr) { +AstNode* QueryAst::createNodeSubquery (char const* variableName, + AstNode const* subQuery) { + if (variableName == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } AstNode* node = createNode(NODE_TYPE_SUBQUERY); - AstNode* variable = createNodeVariable(tempName, false); + AstNode* variable = createNodeVariable(variableName, false); node->addMember(variable); + node->addMember(subQuery); return node; } diff --git a/arangod/Aql/QueryAst.h b/arangod/Aql/QueryAst.h index fdb2caefe5..c51dda5427 100644 --- a/arangod/Aql/QueryAst.h +++ b/arangod/Aql/QueryAst.h @@ -87,6 +87,57 @@ namespace triagens { public: +//////////////////////////////////////////////////////////////////////////////// +/// @brief begin a subquery +//////////////////////////////////////////////////////////////////////////////// + + void startSubQuery () { + // insert a new root node + AstNodeType type; + + if (_queries.empty()) { + // root node of query + type = NODE_TYPE_ROOT; + } + else { + // sub query node + type = NODE_TYPE_SUBQUERY; + } + + auto root = createNode(type); + + // save the root node + _queries.push_back(root); + + // set the current root node if everything went well + _root = root; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief end a subquery +//////////////////////////////////////////////////////////////////////////////// + + AstNode* endSubQuery () { + // get the current root node + AstNode* root = _queries.back(); + // remove it from the stack + _queries.pop_back(); + + // set root node to previous root node + _root = _queries.back(); + + // return the root node we just popped from the stack + return root; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not we currently are in a subquery +//////////////////////////////////////////////////////////////////////////////// + + bool isInSubQuery () const { + return (_queries.size() > 1); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief return a copy of our own bind parameters //////////////////////////////////////////////////////////////////////////////// @@ -297,7 +348,8 @@ namespace triagens { /// @brief create an AST subquery node //////////////////////////////////////////////////////////////////////////////// - AstNode* createNodeSubquery (char const*); + AstNode* createNodeSubquery (char const*, + AstNode const*); //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST attribute access node @@ -563,6 +615,12 @@ namespace triagens { AstNode* _root; +//////////////////////////////////////////////////////////////////////////////// +/// @brief root nodes of queries and subqueries +//////////////////////////////////////////////////////////////////////////////// + + std::vector _queries; + //////////////////////////////////////////////////////////////////////////////// /// @brief which collection is going to be modified in the query //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/grammar.y b/arangod/Aql/grammar.y index 590b273225..bfe1a5488c 100644 --- a/arangod/Aql/grammar.y +++ b/arangod/Aql/grammar.y @@ -434,16 +434,16 @@ expression: } | T_OPEN { parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_SUBQUERY); - parser->startSubQuery(); + parser->ast()->startSubQuery(); } query T_CLOSE { - parser->endSubQuery(); + AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); - char const* tempName = parser->generateName(); - auto subQuery = parser->ast()->createNodeSubquery(tempName); + char const* variableName = parser->generateName(); + auto subQuery = parser->ast()->createNodeLet(variableName, node, false); parser->ast()->addOperation(subQuery); - $$ = parser->ast()->createNodeReference(tempName); + $$ = parser->ast()->createNodeReference(variableName); } | operator_unary { $$ = $1;