diff --git a/src/query/console.cpp b/src/query/console.cpp index 21fe7e78a..9da6d9f62 100644 --- a/src/query/console.cpp +++ b/src/query/console.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "query/exceptions.hpp" #include "query/interpreter.hpp" @@ -55,16 +56,7 @@ std::string ReadLine(const char *prompt) { */ std::string TypedValueToString(const query::TypedValue &value) { std::stringstream ss; - switch (value.type()) { - case query::TypedValue::Type::List: - break; - case query::TypedValue::Type::Map: - break; - case query::TypedValue::Type::Path: - break; - default: - ss << value; - } + ss << value; return ss.str(); } @@ -121,9 +113,10 @@ void PrintResults(ResultStreamFaker results) { // output the summary std::cout << "Query summary: {"; - PrintIterable(std::cout, results.GetSummary(), ", ", [&](const auto kv) { - return kv.first + ": " + TypedValueToString(kv.second); - }); + PrintIterable(std::cout, results.GetSummary(), ", ", + [&](auto &stream, const auto &kv) { + stream << kv.first << ": " << kv.second; + }); std::cout << "}" << std::endl; } diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index f4e053e8e..cc9c19351 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -361,7 +361,14 @@ class IsNullOperator : public UnaryOperator { using UnaryOperator::UnaryOperator; }; -class Literal : public Expression { +class BaseLiteral : public Expression { + friend class AstTreeStorage; + + protected: + BaseLiteral(int uid) : Expression(uid) {} +}; + +class PrimitiveLiteral : public BaseLiteral { friend class AstTreeStorage; public: @@ -369,9 +376,29 @@ class Literal : public Expression { DEFVISITABLE(TreeVisitorBase); protected: - Literal(int uid) : Expression(uid) {} + PrimitiveLiteral(int uid) : BaseLiteral(uid) {} template - Literal(int uid, T value) : Expression(uid), value_(value) {} + PrimitiveLiteral(int uid, T value) : BaseLiteral(uid), value_(value) {} +}; + +class ListLiteral : public BaseLiteral { + friend class AstTreeStorage; + + public: + const std::vector elements_; + void Accept(TreeVisitorBase &visitor) override { + if (visitor.PreVisit(*this)) { + visitor.Visit(*this); + for (auto expr_ptr : elements_) + expr_ptr->Accept(visitor); + visitor.PostVisit(*this); + } + } + + protected: + ListLiteral(int uid) : BaseLiteral(uid) {} + ListLiteral(int uid, const std::vector &elements) + : BaseLiteral(uid), elements_(elements) {} }; class Identifier : public Expression { diff --git a/src/query/frontend/ast/ast_visitor.hpp b/src/query/frontend/ast/ast_visitor.hpp index 5eb2ab325..bbcd278ff 100644 --- a/src/query/frontend/ast/ast_visitor.hpp +++ b/src/query/frontend/ast/ast_visitor.hpp @@ -18,7 +18,8 @@ class With; class Pattern; class NodeAtom; class EdgeAtom; -class Literal; +class PrimitiveLiteral; +class ListLiteral; class OrOperator; class XorOperator; class AndOperator; @@ -51,8 +52,8 @@ using TreeVisitorBase = ::utils::Visitor< AdditionOperator, SubtractionOperator, MultiplicationOperator, DivisionOperator, ModOperator, NotEqualOperator, EqualOperator, LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator, - UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, Identifier, Literal, - PropertyLookup, Aggregation, Function, Create, Match, Return, With, Pattern, - NodeAtom, EdgeAtom, Delete, Where, SetProperty, SetProperties, SetLabels, - RemoveProperty, RemoveLabels, Merge>; + UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, Identifier, + PrimitiveLiteral, ListLiteral, PropertyLookup, Aggregation, Function, + Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where, + SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge>; } diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index 9d68d39eb..291a58f55 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -273,6 +273,14 @@ antlrcpp::Any CypherMainVisitor::visitMapLiteral( return map; } +antlrcpp::Any CypherMainVisitor::visitListLiteral( + CypherParser::ListLiteralContext *ctx) { + std::vector expressions; + for (auto expr_ctx_ptr : ctx->expression()) + expressions.push_back(expr_ctx_ptr->accept(this)); + return expressions; +} + antlrcpp::Any CypherMainVisitor::visitPropertyKeyName( CypherParser::PropertyKeyNameContext *ctx) { return ctx_.db_accessor_.property(visitChildren(ctx)); @@ -606,7 +614,8 @@ antlrcpp::Any CypherMainVisitor::visitExpression2( antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { if (ctx->literal()) { - return static_cast(visitChildren(ctx).as()); + return static_cast( + ctx->literal()->accept(this).as()); } else if (ctx->parameter()) { // TODO: implement other clauses. throw utils::NotYetImplemented(); @@ -628,18 +637,22 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { antlrcpp::Any CypherMainVisitor::visitLiteral( CypherParser::LiteralContext *ctx) { if (ctx->CYPHERNULL()) { - return storage_.Create(TypedValue::Null); + return static_cast( + storage_.Create(TypedValue::Null)); } else if (ctx->StringLiteral()) { - return storage_.Create( - visitStringLiteral(ctx->StringLiteral()->getText()).as()); + return static_cast(storage_.Create( + visitStringLiteral(ctx->StringLiteral()->getText()).as())); } else if (ctx->booleanLiteral()) { - return storage_.Create( - ctx->booleanLiteral()->accept(this).as()); + return static_cast(storage_.Create( + ctx->booleanLiteral()->accept(this).as())); } else if (ctx->numberLiteral()) { - return storage_.Create( - ctx->numberLiteral()->accept(this).as()); + return static_cast(storage_.Create( + ctx->numberLiteral()->accept(this).as())); + } else if (ctx->listLiteral()) { + return static_cast(storage_.Create( + ctx->listLiteral()->accept(this).as>())); } else { - // TODO: Implement map and list literals. + // TODO: Implement map literal. throw utils::NotYetImplemented(); } return visitChildren(ctx); diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp index 865c31494..e62b4f9b6 100644 --- a/src/query/frontend/ast/cypher_main_visitor.hpp +++ b/src/query/frontend/ast/cypher_main_visitor.hpp @@ -205,6 +205,11 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor { */ antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override; + /** + * @return vector + */ + antlrcpp::Any visitListLiteral(CypherParser::ListLiteralContext *ctx) override; + /** * @return GraphDbTypes::Property */ @@ -393,7 +398,7 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor { CypherParser::FunctionNameContext *ctx) override; /** - * @return Literal* + * @return BaseLiteral* */ antlrcpp::Any visitLiteral(CypherParser::LiteralContext *ctx) override; diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp index b2135e214..7871f7abd 100644 --- a/src/query/interpret/eval.hpp +++ b/src/query/interpret/eval.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "database/graph_db_accessor.hpp" #include "query/common.hpp" @@ -117,12 +118,21 @@ class ExpressionEvaluator : public TreeVisitorBase { } } - void Visit(Literal &literal) override { + void Visit(PrimitiveLiteral &literal) override { // TODO: no need to evaluate constants, we can write it to frame in one // of the previous phases. result_stack_.push_back(literal.value_); } + void PostVisit(ListLiteral &literal) override { + std::vector result; + result.reserve(literal.elements_.size()); + for (size_t i = 0 ; i < literal.elements_.size() ; i++) + result.emplace_back(PopBack()); + std::reverse(result.begin(), result.end()); + result_stack_.emplace_back(std::move(result)); + } + bool PreVisit(Aggregation &aggregation) override { auto value = frame_[symbol_table_.at(aggregation)]; // Aggregation is probably always simple type, but let's switch accessor diff --git a/src/query/plan/planner.cpp b/src/query/plan/planner.cpp index 85170dff4..515fa9122 100644 --- a/src/query/plan/planner.cpp +++ b/src/query/plan/planner.cpp @@ -256,7 +256,9 @@ class ReturnBodyContext : public TreeVisitorBase { using TreeVisitorBase::Visit; using TreeVisitorBase::PostVisit; - void Visit(Literal &) override { has_aggregation_.emplace_back(false); } + void Visit(PrimitiveLiteral &) override { has_aggregation_.emplace_back(false); } + + void Visit(ListLiteral &) override { has_aggregation_.emplace_back(false); } void Visit(Identifier &ident) override { const auto &symbol = symbol_table_.at(ident); diff --git a/src/query/typed_value.cpp b/src/query/typed_value.cpp index 19084d81e..79e2c4149 100644 --- a/src/query/typed_value.cpp +++ b/src/query/typed_value.cpp @@ -5,6 +5,7 @@ #include #include +#include "utils/algorithm.hpp" #include "utils/assert.hpp" #include "utils/exceptions.hpp" #include "utils/hashing/fnv.hpp" @@ -295,15 +296,14 @@ std::ostream &operator<<(std::ostream &os, const TypedValue &value) { return os << value.Value(); case TypedValue::Type::List: os << "["; - for (const auto &x : value.Value>()) { - os << x << ","; - } + PrintIterable(os, value.Value>()); return os << "]"; case TypedValue::Type::Map: os << "{"; - for (const auto &x : value.Value>()) { - os << x.first << ": " << x.second << ","; - } + PrintIterable(os, value.Value>(), ", ", + [](auto &stream, const auto &pair) { + stream << pair.first << ": " << pair.second; + }); return os << "}"; case TypedValue::Type::Vertex: return os << value.Value(); @@ -707,8 +707,7 @@ TypedValue operator&&(const TypedValue &a, const TypedValue &b) { // if either operand is false, the result is false if (a.type() == TypedValue::Type::Bool && !a.Value()) return false; if (b.type() == TypedValue::Type::Bool && !b.Value()) return false; - if (a.IsNull() || b.IsNull()) - return TypedValue::Null; + if (a.IsNull() || b.IsNull()) return TypedValue::Null; // neither is false, neither is null, thus both are true return true; } @@ -719,21 +718,18 @@ TypedValue operator||(const TypedValue &a, const TypedValue &b) { // if either operand is true, the result is true if (a.type() == TypedValue::Type::Bool && a.Value()) return true; if (b.type() == TypedValue::Type::Bool && b.Value()) return true; - if (a.IsNull() || b.IsNull()) - return TypedValue::Null; + if (a.IsNull() || b.IsNull()) return TypedValue::Null; // neither is true, neither is null, thus both are false return false; } TypedValue operator^(const TypedValue &a, const TypedValue &b) { - EnsureLogicallyOk(a,b, "logical XOR"); + EnsureLogicallyOk(a, b, "logical XOR"); // at this point we only have null and bool - if (a.IsNull() || - b.IsNull()) - return TypedValue::Null; - else - return static_cast(a.Value() ^ b.Value()); - + if (a.IsNull() || b.IsNull()) + return TypedValue::Null; + else + return static_cast(a.Value() ^ b.Value()); } bool TypedValue::BoolEqual::operator()(const TypedValue &lhs, diff --git a/src/storage/edge_accessor.cpp b/src/storage/edge_accessor.cpp index 2ee6af33f..0d2a74c45 100644 --- a/src/storage/edge_accessor.cpp +++ b/src/storage/edge_accessor.cpp @@ -22,11 +22,8 @@ bool EdgeAccessor::is_cycle() const { std::ostream &operator<<(std::ostream &os, const EdgeAccessor &ea) { os << "E[" << ea.db_accessor().edge_type_name(ea.edge_type()); os << " {"; - auto prop_to_string = [&](const auto kv) { - std::stringstream ss; - ss << ea.db_accessor().property_name(kv.first) << ": " << kv.second; - return ss.str(); - }; - PrintIterable(os, ea.Properties(), ", ", prop_to_string); + PrintIterable(os, ea.Properties(), ", ", [&](auto &stream, const auto &pair) { + stream << ea.db_accessor().property_name(pair.first) << ": " << pair.second; + }); return os << "}]"; } diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 3866eb472..bffc1e8d7 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -43,14 +43,12 @@ const std::vector &VertexAccessor::labels() const { std::ostream &operator<<(std::ostream &os, const VertexAccessor &va) { os << "V("; - PrintIterable(os, va.labels(), ":", - [&](auto label) { return va.db_accessor().label_name(label); }); + PrintIterable(os, va.labels(), ":", [&](auto &stream, auto label) { + stream << va.db_accessor().label_name(label); + }); os << " {"; - auto prop_to_string = [&](const auto kv) { - std::stringstream ss; - ss << va.db_accessor().property_name(kv.first) << ": " << kv.second; - return ss.str(); - }; - PrintIterable(os, va.Properties(), ", ", prop_to_string); + PrintIterable(os, va.Properties(), ", ", [&](auto &stream, const auto &pair) { + stream << va.db_accessor().property_name(pair.first) << ": " << pair.second; + }); return os << "})"; } diff --git a/src/utils/algorithm.hpp b/src/utils/algorithm.hpp index 0df79af07..ad13738c3 100644 --- a/src/utils/algorithm.hpp +++ b/src/utils/algorithm.hpp @@ -31,19 +31,33 @@ ForwardIt action_remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p, * @param stream Destination stream. * @param iterable An iterable collection of items. * @param delim Delimiter that is put between items. - * @param converter Function which converts an item to a type which supports - * @c operator<<. + * @param streamer Function which accepts a TStream and an item and + * streams the item to the stream. */ -template +template void PrintIterable(TStream &stream, const TIterable &iterable, - const std::string &delim = ", ", TConverter converter = {}) { + const std::string &delim = ", ", TStreamer streamer = {}) { bool first = true; for (const auto &item : iterable) { if (first) first = false; else stream << delim; - stream << converter(item); + streamer(stream, item); } } +/** + * Outputs a collection of items to the given stream, separating them with the + * given delimiter. + * + * @param stream Destination stream. + * @param iterable An iterable collection of items. + * @param delim Delimiter that is put between items. + */ +template +void PrintIterable(TStream &stream, const TIterable &iterable, + const std::string &delim = ", ") { + PrintIterable(stream, iterable, delim, + [](auto &stream, const auto &item) { stream << item; }); +} diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 182e51d47..e0fc5612e 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -94,7 +94,7 @@ TEST(CypherMainVisitor, ReturnLimit) { ASSERT_EQ(query->clauses_.size(), 1U); auto *return_clause = dynamic_cast(query->clauses_[0]); ASSERT_TRUE(return_clause->body_.limit); - auto *literal = dynamic_cast(return_clause->body_.limit); + auto *literal = dynamic_cast(return_clause->body_.limit); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 5); } @@ -105,7 +105,7 @@ TEST(CypherMainVisitor, ReturnSkip) { ASSERT_EQ(query->clauses_.size(), 1U); auto *return_clause = dynamic_cast(query->clauses_[0]); ASSERT_TRUE(return_clause->body_.skip); - auto *literal = dynamic_cast(return_clause->body_.skip); + auto *literal = dynamic_cast(return_clause->body_.skip); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 5); } @@ -140,7 +140,7 @@ TEST(CypherMainVisitorTest, IntegerLiteral) { AstGenerator ast_generator("RETURN 42"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 42); @@ -155,7 +155,7 @@ TEST(CypherMainVisitorTest, BooleanLiteralTrue) { AstGenerator ast_generator("RETURN TrUe"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), true); @@ -165,7 +165,7 @@ TEST(CypherMainVisitorTest, BooleanLiteralFalse) { AstGenerator ast_generator("RETURN faLSE"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), false); @@ -175,7 +175,7 @@ TEST(CypherMainVisitorTest, NullLiteral) { AstGenerator ast_generator("RETURN nULl"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.type(), TypedValue::Type::Null); @@ -185,7 +185,7 @@ TEST(CypherMainVisitorTest, ParenthesizedExpression) { AstGenerator ast_generator("RETURN (2)"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 2); @@ -201,10 +201,10 @@ TEST(CypherMainVisitorTest, OrOperator) { ASSERT_TRUE(or_operator2); auto *or_operator1 = dynamic_cast(or_operator2->expression1_); ASSERT_TRUE(or_operator1); - auto *operand1 = dynamic_cast(or_operator1->expression1_); + auto *operand1 = dynamic_cast(or_operator1->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); - auto *operand2 = dynamic_cast(or_operator1->expression2_); + auto *operand2 = dynamic_cast(or_operator1->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); auto *operand3 = dynamic_cast(or_operator2->expression2_); @@ -218,10 +218,10 @@ TEST(CypherMainVisitorTest, XorOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *xor_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(xor_operator->expression1_); + auto *operand1 = dynamic_cast(xor_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); - auto *operand2 = dynamic_cast(xor_operator->expression2_); + auto *operand2 = dynamic_cast(xor_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); } @@ -232,10 +232,10 @@ TEST(CypherMainVisitorTest, AndOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *and_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(and_operator->expression1_); + auto *operand1 = dynamic_cast(and_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); - auto *operand2 = dynamic_cast(and_operator->expression2_); + auto *operand2 = dynamic_cast(and_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); } @@ -250,13 +250,16 @@ TEST(CypherMainVisitorTest, AdditionSubtractionOperators) { auto *subtraction_operator = dynamic_cast(addition_operator->expression1_); ASSERT_TRUE(subtraction_operator); - auto *operand1 = dynamic_cast(subtraction_operator->expression1_); + auto *operand1 = + dynamic_cast(subtraction_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 1); - auto *operand2 = dynamic_cast(subtraction_operator->expression2_); + auto *operand2 = + dynamic_cast(subtraction_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 2); - auto *operand3 = dynamic_cast(addition_operator->expression2_); + auto *operand3 = + dynamic_cast(addition_operator->expression2_); ASSERT_TRUE(operand3); ASSERT_EQ(operand3->value_.Value(), 3); } @@ -267,10 +270,12 @@ TEST(CypherMainVisitorTest, MulitplicationOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *mult_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(mult_operator->expression1_); + auto *operand1 = + dynamic_cast(mult_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); - auto *operand2 = dynamic_cast(mult_operator->expression2_); + auto *operand2 = + dynamic_cast(mult_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } @@ -281,10 +286,10 @@ TEST(CypherMainVisitorTest, DivisionOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *div_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(div_operator->expression1_); + auto *operand1 = dynamic_cast(div_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); - auto *operand2 = dynamic_cast(div_operator->expression2_); + auto *operand2 = dynamic_cast(div_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } @@ -295,10 +300,10 @@ TEST(CypherMainVisitorTest, ModOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *mod_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(mod_operator->expression1_); + auto *operand1 = dynamic_cast(mod_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); - auto *operand2 = dynamic_cast(mod_operator->expression2_); + auto *operand2 = dynamic_cast(mod_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } @@ -310,9 +315,11 @@ TEST(CypherMainVisitorTest, ModOperator) { _operator = and_operator->expression1_; \ auto *cmp_operator = dynamic_cast(and_operator->expression2_); \ ASSERT_TRUE(cmp_operator); \ - auto *operand1 = dynamic_cast(cmp_operator->expression1_); \ + auto *operand1 = \ + dynamic_cast(cmp_operator->expression1_); \ ASSERT_EQ(operand1->value_.Value(), VALUE1); \ - auto *operand2 = dynamic_cast(cmp_operator->expression2_); \ + auto *operand2 = \ + dynamic_cast(cmp_operator->expression2_); \ ASSERT_EQ(operand2->value_.Value(), VALUE2); \ } while (0) @@ -330,9 +337,9 @@ TEST(CypherMainVisitorTest, ComparisonOperators) { CHECK_COMPARISON(NotEqualOperator, 3, 4); auto *cmp_operator = dynamic_cast(_operator); ASSERT_TRUE(cmp_operator); - auto *operand1 = dynamic_cast(cmp_operator->expression1_); + auto *operand1 = dynamic_cast(cmp_operator->expression1_); ASSERT_EQ(operand1->value_.Value(), 2); - auto *operand2 = dynamic_cast(cmp_operator->expression2_); + auto *operand2 = dynamic_cast(cmp_operator->expression2_); ASSERT_EQ(operand2->value_.Value(), 3); } @@ -344,7 +351,8 @@ TEST(CypherMainVisitorTest, IsNull) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *is_type_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand1 = dynamic_cast(is_type_operator->expression_); + auto *operand1 = + dynamic_cast(is_type_operator->expression_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); } @@ -357,7 +365,8 @@ TEST(CypherMainVisitorTest, IsNotNull) { return_clause->body_.named_expressions[0]->expression_); auto *is_type_operator = dynamic_cast(not_operator->expression_); - auto *operand1 = dynamic_cast(is_type_operator->expression_); + auto *operand1 = + dynamic_cast(is_type_operator->expression_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); } @@ -368,7 +377,7 @@ TEST(CypherMainVisitorTest, NotOperator) { auto *return_clause = dynamic_cast(query->clauses_[0]); auto *not_operator = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); - auto *operand = dynamic_cast(not_operator->expression_); + auto *operand = dynamic_cast(not_operator->expression_); ASSERT_TRUE(operand); ASSERT_EQ(operand->value_.Value(), true); } @@ -383,7 +392,8 @@ TEST(CypherMainVisitorTest, UnaryMinusPlusOperators) { auto *unary_plus_operator = dynamic_cast(unary_minus_operator->expression_); ASSERT_TRUE(unary_plus_operator); - auto *operand = dynamic_cast(unary_plus_operator->expression_); + auto *operand = + dynamic_cast(unary_plus_operator->expression_); ASSERT_TRUE(operand); ASSERT_EQ(operand->value_.Value(), 5); } @@ -430,7 +440,7 @@ TEST(CypherMainVisitorTest, StringLiteralDoubleQuotes) { AstGenerator ast_generator("RETURN \"mi'rko\""); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "mi'rko"); @@ -440,7 +450,7 @@ TEST(CypherMainVisitorTest, StringLiteralSingleQuotes) { AstGenerator ast_generator("RETURN 'mi\"rko'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "mi\"rko"); @@ -451,7 +461,7 @@ TEST(CypherMainVisitorTest, StringLiteralEscapedChars) { "RETURN '\\\\\\'\\\"\\b\\B\\f\\F\\n\\N\\r\\R\\t\\T'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "\\'\"\b\b\f\f\n\n\r\r\t\t"); @@ -461,7 +471,7 @@ TEST(CypherMainVisitorTest, StringLiteralEscapedUtf16) { AstGenerator ast_generator("RETURN '\\u221daaa\\U221daaa'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), u8"\u221daaa\u221daaa"); @@ -471,7 +481,7 @@ TEST(CypherMainVisitorTest, StringLiteralEscapedUtf32) { AstGenerator ast_generator("RETURN '\\u0001F600aaaa\\U0001F600aaaaaaaa'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), @@ -482,7 +492,7 @@ TEST(CypherMainVisitorTest, DoubleLiteral) { AstGenerator ast_generator("RETURN 3.5"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 3.5); @@ -492,12 +502,31 @@ TEST(CypherMainVisitorTest, DoubleLiteralExponent) { AstGenerator ast_generator("RETURN 5e-1"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); - auto *literal = dynamic_cast( + auto *literal = dynamic_cast( return_clause->body_.named_expressions[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 0.5); } +TEST(CypherMainVisitorTest, ListLiteral) { + AstGenerator ast_generator("RETURN [3, [], 'johhny']"); + auto *query = ast_generator.query_; + auto *return_clause = dynamic_cast(query->clauses_[0]); + auto *list_literal = dynamic_cast( + return_clause->body_.named_expressions[0]->expression_); + ASSERT_TRUE(list_literal); + ASSERT_EQ(3, list_literal->elements_.size()); + auto *elem_0 = dynamic_cast(list_literal->elements_[0]); + ASSERT_TRUE(elem_0); + EXPECT_EQ(elem_0->value_.type(), TypedValue::Type::Int); + auto *elem_1 = dynamic_cast(list_literal->elements_[1]); + ASSERT_TRUE(elem_1); + EXPECT_EQ(0, elem_1->elements_.size()); + auto *elem_2 = dynamic_cast(list_literal->elements_[2]); + ASSERT_TRUE(elem_2); + EXPECT_EQ(elem_2->value_.type(), TypedValue::Type::String); +} + TEST(CypherMainVisitorTest, NodePattern) { AstGenerator ast_generator( "MATCH (:label1:label2:label3 {a : 5, b : 10}) RETURN 1"); @@ -521,7 +550,7 @@ TEST(CypherMainVisitorTest, NodePattern) { ast_generator.db_accessor_->label("label3"))); std::unordered_map properties; for (auto x : node->properties_) { - auto *literal = dynamic_cast(x.second); + auto *literal = dynamic_cast(x.second); ASSERT_TRUE(literal); ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int); properties[x.first] = literal->value_.Value(); @@ -608,7 +637,7 @@ TEST(CypherMainVisitorTest, RelationshipPatternDetails) { ast_generator.db_accessor_->edge_type("type2"))); std::unordered_map properties; for (auto x : edge->properties_) { - auto *literal = dynamic_cast(x.second); + auto *literal = dynamic_cast(x.second); ASSERT_TRUE(literal); ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int); properties[x.first] = literal->value_.Value(); @@ -976,5 +1005,4 @@ TEST(CypherMainVisitorTest, Merge) { ASSERT_EQ(merge->on_create_.size(), 1U); EXPECT_TRUE(dynamic_cast(merge->on_create_[0])); } - } diff --git a/tests/unit/query_common.hpp b/tests/unit/query_common.hpp index d23f5945a..4ad14b4ac 100644 --- a/tests/unit/query_common.hpp +++ b/tests/unit/query_common.hpp @@ -356,7 +356,7 @@ auto GetMerge(AstTreeStorage &storage, Pattern *pattern, OnMatch on_match, query::test_common::GetWithPatterns(storage.Create(), \ {__VA_ARGS__}) #define IDENT(name) storage.Create((name)) -#define LITERAL(val) storage.Create((val)) +#define LITERAL(val) storage.Create((val)) #define PROPERTY_LOOKUP(...) \ query::test_common::GetPropertyLookup(storage, __VA_ARGS__) #define NEXPR(name, expr) storage.Create((name), (expr)) diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index e9369b26c..3ef780b61 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -37,7 +37,7 @@ TypedValue EvaluateFunction(const std::string &function_name, std::vector expressions; for (const auto &arg : args) { - expressions.push_back(storage.Create(arg)); + expressions.push_back(storage.Create(arg)); } auto *op = storage.Create(NameToFunction(function_name), expressions); @@ -48,12 +48,13 @@ TypedValue EvaluateFunction(const std::string &function_name, TEST(ExpressionEvaluator, OrOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(true), - storage.Create(false)); + auto *op = + storage.Create(storage.Create(true), + storage.Create(false)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(true), - storage.Create(true)); + op = storage.Create(storage.Create(true), + storage.Create(true)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } @@ -61,12 +62,13 @@ TEST(ExpressionEvaluator, OrOperator) { TEST(ExpressionEvaluator, XorOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(true), - storage.Create(false)); + auto *op = + storage.Create(storage.Create(true), + storage.Create(false)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(true), - storage.Create(true)); + op = storage.Create(storage.Create(true), + storage.Create(true)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); } @@ -74,12 +76,13 @@ TEST(ExpressionEvaluator, XorOperator) { TEST(ExpressionEvaluator, AndOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(true), - storage.Create(true)); + auto *op = + storage.Create(storage.Create(true), + storage.Create(true)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(false), - storage.Create(true)); + op = storage.Create(storage.Create(false), + storage.Create(true)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); } @@ -87,8 +90,8 @@ TEST(ExpressionEvaluator, AndOperator) { TEST(ExpressionEvaluator, AdditionOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(2), - storage.Create(3)); + auto *op = storage.Create( + storage.Create(2), storage.Create(3)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), 5); } @@ -96,8 +99,8 @@ TEST(ExpressionEvaluator, AdditionOperator) { TEST(ExpressionEvaluator, SubtractionOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(2), - storage.Create(3)); + auto *op = storage.Create( + storage.Create(2), storage.Create(3)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), -1); } @@ -105,8 +108,8 @@ TEST(ExpressionEvaluator, SubtractionOperator) { TEST(ExpressionEvaluator, MultiplicationOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(2), - storage.Create(3)); + auto *op = storage.Create( + storage.Create(2), storage.Create(3)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), 6); } @@ -114,8 +117,9 @@ TEST(ExpressionEvaluator, MultiplicationOperator) { TEST(ExpressionEvaluator, DivisionOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(50), - storage.Create(10)); + auto *op = + storage.Create(storage.Create(50), + storage.Create(10)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), 5); } @@ -123,8 +127,8 @@ TEST(ExpressionEvaluator, DivisionOperator) { TEST(ExpressionEvaluator, ModOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(65), - storage.Create(10)); + auto *op = storage.Create(storage.Create(65), + storage.Create(10)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), 5); } @@ -132,16 +136,17 @@ TEST(ExpressionEvaluator, ModOperator) { TEST(ExpressionEvaluator, EqualOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = + storage.Create(storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create(storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create(storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); } @@ -149,16 +154,17 @@ TEST(ExpressionEvaluator, EqualOperator) { TEST(ExpressionEvaluator, NotEqualOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = + storage.Create(storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create(storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create(storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } @@ -166,16 +172,16 @@ TEST(ExpressionEvaluator, NotEqualOperator) { TEST(ExpressionEvaluator, LessOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = storage.Create(storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create(storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create(storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); } @@ -183,16 +189,17 @@ TEST(ExpressionEvaluator, LessOperator) { TEST(ExpressionEvaluator, GreaterOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = + storage.Create(storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create(storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create(storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } @@ -200,16 +207,17 @@ TEST(ExpressionEvaluator, GreaterOperator) { TEST(ExpressionEvaluator, LessEqualOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = + storage.Create(storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create(storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create(storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); } @@ -217,16 +225,19 @@ TEST(ExpressionEvaluator, LessEqualOperator) { TEST(ExpressionEvaluator, GreaterEqualOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(10), - storage.Create(15)); + auto *op = storage.Create( + storage.Create(10), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = storage.Create(storage.Create(15), - storage.Create(15)); + op = storage.Create( + storage.Create(15), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); - op = storage.Create(storage.Create(20), - storage.Create(15)); + op = storage.Create( + storage.Create(20), + storage.Create(15)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } @@ -234,7 +245,8 @@ TEST(ExpressionEvaluator, GreaterEqualOperator) { TEST(ExpressionEvaluator, NotOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(false)); + auto *op = + storage.Create(storage.Create(false)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } @@ -242,7 +254,8 @@ TEST(ExpressionEvaluator, NotOperator) { TEST(ExpressionEvaluator, UnaryPlusOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(5)); + auto *op = + storage.Create(storage.Create(5)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), 5); } @@ -250,7 +263,8 @@ TEST(ExpressionEvaluator, UnaryPlusOperator) { TEST(ExpressionEvaluator, UnaryMinusOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(5)); + auto *op = + storage.Create(storage.Create(5)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), -5); } @@ -258,18 +272,19 @@ TEST(ExpressionEvaluator, UnaryMinusOperator) { TEST(ExpressionEvaluator, IsNullOperator) { AstTreeStorage storage; NoContextExpressionEvaluator eval; - auto *op = storage.Create(storage.Create(1)); + auto *op = + storage.Create(storage.Create(1)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), false); - op = - storage.Create(storage.Create(TypedValue::Null)); + op = storage.Create( + storage.Create(TypedValue::Null)); op->Accept(eval.eval); ASSERT_EQ(eval.eval.PopBack().Value(), true); } TEST(ExpressionEvaluator, Aggregation) { AstTreeStorage storage; - auto aggr = storage.Create(storage.Create(42), + auto aggr = storage.Create(storage.Create(42), Aggregation::Op::COUNT); SymbolTable symbol_table; auto aggr_sym = symbol_table.CreateSymbol("aggr"); @@ -283,6 +298,23 @@ TEST(ExpressionEvaluator, Aggregation) { EXPECT_EQ(eval.PopBack().Value(), 1); } +TEST(ExpressionEvaluator, ListLiteral) { + AstTreeStorage storage; + NoContextExpressionEvaluator eval; + auto *list_literal = storage.Create( + std::vector{storage.Create(1), + storage.Create("bla"), + storage.Create(true)}); + list_literal->Accept(eval.eval); + TypedValue result = eval.eval.PopBack(); + ASSERT_EQ(result.type(), TypedValue::Type::List); + auto &result_elems = result.Value>(); + ASSERT_EQ(3, result_elems.size()); + EXPECT_EQ(result_elems[0].type(), TypedValue::Type::Int); + EXPECT_EQ(result_elems[1].type(), TypedValue::Type::String); + EXPECT_EQ(result_elems[2].type(), TypedValue::Type::Bool); +} + TEST(ExpressionEvaluator, FunctionCoalesce) { ASSERT_THROW(EvaluateFunction("COALESCE", {}), QueryRuntimeException); ASSERT_EQ(