Predefined variables (#138)

* Add support for injecting predefined variables in any scope
This commit is contained in:
antonio2368
2021-04-23 14:19:42 +02:00
committed by Antonio Andelic
parent 7bf40eb5d2
commit 2f3fa656d9
16 changed files with 323 additions and 92 deletions

View File

@@ -3,6 +3,7 @@
#include "gtest/gtest.h"
#include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_generator.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
@@ -1093,3 +1094,53 @@ TEST(TestSymbolTable, CreateAnonymousSymbolWithExistingUserSymbolCalledAnon) {
auto anon2 = symbol_table.CreateAnonymousSymbol();
ASSERT_EQ(anon2.name_, "anon2");
}
TEST_F(TestSymbolGenerator, PredefinedIdentifiers) {
auto *first_op = IDENT("first_op", false);
auto *second_op = IDENT("second_op", false);
// RETURN first_op + second_op AS result
auto query = QUERY(SINGLE_QUERY(RETURN(ADD(first_op, second_op), AS("result"))));
EXPECT_THROW(query::MakeSymbolTable(query), SemanticException);
EXPECT_THROW(query::MakeSymbolTable(query, {first_op}), SemanticException);
EXPECT_THROW(query::MakeSymbolTable(query, {second_op}), SemanticException);
auto symbol_table = query::MakeSymbolTable(query, {first_op, second_op});
ASSERT_EQ(symbol_table.max_position(), 3);
// predefined identifier can only be used in one scope
// RETURN first_op + second_op AS result UNION RETURN second_op + first_op AS result
query = QUERY(SINGLE_QUERY(RETURN(ADD(first_op, second_op), AS("result"))),
UNION(SINGLE_QUERY(RETURN(ADD(second_op, first_op), AS("result")))));
ASSERT_THROW(query::MakeSymbolTable(query, {first_op, second_op}), SemanticException);
// predefined identifier can be introduced in any of the scope
// different predefined identifiers can be introduced in different scopes
// RETURN first_op AS result UNION RETURN second_op AS result
query = QUERY(SINGLE_QUERY(RETURN(first_op, AS("result"))), UNION(SINGLE_QUERY(RETURN(second_op, AS("result")))));
ASSERT_THROW(query::MakeSymbolTable(query), SemanticException);
symbol_table = query::MakeSymbolTable(query, {first_op, second_op});
ASSERT_EQ(symbol_table.max_position(), 5);
// WITH statement resets the scope, but the predefined identifier is okay
// because it's the first introduction of it in the query
// WITH 1 as one RETURN first_op AS first
query = QUERY(SINGLE_QUERY(WITH(LITERAL(1), AS("one")), RETURN(first_op, AS("first"))));
ASSERT_THROW(query::MakeSymbolTable(query), SemanticException);
symbol_table = query::MakeSymbolTable(query, {first_op});
ASSERT_EQ(symbol_table.max_position(), 3);
// In the first scope, first_op represents identifier created by match,
// in the second it represent the predefined identifier
// MATCH(first_op) WITH first_op as n RETURN first_op, n
query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("first_op"))), WITH("first_op", AS("n")), RETURN("first_op", "n")));
ASSERT_THROW(query::MakeSymbolTable(query), SemanticException);
symbol_table = query::MakeSymbolTable(query, {first_op});
ASSERT_EQ(symbol_table.max_position(), 6);
// You cannot redaclare the predefined identifier in the same scope
// UNWIND first_op as u CREATE(first_op {prop: u})
auto unwind = UNWIND(first_op, AS("u"));
auto node = NODE("first_op");
node->properties_[storage.GetPropertyIx("prop")] = dynamic_cast<Identifier *>(unwind->named_expression_->expression_);
query = QUERY(SINGLE_QUERY(unwind, CREATE(PATTERN(node))));
ASSERT_THROW(query::MakeSymbolTable(query, {first_op}), SemanticException);
}