#include #include #include #include #include #include #include #include "query/context.hpp" #include "query/frontend/ast/ast.hpp" #include "query/frontend/opencypher/parser.hpp" #include "query/interpret/awesome_memgraph_functions.hpp" #include "query/interpret/eval.hpp" #include "query/interpret/frame.hpp" #include "query/path.hpp" #include "storage/v2/storage.hpp" #include "utils/string.hpp" #include "query_common.hpp" using namespace query; using query::test_common::ToIntList; using testing::ElementsAre; using testing::UnorderedElementsAre; namespace { class ExpressionEvaluatorTest : public ::testing::Test { protected: storage::Storage db; storage::Storage::Accessor storage_dba{db.Access()}; query::DbAccessor dba{&storage_dba}; AstStorage storage; utils::MonotonicBufferResource mem{1024}; EvaluationContext ctx{&mem}; SymbolTable symbol_table; Frame frame{128}; ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, storage::View::OLD}; Identifier *CreateIdentifierWithValue(std::string name, const TypedValue &value) { auto id = storage.Create(name, true); auto symbol = symbol_table.CreateSymbol(name, true); id->MapTo(symbol); frame[symbol] = value; return id; } template auto Eval(TExpression *expr) { ctx.properties = NamesToProperties(storage.properties_, &dba); ctx.labels = NamesToLabels(storage.labels_, &dba); auto value = expr->Accept(eval); EXPECT_EQ(value.GetMemoryResource(), &mem) << "ExpressionEvaluator must use the MemoryResource from " "EvaluationContext for allocations!"; return value; } }; TEST_F(ExpressionEvaluatorTest, OrOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(false)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(true), storage.Create(true)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, XorOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(false)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(true), storage.Create(true)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), false); } TEST_F(ExpressionEvaluatorTest, AndOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(true)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(false), storage.Create(true)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), false); } TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) { { auto *op = storage.Create(storage.Create(false), storage.Create(5)); auto value = Eval(op); EXPECT_EQ(value.ValueBool(), false); } { auto *op = storage.Create(storage.Create(5), storage.Create(false)); // We are evaluating left to right, so we don't short circuit here and // raise due to `5`. This differs from neo4j, where they evaluate both // sides and return `false` without checking for type of the first // expression. EXPECT_THROW(Eval(op), QueryRuntimeException); } } TEST_F(ExpressionEvaluatorTest, AndOperatorNull) { { // Null doesn't short circuit auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(5)); EXPECT_THROW(Eval(op), QueryRuntimeException); } { auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(true)); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(false)); auto value = Eval(op); ASSERT_TRUE(value.IsBool()); EXPECT_EQ(value.ValueBool(), false); } } TEST_F(ExpressionEvaluatorTest, AdditionOperator) { auto *op = storage.Create(storage.Create(2), storage.Create(3)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TEST_F(ExpressionEvaluatorTest, SubtractionOperator) { auto *op = storage.Create(storage.Create(2), storage.Create(3)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), -1); } TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) { auto *op = storage.Create(storage.Create(2), storage.Create(3)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 6); } TEST_F(ExpressionEvaluatorTest, DivisionOperator) { auto *op = storage.Create(storage.Create(50), storage.Create(10)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TEST_F(ExpressionEvaluatorTest, ModOperator) { auto *op = storage.Create(storage.Create(65), storage.Create(10)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TEST_F(ExpressionEvaluatorTest, EqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TEST_F(ExpressionEvaluatorTest, NotEqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, LessOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TEST_F(ExpressionEvaluatorTest, GreaterOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, LessEqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(15), storage.Create(15)); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = storage.Create(storage.Create(20), storage.Create(15)); auto val3 = Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, InListOperator) { auto *list_literal = storage.Create(std::vector{ storage.Create(1), storage.Create(2), storage.Create("a")}); { // Element exists in list. auto *op = storage.Create(storage.Create(2), list_literal); auto value = Eval(op); EXPECT_EQ(value.ValueBool(), true); } { // Element doesn't exist in list. auto *op = storage.Create(storage.Create("x"), list_literal); auto value = Eval(op); EXPECT_EQ(value.ValueBool(), false); } { auto *list_literal = storage.Create( std::vector{storage.Create(storage::PropertyValue()), storage.Create(2), storage.Create("a")}); // Element doesn't exist in list with null element. auto *op = storage.Create(storage.Create("x"), list_literal); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null list. auto *op = storage.Create(storage.Create("x"), storage.Create(storage::PropertyValue())); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null literal. auto *op = storage.Create(storage.Create(storage::PropertyValue()), list_literal); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null literal, empty list. auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(std::vector())); auto value = Eval(op); EXPECT_FALSE(value.ValueBool()); } } TEST_F(ExpressionEvaluatorTest, ListIndexing) { auto *list_literal = storage.Create( std::vector{storage.Create(1), storage.Create(2), storage.Create(3), storage.Create(4)}); { // Legal indexing. auto *op = storage.Create(list_literal, storage.Create(2)); auto value = Eval(op); EXPECT_EQ(value.ValueInt(), 3); } { // Out of bounds indexing. auto *op = storage.Create(list_literal, storage.Create(4)); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Out of bounds indexing with negative bound. auto *op = storage.Create(list_literal, storage.Create(-100)); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Legal indexing with negative index. auto *op = storage.Create(list_literal, storage.Create(-2)); auto value = Eval(op); EXPECT_EQ(value.ValueInt(), 3); } { // Indexing with one operator being null. auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(-2)); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Indexing with incompatible type. auto *op = storage.Create(list_literal, storage.Create("bla")); EXPECT_THROW(Eval(op), QueryRuntimeException); } } TEST_F(ExpressionEvaluatorTest, MapIndexing) { auto *map_literal = storage.Create( std::unordered_map{{storage.GetPropertyIx("a"), storage.Create(1)}, {storage.GetPropertyIx("b"), storage.Create(2)}, {storage.GetPropertyIx("c"), storage.Create(3)}}); { // Legal indexing. auto *op = storage.Create(map_literal, storage.Create("b")); auto value = Eval(op); EXPECT_EQ(value.ValueInt(), 2); } { // Legal indexing, non-existing key. auto *op = storage.Create(map_literal, storage.Create("z")); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } { // Wrong key type. auto *op = storage.Create(map_literal, storage.Create(42)); EXPECT_THROW(Eval(op), QueryRuntimeException); } { // Indexing with Null. auto *op = storage.Create(map_literal, storage.Create(storage::PropertyValue())); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } } TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { auto edge_type = dba.NameToEdgeType("edge_type"); auto prop = dba.NameToProperty("prop"); auto v1 = dba.InsertVertex(); auto e11 = dba.InsertEdge(&v1, &v1, edge_type); ASSERT_TRUE(e11.HasValue()); ASSERT_TRUE(v1.SetProperty(prop, storage::PropertyValue(42)).HasValue()); ASSERT_TRUE(e11->SetProperty(prop, storage::PropertyValue(43)).HasValue()); dba.AdvanceCommand(); auto *vertex_id = CreateIdentifierWithValue("v1", TypedValue(v1)); auto *edge_id = CreateIdentifierWithValue("e11", TypedValue(*e11)); { // Legal indexing. auto *op1 = storage.Create(vertex_id, storage.Create("prop")); auto value1 = Eval(op1); EXPECT_EQ(value1.ValueInt(), 42); auto *op2 = storage.Create(edge_id, storage.Create("prop")); auto value2 = Eval(op2); EXPECT_EQ(value2.ValueInt(), 43); } { // Legal indexing, non-existing key. auto *op1 = storage.Create(vertex_id, storage.Create("blah")); auto value1 = Eval(op1); EXPECT_TRUE(value1.IsNull()); auto *op2 = storage.Create(edge_id, storage.Create("blah")); auto value2 = Eval(op2); EXPECT_TRUE(value2.IsNull()); } { // Wrong key type. auto *op1 = storage.Create(vertex_id, storage.Create(1)); EXPECT_THROW(Eval(op1), QueryRuntimeException); auto *op2 = storage.Create(edge_id, storage.Create(1)); EXPECT_THROW(Eval(op2), QueryRuntimeException); } { // Indexing with Null. auto *op1 = storage.Create(vertex_id, storage.Create(storage::PropertyValue())); auto value1 = Eval(op1); EXPECT_TRUE(value1.IsNull()); auto *op2 = storage.Create(edge_id, storage.Create(storage::PropertyValue())); auto value2 = Eval(op2); EXPECT_TRUE(value2.IsNull()); } } TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) { auto *list_literal = storage.Create( std::vector{storage.Create(1), storage.Create(2), storage.Create(3), storage.Create(4)}); auto extract_ints = [](TypedValue list) { std::vector int_list; for (auto x : list.ValueList()) { int_list.push_back(x.ValueInt()); } return int_list; }; { // Legal slicing with both bounds defined. auto *op = storage.Create(list_literal, storage.Create(2), storage.Create(4)); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { // Legal slicing with negative bound. auto *op = storage.Create(list_literal, storage.Create(2), storage.Create(-1)); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3)); } { // Lower bound larger than upper bound. auto *op = storage.Create(list_literal, storage.Create(2), storage.Create(-4)); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre()); } { // Bounds ouf or range. auto *op = storage.Create(list_literal, storage.Create(-100), storage.Create(10)); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4)); } { // Lower bound undefined. auto *op = storage.Create(list_literal, nullptr, storage.Create(3)); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3)); } { // Upper bound undefined. auto *op = storage.Create(list_literal, storage.Create(-2), nullptr); auto value = Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { // Bound of illegal type and null value bound. auto *op = storage.Create(list_literal, storage.Create(storage::PropertyValue()), storage.Create("mirko")); EXPECT_THROW(Eval(op), QueryRuntimeException); } { // List of illegal type. auto *op = storage.Create(storage.Create("a"), storage.Create(-2), nullptr); EXPECT_THROW(Eval(op), QueryRuntimeException); } { // Null value list with undefined upper bound. auto *op = storage.Create(storage.Create(storage::PropertyValue()), storage.Create(-2), nullptr); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); ; } { // Null value index. auto *op = storage.Create(list_literal, storage.Create(-2), storage.Create(storage::PropertyValue())); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); ; } } TEST_F(ExpressionEvaluatorTest, IfOperator) { auto *then_expression = storage.Create(10); auto *else_expression = storage.Create(20); { auto *condition_true = storage.Create(storage.Create(2), storage.Create(2)); auto *op = storage.Create(condition_true, then_expression, else_expression); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 10); } { auto *condition_false = storage.Create(storage.Create(2), storage.Create(3)); auto *op = storage.Create(condition_false, then_expression, else_expression); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 20); } { auto *condition_exception = storage.Create(storage.Create(2), storage.Create(3)); auto *op = storage.Create(condition_exception, then_expression, else_expression); ASSERT_THROW(Eval(op), QueryRuntimeException); } } TEST_F(ExpressionEvaluatorTest, NotOperator) { auto *op = storage.Create(storage.Create(false)); auto value = Eval(op); ASSERT_EQ(value.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) { auto *op = storage.Create(storage.Create(5)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) { auto *op = storage.Create(storage.Create(5)); auto value = Eval(op); ASSERT_EQ(value.ValueInt(), -5); } TEST_F(ExpressionEvaluatorTest, IsNullOperator) { auto *op = storage.Create(storage.Create(1)); auto val1 = Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(storage::PropertyValue())); auto val2 = Eval(op); ASSERT_EQ(val2.ValueBool(), true); } TEST_F(ExpressionEvaluatorTest, LabelsTest) { auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("ANIMAL")).HasValue()); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("DOG")).HasValue()); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("NICE_DOG")).HasValue()); dba.AdvanceCommand(); auto *identifier = storage.Create("n"); auto node_symbol = symbol_table.CreateSymbol("n", true); identifier->MapTo(node_symbol); frame[node_symbol] = TypedValue(v1); { auto *op = storage.Create( identifier, std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("ANIMAL")}); auto value = Eval(op); EXPECT_EQ(value.ValueBool(), true); } { auto *op = storage.Create( identifier, std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), storage.GetLabelIx("ANIMAL")}); auto value = Eval(op); EXPECT_EQ(value.ValueBool(), false); } { frame[node_symbol] = TypedValue(); auto *op = storage.Create( identifier, std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), storage.GetLabelIx("ANIMAL")}); auto value = Eval(op); EXPECT_TRUE(value.IsNull()); } } TEST_F(ExpressionEvaluatorTest, Aggregation) { auto aggr = storage.Create(storage.Create(42), nullptr, Aggregation::Op::COUNT); auto aggr_sym = symbol_table.CreateSymbol("aggr", true); aggr->MapTo(aggr_sym); frame[aggr_sym] = TypedValue(1); auto value = Eval(aggr); EXPECT_EQ(value.ValueInt(), 1); } TEST_F(ExpressionEvaluatorTest, ListLiteral) { auto *list_literal = storage.Create(std::vector{storage.Create(1), storage.Create("bla"), storage.Create(true)}); TypedValue result = Eval(list_literal); ASSERT_TRUE(result.IsList()); auto &result_elems = result.ValueList(); ASSERT_EQ(3, result_elems.size()); EXPECT_TRUE(result_elems[0].IsInt()); ; EXPECT_TRUE(result_elems[1].IsString()); ; EXPECT_TRUE(result_elems[2].IsBool()); ; } TEST_F(ExpressionEvaluatorTest, ParameterLookup) { ctx.parameters.Add(0, storage::PropertyValue(42)); auto *param_lookup = storage.Create(0); auto value = Eval(param_lookup); ASSERT_TRUE(value.IsInt()); EXPECT_EQ(value.ValueInt(), 42); } TEST_F(ExpressionEvaluatorTest, FunctionAll1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(1), LITERAL(1)), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAll2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) { AstStorage storage; auto *all = ALL("x", LITERAL(storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); auto value = Eval(all); EXPECT_TRUE(value.IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(2), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) { AstStorage storage; auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); EXPECT_THROW(Eval(all), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, FunctionSingle1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionSingle2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(GREATER(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) { AstStorage storage; auto *single = SINGLE("x", LITERAL(storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); auto value = Eval(single); EXPECT_TRUE(value.IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(2), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAny1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(any); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAny2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(any); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAnyNullList) { AstStorage storage; auto *any = ANY("x", LITERAL(storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); auto value = Eval(any); EXPECT_TRUE(value.IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(0), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(any); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(any); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionAnyWhereWrongType) { AstStorage storage; auto *any = ANY("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); EXPECT_THROW(Eval(any), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, FunctionNone1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(none); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionNone2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(none); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionNoneNullList) { AstStorage storage; auto *none = NONE("x", LITERAL(storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); auto value = Eval(none); EXPECT_TRUE(value.IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = NONE("x", LIST(LITERAL(1), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(any); EXPECT_TRUE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *none = NONE("x", LIST(LITERAL(0), LITERAL(storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(none); EXPECT_FALSE(value.ValueBool()); } TEST_F(ExpressionEvaluatorTest, FunctionNoneWhereWrongType) { AstStorage storage; auto *none = NONE("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); EXPECT_THROW(Eval(none), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, FunctionReduce) { AstStorage storage; auto *ident_sum = IDENT("sum"); auto *ident_x = IDENT("x"); auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)), ADD(ident_sum, ident_x)); const auto sum_sym = symbol_table.CreateSymbol("sum", true); reduce->accumulator_->MapTo(sum_sym); ident_sum->MapTo(sum_sym); const auto x_sym = symbol_table.CreateSymbol("x", true); reduce->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(reduce); ASSERT_TRUE(value.IsInt()); EXPECT_EQ(value.ValueInt(), 3); } TEST_F(ExpressionEvaluatorTest, FunctionExtract) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(storage::PropertyValue())), ADD(ident_x, LITERAL(1))); const auto x_sym = symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(extract); EXPECT_TRUE(value.IsList()); ; auto result = value.ValueList(); EXPECT_EQ(result[0].ValueInt(), 2); EXPECT_EQ(result[1].ValueInt(), 3); EXPECT_TRUE(result[2].IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionExtractNull) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LITERAL(storage::PropertyValue()), ADD(ident_x, LITERAL(1))); const auto x_sym = symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = Eval(extract); EXPECT_TRUE(value.IsNull()); } TEST_F(ExpressionEvaluatorTest, FunctionExtractExceptions) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1))); const auto x_sym = symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); EXPECT_THROW(Eval(extract), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, Coalesce) { // coalesce() EXPECT_THROW(Eval(COALESCE()), QueryRuntimeException); // coalesce(null, null) EXPECT_TRUE(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(TypedValue()))).IsNull()); // coalesce(null, 2, 3) EXPECT_EQ(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), LITERAL(3))).ValueInt(), 2); // coalesce(null, 2, assert(false), 3) EXPECT_EQ(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), FN("ASSERT", LITERAL(false)), LITERAL(3))).ValueInt(), 2); // (null, assert(false)) EXPECT_THROW(Eval(COALESCE(LITERAL(TypedValue()), FN("ASSERT", LITERAL(false)))), QueryRuntimeException); // coalesce([null, null]) EXPECT_FALSE(Eval(COALESCE(LITERAL(TypedValue(std::vector{TypedValue(), TypedValue()})))).IsNull()); } TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidArguments) { EXPECT_TRUE(Eval(storage.Create(LITERAL(TypedValue()), LITERAL("regex"))).IsNull()); EXPECT_TRUE(Eval(storage.Create(LITERAL(3), LITERAL("regex"))).IsNull()); EXPECT_TRUE(Eval(storage.Create(LIST(LITERAL("string")), LITERAL("regex"))).IsNull()); EXPECT_TRUE(Eval(storage.Create(LITERAL("string"), LITERAL(TypedValue()))).IsNull()); EXPECT_THROW(Eval(storage.Create(LITERAL("string"), LITERAL(42))), QueryRuntimeException); EXPECT_THROW(Eval(storage.Create(LITERAL("string"), LIST(LITERAL("regex")))), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidRegex) { EXPECT_THROW(Eval(storage.Create(LITERAL("text"), LITERAL("*ext"))), QueryRuntimeException); EXPECT_THROW(Eval(storage.Create(LITERAL("text"), LITERAL("[ext"))), QueryRuntimeException); } TEST_F(ExpressionEvaluatorTest, RegexMatch) { EXPECT_FALSE(Eval(storage.Create(LITERAL("text"), LITERAL(".*ex"))).ValueBool()); EXPECT_TRUE(Eval(storage.Create(LITERAL("text"), LITERAL(".*ext"))).ValueBool()); EXPECT_FALSE(Eval(storage.Create(LITERAL("text"), LITERAL("[ext]"))).ValueBool()); EXPECT_TRUE(Eval(storage.Create(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool()); } class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { protected: std::pair prop_age = std::make_pair("age", dba.NameToProperty("age")); std::pair prop_height = std::make_pair("height", dba.NameToProperty("height")); Identifier *identifier = storage.Create("element"); Symbol symbol = symbol_table.CreateSymbol("element", true); void SetUp() { identifier->MapTo(symbol); } auto Value(std::pair property) { auto *op = storage.Create(identifier, storage.GetPropertyIx(property.first)); return Eval(op); } }; TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(prop_age.second, storage::PropertyValue(10)).HasValue()); dba.AdvanceCommand(); frame[symbol] = TypedValue(v1); EXPECT_EQ(Value(prop_age).ValueInt(), 10); EXPECT_TRUE(Value(prop_height).IsNull()); } TEST_F(ExpressionEvaluatorPropertyLookup, Edge) { auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(e12->SetProperty(prop_age.second, storage::PropertyValue(10)).HasValue()); dba.AdvanceCommand(); frame[symbol] = TypedValue(*e12); EXPECT_EQ(Value(prop_age).ValueInt(), 10); EXPECT_TRUE(Value(prop_height).IsNull()); } TEST_F(ExpressionEvaluatorPropertyLookup, Null) { frame[symbol] = TypedValue(); EXPECT_TRUE(Value(prop_age).IsNull()); } TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) { frame[symbol] = TypedValue(std::map{{prop_age.first, TypedValue(10)}}); EXPECT_EQ(Value(prop_age).ValueInt(), 10); EXPECT_TRUE(Value(prop_height).IsNull()); } class FunctionTest : public ExpressionEvaluatorTest { protected: std::vector ExpressionsFromTypedValues(const std::vector &tvs) { std::vector expressions; expressions.reserve(tvs.size()); for (size_t i = 0; i < tvs.size(); ++i) { auto *ident = storage.Create("arg_" + std::to_string(i), true); auto sym = symbol_table.CreateSymbol("arg_" + std::to_string(i), true); ident->MapTo(sym); frame[sym] = tvs[i]; expressions.push_back(ident); } return expressions; } TypedValue EvaluateFunctionWithExprs(const std::string &function_name, const std::vector &expressions) { auto *op = storage.Create(function_name, expressions); return Eval(op); } template TypedValue EvaluateFunction(const std::string &function_name, std::tuple args) { std::vector tv_args; tv_args.reserve(args.size()); for (auto &arg : args) tv_args.emplace_back(std::move(arg)); return EvaluateFunctionWithExprs(function_name, ExpressionsFromTypedValues(tv_args)); } template TypedValue EvaluateFunction(const std::string &function_name, TArgs &&...args) { return EvaluateFunctionWithExprs(function_name, ExpressionsFromTypedValues(std::vector{TypedValue(args)...})); } }; template static TypedValue MakeTypedValueList(TArgs &&...args) { return TypedValue(std::vector{TypedValue(args)...}); } TEST_F(FunctionTest, EndNode) { ASSERT_THROW(EvaluateFunction("ENDNODE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("ENDNODE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue()); auto v2 = dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue()); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(*EvaluateFunction("ENDNODE", *e).ValueVertex().HasLabel(storage::View::NEW, dba.NameToLabel("label2"))); ASSERT_THROW(EvaluateFunction("ENDNODE", 2), QueryRuntimeException); } TEST_F(FunctionTest, Head) { ASSERT_THROW(EvaluateFunction("HEAD"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("HEAD", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(EvaluateFunction("HEAD", argument).ValueInt(), 3); argument.ValueList().clear(); ASSERT_TRUE(EvaluateFunction("HEAD", argument).IsNull()); ASSERT_THROW(EvaluateFunction("HEAD", 2), QueryRuntimeException); } TEST_F(FunctionTest, Properties) { ASSERT_THROW(EvaluateFunction("PROPERTIES"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("PROPERTIES", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("height"), storage::PropertyValue(5)).HasValue()); ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("age"), storage::PropertyValue(10)).HasValue()); auto v2 = dba.InsertVertex(); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(e->SetProperty(dba.NameToProperty("height"), storage::PropertyValue(3)).HasValue()); ASSERT_TRUE(e->SetProperty(dba.NameToProperty("age"), storage::PropertyValue(15)).HasValue()); dba.AdvanceCommand(); auto prop_values_to_int = [](TypedValue t) { std::unordered_map properties; for (auto property : t.ValueMap()) { properties[std::string(property.first)] = property.second.ValueInt(); } return properties; }; ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", v1)), UnorderedElementsAre(testing::Pair("height", 5), testing::Pair("age", 10))); ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", *e)), UnorderedElementsAre(testing::Pair("height", 3), testing::Pair("age", 15))); ASSERT_THROW(EvaluateFunction("PROPERTIES", 2), QueryRuntimeException); } TEST_F(FunctionTest, Last) { ASSERT_THROW(EvaluateFunction("LAST"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("LAST", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(EvaluateFunction("LAST", argument).ValueInt(), 5); argument.ValueList().clear(); ASSERT_TRUE(EvaluateFunction("LAST", argument).IsNull()); ASSERT_THROW(EvaluateFunction("LAST", 5), QueryRuntimeException); } TEST_F(FunctionTest, Size) { ASSERT_THROW(EvaluateFunction("SIZE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("SIZE", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(EvaluateFunction("SIZE", argument).ValueInt(), 3); ASSERT_EQ(EvaluateFunction("SIZE", "john").ValueInt(), 4); ASSERT_EQ(EvaluateFunction("SIZE", std::map{ {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}}) .ValueInt(), 3); ASSERT_THROW(EvaluateFunction("SIZE", 5), QueryRuntimeException); auto v0 = dba.InsertVertex(); query::Path path(v0); EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 0); auto v1 = dba.InsertVertex(); auto edge = dba.InsertEdge(&v0, &v1, dba.NameToEdgeType("type")); ASSERT_TRUE(edge.HasValue()); path.Expand(*edge); path.Expand(v1); EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 1); } TEST_F(FunctionTest, StartNode) { ASSERT_THROW(EvaluateFunction("STARTNODE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("STARTNODE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue()); auto v2 = dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue()); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(*EvaluateFunction("STARTNODE", *e).ValueVertex().HasLabel(storage::View::NEW, dba.NameToLabel("label1"))); ASSERT_THROW(EvaluateFunction("STARTNODE", 2), QueryRuntimeException); } TEST_F(FunctionTest, Degree) { ASSERT_THROW(EvaluateFunction("DEGREE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("DEGREE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); auto v3 = dba.InsertVertex(); auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); dba.AdvanceCommand(); ASSERT_EQ(EvaluateFunction("DEGREE", v1).ValueInt(), 1); ASSERT_EQ(EvaluateFunction("DEGREE", v2).ValueInt(), 2); ASSERT_EQ(EvaluateFunction("DEGREE", v3).ValueInt(), 1); ASSERT_THROW(EvaluateFunction("DEGREE", 2), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("DEGREE", *e12), QueryRuntimeException); } TEST_F(FunctionTest, InDegree) { ASSERT_THROW(EvaluateFunction("INDEGREE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("INDEGREE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); auto v3 = dba.InsertVertex(); auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); dba.AdvanceCommand(); ASSERT_EQ(EvaluateFunction("INDEGREE", v1).ValueInt(), 0); ASSERT_EQ(EvaluateFunction("INDEGREE", v2).ValueInt(), 2); ASSERT_EQ(EvaluateFunction("INDEGREE", v3).ValueInt(), 0); ASSERT_THROW(EvaluateFunction("INDEGREE", 2), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("INDEGREE", *e12), QueryRuntimeException); } TEST_F(FunctionTest, OutDegree) { ASSERT_THROW(EvaluateFunction("OUTDEGREE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("OUTDEGREE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); auto v3 = dba.InsertVertex(); auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); dba.AdvanceCommand(); ASSERT_EQ(EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1); ASSERT_EQ(EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0); ASSERT_EQ(EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1); ASSERT_THROW(EvaluateFunction("OUTDEGREE", 2), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("OUTDEGREE", *e12), QueryRuntimeException); } TEST_F(FunctionTest, ToBoolean) { ASSERT_THROW(EvaluateFunction("TOBOOLEAN"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 123).ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", -213).ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 0).ValueBool(), false); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", " trUE \n\t").ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", "\n\tFalsE").ValueBool(), false); ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", "\n\tFALSEA ").IsNull()); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", true).ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", false).ValueBool(), false); } TEST_F(FunctionTest, ToFloat) { ASSERT_THROW(EvaluateFunction("TOFLOAT"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("TOFLOAT", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("TOFLOAT", " -3.5 \n\t").ValueDouble(), -3.5); ASSERT_EQ(EvaluateFunction("TOFLOAT", "\n\t0.5e-1").ValueDouble(), 0.05); ASSERT_TRUE(EvaluateFunction("TOFLOAT", "\n\t3.4e-3X ").IsNull()); ASSERT_EQ(EvaluateFunction("TOFLOAT", -3.5).ValueDouble(), -3.5); ASSERT_EQ(EvaluateFunction("TOFLOAT", -3).ValueDouble(), -3.0); ASSERT_THROW(EvaluateFunction("TOFLOAT", true), QueryRuntimeException); } TEST_F(FunctionTest, ToInteger) { ASSERT_THROW(EvaluateFunction("TOINTEGER"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("TOINTEGER", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("TOINTEGER", false).ValueInt(), 0); ASSERT_EQ(EvaluateFunction("TOINTEGER", true).ValueInt(), 1); ASSERT_EQ(EvaluateFunction("TOINTEGER", "\n\t3").ValueInt(), 3); ASSERT_EQ(EvaluateFunction("TOINTEGER", " -3.5 \n\t").ValueInt(), -3); ASSERT_TRUE(EvaluateFunction("TOINTEGER", "\n\t3X ").IsNull()); ASSERT_EQ(EvaluateFunction("TOINTEGER", -3.5).ValueInt(), -3); ASSERT_EQ(EvaluateFunction("TOINTEGER", 3.5).ValueInt(), 3); } TEST_F(FunctionTest, Type) { ASSERT_THROW(EvaluateFunction("TYPE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("TYPE", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue()); auto v2 = dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue()); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_EQ(EvaluateFunction("TYPE", *e).ValueString(), "type1"); ASSERT_THROW(EvaluateFunction("TYPE", 2), QueryRuntimeException); } TEST_F(FunctionTest, ValueType) { ASSERT_THROW(EvaluateFunction("VALUETYPE"), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), QueryRuntimeException); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL"); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), "BOOLEAN"); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), "INTEGER"); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), "FLOAT"); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), "STRING"); ASSERT_EQ( EvaluateFunction("VALUETYPE", TypedValue(std::vector{TypedValue(1), TypedValue(2)})).ValueString(), "LIST"); ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(std::map{{"test", TypedValue(1)}})) .ValueString(), "MAP"); auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); ASSERT_EQ(EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE"); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_EQ(EvaluateFunction("VALUETYPE", *e).ValueString(), "RELATIONSHIP"); Path p(v1, *e, v2); ASSERT_EQ(EvaluateFunction("VALUETYPE", p).ValueString(), "PATH"); } TEST_F(FunctionTest, Labels) { ASSERT_THROW(EvaluateFunction("LABELS"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("LABELS", TypedValue()).IsNull()); auto v = dba.InsertVertex(); ASSERT_TRUE(v.AddLabel(dba.NameToLabel("label1")).HasValue()); ASSERT_TRUE(v.AddLabel(dba.NameToLabel("label2")).HasValue()); dba.AdvanceCommand(); std::vector labels; auto _labels = EvaluateFunction("LABELS", v).ValueList(); labels.reserve(_labels.size()); for (auto label : _labels) { labels.emplace_back(label.ValueString()); } ASSERT_THAT(labels, UnorderedElementsAre("label1", "label2")); ASSERT_THROW(EvaluateFunction("LABELS", 2), QueryRuntimeException); } TEST_F(FunctionTest, NodesRelationships) { EXPECT_THROW(EvaluateFunction("NODES"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RELATIONSHIPS"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("NODES", TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull()); { auto v1 = dba.InsertVertex(); auto v2 = dba.InsertVertex(); auto v3 = dba.InsertVertex(); auto e1 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")); ASSERT_TRUE(e1.HasValue()); auto e2 = dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("Type")); ASSERT_TRUE(e2.HasValue()); query::Path path{v1, *e1, v2, *e2, v3}; dba.AdvanceCommand(); auto _nodes = EvaluateFunction("NODES", path).ValueList(); std::vector nodes; for (const auto &node : _nodes) { nodes.push_back(node.ValueVertex()); } EXPECT_THAT(nodes, ElementsAre(v1, v2, v3)); auto _edges = EvaluateFunction("RELATIONSHIPS", path).ValueList(); std::vector edges; for (const auto &edge : _edges) { edges.push_back(edge.ValueEdge()); } EXPECT_THAT(edges, ElementsAre(*e1, *e2)); } EXPECT_THROW(EvaluateFunction("NODES", 2), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", 2), QueryRuntimeException); } TEST_F(FunctionTest, Range) { EXPECT_THROW(EvaluateFunction("RANGE"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull()); EXPECT_THROW(EvaluateFunction("RANGE", 1, TypedValue(), 1.3), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RANGE", 1, 2, 0), QueryRuntimeException); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 1, 3)), ElementsAre(1, 2, 3)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -1, 5, 2)), ElementsAre(-1, 1, 3, 5)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 10, 3)), ElementsAre(2, 5, 8)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, 2)), ElementsAre(2)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 3, 0, 5)), ElementsAre()); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 5, 1, -2)), ElementsAre(5, 3, 1)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 6, 1, -2)), ElementsAre(6, 4, 2)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, -3)), ElementsAre(2)); EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -2, 4, -1)), ElementsAre()); } TEST_F(FunctionTest, Keys) { ASSERT_THROW(EvaluateFunction("KEYS"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("KEYS", TypedValue()).IsNull()); auto v1 = dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("height"), storage::PropertyValue(5)).HasValue()); ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("age"), storage::PropertyValue(10)).HasValue()); auto v2 = dba.InsertVertex(); auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(e->SetProperty(dba.NameToProperty("width"), storage::PropertyValue(3)).HasValue()); ASSERT_TRUE(e->SetProperty(dba.NameToProperty("age"), storage::PropertyValue(15)).HasValue()); dba.AdvanceCommand(); auto prop_keys_to_string = [](TypedValue t) { std::vector keys; for (auto property : t.ValueList()) { keys.emplace_back(property.ValueString()); } return keys; }; ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", v1)), UnorderedElementsAre("height", "age")); ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", *e)), UnorderedElementsAre("width", "age")); ASSERT_THROW(EvaluateFunction("KEYS", 2), QueryRuntimeException); } TEST_F(FunctionTest, Tail) { ASSERT_THROW(EvaluateFunction("TAIL"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("TAIL", TypedValue()).IsNull()); auto argument = MakeTypedValueList(); ASSERT_EQ(EvaluateFunction("TAIL", argument).ValueList().size(), 0U); argument = MakeTypedValueList(3, 4, true, "john"); auto list = EvaluateFunction("TAIL", argument).ValueList(); ASSERT_EQ(list.size(), 3U); ASSERT_EQ(list[0].ValueInt(), 4); ASSERT_EQ(list[1].ValueBool(), true); ASSERT_EQ(list[2].ValueString(), "john"); ASSERT_THROW(EvaluateFunction("TAIL", 2), QueryRuntimeException); } TEST_F(FunctionTest, UniformSample) { ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull()); ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull()); ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()).IsNull()); ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull()); ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), -1), QueryRuntimeException); ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 0).ValueList().size(), 0); ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 2).ValueList().size(), 2); ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 3).ValueList().size(), 3); ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 5).ValueList().size(), 5); } TEST_F(FunctionTest, Abs) { ASSERT_THROW(EvaluateFunction("ABS"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("ABS", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("ABS", -2).ValueInt(), 2); ASSERT_EQ(EvaluateFunction("ABS", -2.5).ValueDouble(), 2.5); ASSERT_THROW(EvaluateFunction("ABS", true), QueryRuntimeException); } // Test if log works. If it does then all functions wrapped with // WRAP_CMATH_FLOAT_FUNCTION macro should work and are not gonna be tested for // correctnes.. TEST_F(FunctionTest, Log) { ASSERT_THROW(EvaluateFunction("LOG"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("LOG", TypedValue()).IsNull()); ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 2).ValueDouble(), log(2)); ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 1.5).ValueDouble(), log(1.5)); // Not portable, but should work on most platforms. ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", -1.5).ValueDouble())); ASSERT_THROW(EvaluateFunction("LOG", true), QueryRuntimeException); } // Function Round wraps round from cmath and will work if FunctionTest.Log test // passes. This test is used to show behavior of round since it differs from // neo4j's round. TEST_F(FunctionTest, Round) { ASSERT_THROW(EvaluateFunction("ROUND"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("ROUND", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("ROUND", -2).ValueDouble(), -2); ASSERT_EQ(EvaluateFunction("ROUND", -2.4).ValueDouble(), -2); ASSERT_EQ(EvaluateFunction("ROUND", -2.5).ValueDouble(), -3); ASSERT_EQ(EvaluateFunction("ROUND", -2.6).ValueDouble(), -3); ASSERT_EQ(EvaluateFunction("ROUND", 2.4).ValueDouble(), 2); ASSERT_EQ(EvaluateFunction("ROUND", 2.5).ValueDouble(), 3); ASSERT_EQ(EvaluateFunction("ROUND", 2.6).ValueDouble(), 3); ASSERT_THROW(EvaluateFunction("ROUND", true), QueryRuntimeException); } // Check if wrapped functions are callable (check if everything was spelled // correctly...). Wrapper correctnes is checked in FunctionTest.Log function // test. TEST_F(FunctionTest, WrappedMathFunctions) { for (auto function_name : {"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN", "ATAN", "COS", "SIN", "TAN"}) { EvaluateFunction(function_name, 0.5); } } TEST_F(FunctionTest, Atan2) { ASSERT_THROW(EvaluateFunction("ATAN2"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("ATAN2", TypedValue(), 1).IsNull()); ASSERT_TRUE(EvaluateFunction("ATAN2", 1, TypedValue()).IsNull()); ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", 2, -1.0).ValueDouble(), atan2(2, -1)); ASSERT_THROW(EvaluateFunction("ATAN2", 3.0, true), QueryRuntimeException); } TEST_F(FunctionTest, Sign) { ASSERT_THROW(EvaluateFunction("SIGN"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("SIGN", TypedValue()).IsNull()); ASSERT_EQ(EvaluateFunction("SIGN", -2).ValueInt(), -1); ASSERT_EQ(EvaluateFunction("SIGN", -0.2).ValueInt(), -1); ASSERT_EQ(EvaluateFunction("SIGN", 0.0).ValueInt(), 0); ASSERT_EQ(EvaluateFunction("SIGN", 2.5).ValueInt(), 1); ASSERT_THROW(EvaluateFunction("SIGN", true), QueryRuntimeException); } TEST_F(FunctionTest, E) { ASSERT_THROW(EvaluateFunction("E", 1), QueryRuntimeException); ASSERT_DOUBLE_EQ(EvaluateFunction("E").ValueDouble(), M_E); } TEST_F(FunctionTest, Pi) { ASSERT_THROW(EvaluateFunction("PI", 1), QueryRuntimeException); ASSERT_DOUBLE_EQ(EvaluateFunction("PI").ValueDouble(), M_PI); } TEST_F(FunctionTest, Rand) { ASSERT_THROW(EvaluateFunction("RAND", 1), QueryRuntimeException); ASSERT_GE(EvaluateFunction("RAND").ValueDouble(), 0.0); ASSERT_LT(EvaluateFunction("RAND").ValueDouble(), 1.0); } TEST_F(FunctionTest, StartsWith) { EXPECT_THROW(EvaluateFunction(kStartsWith), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kStartsWith, "a", TypedValue()).IsNull()); EXPECT_THROW(EvaluateFunction(kStartsWith, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kStartsWith, "abc", "abc").ValueBool()); EXPECT_TRUE(EvaluateFunction(kStartsWith, "abcdef", "abc").ValueBool()); EXPECT_FALSE(EvaluateFunction(kStartsWith, "abcdef", "aBc").ValueBool()); EXPECT_FALSE(EvaluateFunction(kStartsWith, "abc", "abcd").ValueBool()); } TEST_F(FunctionTest, EndsWith) { EXPECT_THROW(EvaluateFunction(kEndsWith), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kEndsWith, "a", TypedValue()).IsNull()); EXPECT_THROW(EvaluateFunction(kEndsWith, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kEndsWith, "abc", "abc").ValueBool()); EXPECT_TRUE(EvaluateFunction(kEndsWith, "abcdef", "def").ValueBool()); EXPECT_FALSE(EvaluateFunction(kEndsWith, "abcdef", "dEf").ValueBool()); EXPECT_FALSE(EvaluateFunction(kEndsWith, "bcd", "abcd").ValueBool()); } TEST_F(FunctionTest, Contains) { EXPECT_THROW(EvaluateFunction(kContains), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kContains, "a", TypedValue()).IsNull()); EXPECT_THROW(EvaluateFunction(kContains, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kContains, "abc", "abc").ValueBool()); EXPECT_TRUE(EvaluateFunction(kContains, "abcde", "bcd").ValueBool()); EXPECT_FALSE(EvaluateFunction(kContains, "cde", "abcdef").ValueBool()); EXPECT_FALSE(EvaluateFunction(kContains, "abcdef", "dEf").ValueBool()); } TEST_F(FunctionTest, Assert) { // Invalid calls. ASSERT_THROW(EvaluateFunction("ASSERT"), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("ASSERT", false, false), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("ASSERT", "string", false), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("ASSERT", false, "reason", true), QueryRuntimeException); // Valid calls, assertion fails. ASSERT_THROW(EvaluateFunction("ASSERT", false), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("ASSERT", false, "message"), QueryRuntimeException); try { EvaluateFunction("ASSERT", false, "bbgba"); } catch (QueryRuntimeException &e) { ASSERT_TRUE(std::string(e.what()).find("bbgba") != std::string::npos); } // Valid calls, assertion passes. ASSERT_TRUE(EvaluateFunction("ASSERT", true).ValueBool()); ASSERT_TRUE(EvaluateFunction("ASSERT", true, "message").ValueBool()); } TEST_F(FunctionTest, Counter) { EXPECT_THROW(EvaluateFunction("COUNTER"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("COUNTER", "a"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b", "c"), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 1); EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 2); EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 1); EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), -1); EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 1); EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 5); EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 10); EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -5); EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -10); EXPECT_THROW(EvaluateFunction("COUNTER", "c6", 0, 0), QueryRuntimeException); } TEST_F(FunctionTest, Id) { auto va = dba.InsertVertex(); auto ea = dba.InsertEdge(&va, &va, dba.NameToEdgeType("edge")); ASSERT_TRUE(ea.HasValue()); auto vb = dba.InsertVertex(); dba.AdvanceCommand(); EXPECT_TRUE(EvaluateFunction("ID", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("ID", va).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("ID", *ea).ValueInt(), 0); EXPECT_EQ(EvaluateFunction("ID", vb).ValueInt(), 1); EXPECT_THROW(EvaluateFunction("ID"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("ID", 0), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("ID", va, *ea), QueryRuntimeException); } TEST_F(FunctionTest, ToStringNull) { EXPECT_TRUE(EvaluateFunction("TOSTRING", TypedValue()).IsNull()); } TEST_F(FunctionTest, ToStringString) { EXPECT_EQ(EvaluateFunction("TOSTRING", "").ValueString(), ""); EXPECT_EQ(EvaluateFunction("TOSTRING", "this is a string").ValueString(), "this is a string"); } TEST_F(FunctionTest, ToStringInteger) { EXPECT_EQ(EvaluateFunction("TOSTRING", -23321312).ValueString(), "-23321312"); EXPECT_EQ(EvaluateFunction("TOSTRING", 0).ValueString(), "0"); EXPECT_EQ(EvaluateFunction("TOSTRING", 42).ValueString(), "42"); } TEST_F(FunctionTest, ToStringDouble) { EXPECT_EQ(EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000"); EXPECT_EQ(EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000"); EXPECT_EQ(EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322"); } TEST_F(FunctionTest, ToStringBool) { EXPECT_EQ(EvaluateFunction("TOSTRING", true).ValueString(), "true"); EXPECT_EQ(EvaluateFunction("TOSTRING", false).ValueString(), "false"); } TEST_F(FunctionTest, ToStringExceptions) { EXPECT_THROW(EvaluateFunction("TOSTRING", 1, 2, 3), QueryRuntimeException); } TEST_F(FunctionTest, Timestamp) { ctx.timestamp = 42; EXPECT_EQ(EvaluateFunction("TIMESTAMP").ValueInt(), 42); } TEST_F(FunctionTest, TimestampExceptions) { ctx.timestamp = 42; EXPECT_THROW(EvaluateFunction("TIMESTAMP", 1).ValueInt(), QueryRuntimeException); } TEST_F(FunctionTest, Left) { EXPECT_THROW(EvaluateFunction("LEFT"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), 10).IsNull()); EXPECT_THROW(EvaluateFunction("LEFT", TypedValue(), -10), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 0).ValueString(), ""); EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 3).ValueString(), "mem"); EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 1000).ValueString(), "memgraph"); EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", -10), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", "graph"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("LEFT", 132, 10), QueryRuntimeException); } TEST_F(FunctionTest, Right) { EXPECT_THROW(EvaluateFunction("RIGHT"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), 10).IsNull()); EXPECT_THROW(EvaluateFunction("RIGHT", TypedValue(), -10), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 0).ValueString(), ""); EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 3).ValueString(), "aph"); EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 1000).ValueString(), "memgraph"); EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", -10), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", "graph"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RIGHT", 132, 10), QueryRuntimeException); } TEST_F(FunctionTest, Trimming) { EXPECT_TRUE(EvaluateFunction("LTRIM", TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("RTRIM", TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("TRIM", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("LTRIM", " abc ").ValueString(), "abc "); EXPECT_EQ(EvaluateFunction("RTRIM", " abc ").ValueString(), " abc"); EXPECT_EQ(EvaluateFunction("TRIM", "abc").ValueString(), "abc"); EXPECT_THROW(EvaluateFunction("LTRIM", "x", "y"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RTRIM", "x", "y"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TRIM", "x", "y"), QueryRuntimeException); } TEST_F(FunctionTest, Reverse) { EXPECT_TRUE(EvaluateFunction("REVERSE", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("REVERSE", "abc").ValueString(), "cba"); EXPECT_THROW(EvaluateFunction("REVERSE", "x", "y"), QueryRuntimeException); } TEST_F(FunctionTest, Replace) { EXPECT_THROW(EvaluateFunction("REPLACE"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("REPLACE", TypedValue(), "l", "w").IsNull()); EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", TypedValue(), "w").IsNull()); EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", "l", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("REPLACE", "hello", "l", "w").ValueString(), "hewwo"); EXPECT_THROW(EvaluateFunction("REPLACE", 1, "l", "w"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("REPLACE", "hello", 1, "w"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("REPLACE", "hello", "l", 1), QueryRuntimeException); } TEST_F(FunctionTest, Split) { EXPECT_THROW(EvaluateFunction("SPLIT"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SPLIT", "one,two", 1), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SPLIT", 1, "one,two"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("SPLIT", "one,two", TypedValue()).IsNull()); EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), ",").IsNull()); auto result = EvaluateFunction("SPLIT", "one,two", ","); EXPECT_TRUE(result.IsList()); EXPECT_EQ(result.ValueList()[0].ValueString(), "one"); EXPECT_EQ(result.ValueList()[1].ValueString(), "two"); } TEST_F(FunctionTest, Substring) { EXPECT_THROW(EvaluateFunction("SUBSTRING"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("SUBSTRING", TypedValue(), 0, 10).IsNull()); EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), TypedValue()), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), -10), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, TypedValue()), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, -10), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2).ValueString(), "llo"); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 10).ValueString(), ""); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2, 0).ValueString(), ""); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 3).ValueString(), "ell"); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 4).ValueString(), "ello"); EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 10).ValueString(), "ello"); } TEST_F(FunctionTest, ToLower) { EXPECT_THROW(EvaluateFunction("TOLOWER"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("TOLOWER", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("TOLOWER", "Ab__C").ValueString(), "ab__c"); } TEST_F(FunctionTest, ToUpper) { EXPECT_THROW(EvaluateFunction("TOUPPER"), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("TOUPPER", TypedValue()).IsNull()); EXPECT_EQ(EvaluateFunction("TOUPPER", "Ab__C").ValueString(), "AB__C"); } TEST_F(FunctionTest, ToByteString) { EXPECT_THROW(EvaluateFunction("TOBYTESTRING"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", 42), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", TypedValue()), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "", 42), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "ff"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "00"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "0xG"), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "").ValueString(), ""); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x").ValueString(), ""); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0X").ValueString(), ""); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x0123456789aAbBcCdDeEfF").ValueString(), "\x01\x23\x45\x67\x89\xAA\xBB\xCC\xDD\xEE\xFF"); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString().size(), 2); EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString(), utils::pmr::string("\x00\x42", 2, utils::NewDeleteResource())); } TEST_F(FunctionTest, FromByteString) { EXPECT_THROW(EvaluateFunction("FROMBYTESTRING"), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", 42), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", TypedValue()), QueryRuntimeException); EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", "").ValueString(), ""); auto bytestring = EvaluateFunction("TOBYTESTRING", "0x123456789aAbBcCdDeEfF"); EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", bytestring).ValueString(), "0x0123456789aabbccddeeff"); EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042"); } } // namespace