// Copyright 2023 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #include #include #include #include #include #include #include #include #include "disk_test_utils.hpp" #include "query/context.hpp" #include "query/db_accessor.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 "query/typed_value.hpp" #include "storage/v2/disk/storage.hpp" #include "storage/v2/inmemory/storage.hpp" #include "storage/v2/storage.hpp" #include "utils/exceptions.hpp" #include "utils/string.hpp" #include "query_common.hpp" #include "utils/temporal.hpp" using namespace memgraph::query; using memgraph::query::test_common::ToIntList; using testing::ElementsAre; using testing::UnorderedElementsAre; namespace { template class ExpressionEvaluatorTest : public ::testing::Test { protected: const std::string testSuite = "expression_evaluator"; memgraph::storage::Config config; std::unique_ptr db; std::unique_ptr storage_dba; memgraph::query::DbAccessor dba; AstStorage storage; memgraph::utils::MonotonicBufferResource mem{1024}; EvaluationContext ctx{.memory = &mem, .timestamp = memgraph::query::QueryTimestamp()}; SymbolTable symbol_table; Frame frame{128}; ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, memgraph::storage::View::OLD}; ExpressionEvaluatorTest() : config(disk_test_utils::GenerateOnDiskConfig(testSuite)), db(new StorageType(config)), storage_dba(db->Access()), dba(storage_dba.get()) {} ~ExpressionEvaluatorTest() { if (std::is_same::value) { disk_test_utils::RemoveRocksDbDirs(testSuite); } } Identifier *CreateIdentifierWithValue(std::string name, const TypedValue &value) { auto id = storage.template 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; } }; // using StorageTypes = ::testing::Types; using StorageTypes = ::testing::Types; TYPED_TEST_CASE(ExpressionEvaluatorTest, StorageTypes); TYPED_TEST(ExpressionEvaluatorTest, OrOperator) { auto *op = this->storage.template Create(this->storage.template Create(true), this->storage.template Create(false)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(true), this->storage.template Create(true)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, XorOperator) { auto *op = this->storage.template Create(this->storage.template Create(true), this->storage.template Create(false)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(true), this->storage.template Create(true)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), false); } TYPED_TEST(ExpressionEvaluatorTest, AndOperator) { auto *op = this->storage.template Create(this->storage.template Create(true), this->storage.template Create(true)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(false), this->storage.template Create(true)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), false); } TYPED_TEST(ExpressionEvaluatorTest, AndOperatorShortCircuit) { { auto *op = this->storage.template Create(this->storage.template Create(false), this->storage.template Create(5)); auto value = this->Eval(op); EXPECT_EQ(value.ValueBool(), false); } { auto *op = this->storage.template Create(this->storage.template Create(5), this->storage.template 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(this->Eval(op), QueryRuntimeException); } } TYPED_TEST(ExpressionEvaluatorTest, AndOperatorNull) { { // Null doesn't short circuit auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(5)); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } { auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(true)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(false)); auto value = this->Eval(op); ASSERT_TRUE(value.IsBool()); EXPECT_EQ(value.ValueBool(), false); } } TYPED_TEST(ExpressionEvaluatorTest, AdditionOperator) { auto *op = this->storage.template Create(this->storage.template Create(2), this->storage.template Create(3)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TYPED_TEST(ExpressionEvaluatorTest, SubtractionOperator) { auto *op = this->storage.template Create(this->storage.template Create(2), this->storage.template Create(3)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), -1); } TYPED_TEST(ExpressionEvaluatorTest, MultiplicationOperator) { auto *op = this->storage.template Create(this->storage.template Create(2), this->storage.template Create(3)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 6); } TYPED_TEST(ExpressionEvaluatorTest, DivisionOperator) { auto *op = this->storage.template Create(this->storage.template Create(50), this->storage.template Create(10)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TYPED_TEST(ExpressionEvaluatorTest, ModOperator) { auto *op = this->storage.template Create(this->storage.template Create(65), this->storage.template Create(10)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TYPED_TEST(ExpressionEvaluatorTest, EqualOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TYPED_TEST(ExpressionEvaluatorTest, NotEqualOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, LessOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TYPED_TEST(ExpressionEvaluatorTest, GreaterOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, LessEqualOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), false); } TYPED_TEST(ExpressionEvaluatorTest, GreaterEqualOperator) { auto *op = this->storage.template Create(this->storage.template Create(10), this->storage.template Create(15)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = this->storage.template Create(this->storage.template Create(15), this->storage.template Create(15)); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), true); op = this->storage.template Create(this->storage.template Create(20), this->storage.template Create(15)); auto val3 = this->Eval(op); ASSERT_EQ(val3.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, InListOperator) { auto *list_literal = this->storage.template Create(std::vector{ this->storage.template Create(1), this->storage.template Create(2), this->storage.template Create("a")}); { // Element exists in list. auto *op = this->storage.template Create(this->storage.template Create(2), list_literal); auto value = this->Eval(op); EXPECT_EQ(value.ValueBool(), true); } { // Element doesn't exist in list. auto *op = this->storage.template Create(this->storage.template Create("x"), list_literal); auto value = this->Eval(op); EXPECT_EQ(value.ValueBool(), false); } { auto *list_literal = this->storage.template Create(std::vector{ this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(2), this->storage.template Create("a")}); // Element doesn't exist in list with null element. auto *op = this->storage.template Create(this->storage.template Create("x"), list_literal); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null list. auto *op = this->storage.template Create( this->storage.template Create("x"), this->storage.template Create(memgraph::storage::PropertyValue())); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null literal. auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), list_literal); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Null literal, empty list. auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(std::vector())); auto value = this->Eval(op); EXPECT_FALSE(value.ValueBool()); } } TYPED_TEST(ExpressionEvaluatorTest, ListIndexing) { auto *list_literal = this->storage.template Create(std::vector{ this->storage.template Create(1), this->storage.template Create(2), this->storage.template Create(3), this->storage.template Create(4)}); { // Legal indexing. auto *op = this->storage.template Create(list_literal, this->storage.template Create(2)); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 3); } { // Out of bounds indexing. auto *op = this->storage.template Create(list_literal, this->storage.template Create(4)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Out of bounds indexing with negative bound. auto *op = this->storage.template Create(list_literal, this->storage.template Create(-100)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Legal indexing with negative index. auto *op = this->storage.template Create(list_literal, this->storage.template Create(-2)); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 3); } { // Indexing with one operator being null. auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(-2)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Indexing with incompatible type. auto *op = this->storage.template Create(list_literal, this->storage.template Create("bla")); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } } TYPED_TEST(ExpressionEvaluatorTest, MapIndexing) { auto *map_literal = this->storage.template Create(std::unordered_map{ {this->storage.GetPropertyIx("a"), this->storage.template Create(1)}, {this->storage.GetPropertyIx("b"), this->storage.template Create(2)}, {this->storage.GetPropertyIx("c"), this->storage.template Create(3)}}); { // Legal indexing. auto *op = this->storage.template Create(map_literal, this->storage.template Create("b")); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 2); } { // Legal indexing, non-existing key. auto *op = this->storage.template Create(map_literal, this->storage.template Create("z")); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Wrong key type. auto *op = this->storage.template Create(map_literal, this->storage.template Create(42)); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } { // Indexing with Null. auto *op = this->storage.template Create( map_literal, this->storage.template Create(memgraph::storage::PropertyValue())); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } } TYPED_TEST(ExpressionEvaluatorTest, MapProjectionIndexing) { auto *map_variable = this->storage.template Create(std::unordered_map{ {this->storage.GetPropertyIx("x"), this->storage.template Create(0)}}); auto *map_projection_literal = this->storage.template Create( map_variable, std::unordered_map{ {this->storage.GetPropertyIx("a"), this->storage.template Create(1)}, {this->storage.GetPropertyIx("y"), this->storage.template Create( map_variable, this->storage.GetPropertyIx("y"))}}); { // Legal indexing. auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create("a")); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 1); } { // Legal indexing; property created by PropertyLookup of a non-existent map variable key auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create("y")); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Legal indexing, non-existing property. auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create("z")); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Wrong key type. auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create(42)); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } { // Indexing with Null. auto *op = this->storage.template Create( map_projection_literal, this->storage.template Create(memgraph::storage::PropertyValue())); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } } TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupBefore) { // AllPropertiesLookup (.*) may contain properties whose names also occur in MapProjectionLiteral // The ones in MapProjectionLiteral are explicitly given and thus take precedence over those in AllPropertiesLookup // Test case: AllPropertiesLookup comes before the identically-named properties auto *map_variable = this->storage.template Create(std::unordered_map{ {this->storage.GetPropertyIx("x"), this->storage.template Create(0)}}); auto *map_projection_literal = this->storage.template Create( map_variable, std::unordered_map{ {this->storage.GetPropertyIx("*"), this->storage.template Create(map_variable)}, {this->storage.GetPropertyIx("x"), this->storage.template Create(1)}}); auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create("x")); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 1); } TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupAfter) { // AllPropertiesLookup (.*) may contain properties whose names also occur in MapProjectionLiteral // The ones in MapProjectionLiteral are explicitly given and thus take precedence over those in AllPropertiesLookup // Test case: AllPropertiesLookup comes after the identically-named properties auto *map_variable = this->storage.template Create(std::unordered_map{ {this->storage.GetPropertyIx("x"), this->storage.template Create(0)}}); auto *map_projection_literal = this->storage.template Create( map_variable, std::unordered_map{ {this->storage.GetPropertyIx("x"), this->storage.template Create(1)}, {this->storage.GetPropertyIx("*"), this->storage.template Create(map_variable)}}); auto *op = this->storage.template Create(map_projection_literal, this->storage.template Create("x")); auto value = this->Eval(op); EXPECT_EQ(value.ValueInt(), 1); } TYPED_TEST(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { auto edge_type = this->dba.NameToEdgeType("edge_type"); auto prop = this->dba.NameToProperty("prop"); auto v1 = this->dba.InsertVertex(); auto e11 = this->dba.InsertEdge(&v1, &v1, edge_type); ASSERT_TRUE(e11.HasValue()); ASSERT_TRUE(v1.SetProperty(prop, memgraph::storage::PropertyValue(42)).HasValue()); ASSERT_TRUE(e11->SetProperty(prop, memgraph::storage::PropertyValue(43)).HasValue()); this->dba.AdvanceCommand(); auto *vertex_id = this->CreateIdentifierWithValue("v1", TypedValue(v1)); auto *edge_id = this->CreateIdentifierWithValue("e11", TypedValue(*e11)); { // Legal indexing. auto *op1 = this->storage.template Create( vertex_id, this->storage.template Create("prop")); auto value1 = this->Eval(op1); EXPECT_EQ(value1.ValueInt(), 42); auto *op2 = this->storage.template Create( edge_id, this->storage.template Create("prop")); auto value2 = this->Eval(op2); EXPECT_EQ(value2.ValueInt(), 43); } { // Legal indexing, non-existing key. auto *op1 = this->storage.template Create( vertex_id, this->storage.template Create("blah")); auto value1 = this->Eval(op1); EXPECT_TRUE(value1.IsNull()); auto *op2 = this->storage.template Create( edge_id, this->storage.template Create("blah")); auto value2 = this->Eval(op2); EXPECT_TRUE(value2.IsNull()); } { // Wrong key type. auto *op1 = this->storage.template Create(vertex_id, this->storage.template Create(1)); EXPECT_THROW(this->Eval(op1), QueryRuntimeException); auto *op2 = this->storage.template Create(edge_id, this->storage.template Create(1)); EXPECT_THROW(this->Eval(op2), QueryRuntimeException); } { // Indexing with Null. auto *op1 = this->storage.template Create( vertex_id, this->storage.template Create(memgraph::storage::PropertyValue())); auto value1 = this->Eval(op1); EXPECT_TRUE(value1.IsNull()); auto *op2 = this->storage.template Create( edge_id, this->storage.template Create(memgraph::storage::PropertyValue())); auto value2 = this->Eval(op2); EXPECT_TRUE(value2.IsNull()); } } TYPED_TEST(ExpressionEvaluatorTest, TypedValueListIndexing) { auto list_vector = memgraph::utils::pmr::vector(this->ctx.memory); list_vector.emplace_back("string1"); list_vector.emplace_back(TypedValue("string2")); auto *identifier = this->storage.template Create("n"); auto node_symbol = this->symbol_table.CreateSymbol("n", true); identifier->MapTo(node_symbol); this->frame[node_symbol] = TypedValue(list_vector, this->ctx.memory); { // Legal indexing. auto *op = this->storage.template Create(identifier, this->storage.template Create(0)); auto value = this->Eval(op); EXPECT_EQ(value.ValueString(), "string1"); } { // Out of bounds indexing auto *op = this->storage.template Create(identifier, this->storage.template Create(3)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Out of bounds indexing with negative bound. auto *op = this->storage.template Create(identifier, this->storage.template Create(-100)); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } { // Legal indexing with negative index. auto *op = this->storage.template Create(identifier, this->storage.template Create(-2)); auto value = this->Eval(op); EXPECT_EQ(value.ValueString(), "string1"); } { // Indexing with incompatible type. auto *op = this->storage.template Create(identifier, this->storage.template Create("bla")); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } } TYPED_TEST(ExpressionEvaluatorTest, ListSlicingOperator) { auto *list_literal = this->storage.template Create(std::vector{ this->storage.template Create(1), this->storage.template Create(2), this->storage.template Create(3), this->storage.template 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 = this->storage.template Create(list_literal, this->storage.template Create(2), this->storage.template Create(4)); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { // Legal slicing with negative bound. auto *op = this->storage.template Create(list_literal, this->storage.template Create(2), this->storage.template Create(-1)); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3)); } { // Lower bound larger than upper bound. auto *op = this->storage.template Create(list_literal, this->storage.template Create(2), this->storage.template Create(-4)); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre()); } { // Bounds ouf or range. auto *op = this->storage.template Create(list_literal, this->storage.template Create(-100), this->storage.template Create(10)); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4)); } { // Lower bound undefined. auto *op = this->storage.template Create(list_literal, nullptr, this->storage.template Create(3)); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3)); } { // Upper bound undefined. auto *op = this->storage.template Create( list_literal, this->storage.template Create(-2), nullptr); auto value = this->Eval(op); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { // Bound of illegal type and null value bound. auto *op = this->storage.template Create( list_literal, this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create("mirko")); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } { // List of illegal type. auto *op = this->storage.template Create(this->storage.template Create("a"), this->storage.template Create(-2), nullptr); EXPECT_THROW(this->Eval(op), QueryRuntimeException); } { // Null value list with undefined upper bound. auto *op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue()), this->storage.template Create(-2), nullptr); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); ; } { // Null value index. auto *op = this->storage.template Create( list_literal, this->storage.template Create(-2), this->storage.template Create(memgraph::storage::PropertyValue())); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); ; } } TYPED_TEST(ExpressionEvaluatorTest, IfOperator) { auto *then_expression = this->storage.template Create(10); auto *else_expression = this->storage.template Create(20); { auto *condition_true = this->storage.template Create( this->storage.template Create(2), this->storage.template Create(2)); auto *op = this->storage.template Create(condition_true, then_expression, else_expression); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 10); } { auto *condition_false = this->storage.template Create( this->storage.template Create(2), this->storage.template Create(3)); auto *op = this->storage.template Create(condition_false, then_expression, else_expression); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 20); } { auto *condition_exception = this->storage.template Create( this->storage.template Create(2), this->storage.template Create(3)); auto *op = this->storage.template Create(condition_exception, then_expression, else_expression); ASSERT_THROW(this->Eval(op), QueryRuntimeException); } } TYPED_TEST(ExpressionEvaluatorTest, NotOperator) { auto *op = this->storage.template Create(this->storage.template Create(false)); auto value = this->Eval(op); ASSERT_EQ(value.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, UnaryPlusOperator) { auto *op = this->storage.template Create(this->storage.template Create(5)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), 5); } TYPED_TEST(ExpressionEvaluatorTest, UnaryMinusOperator) { auto *op = this->storage.template Create(this->storage.template Create(5)); auto value = this->Eval(op); ASSERT_EQ(value.ValueInt(), -5); } TYPED_TEST(ExpressionEvaluatorTest, IsNullOperator) { auto *op = this->storage.template Create(this->storage.template Create(1)); auto val1 = this->Eval(op); ASSERT_EQ(val1.ValueBool(), false); op = this->storage.template Create( this->storage.template Create(memgraph::storage::PropertyValue())); auto val2 = this->Eval(op); ASSERT_EQ(val2.ValueBool(), true); } TYPED_TEST(ExpressionEvaluatorTest, LabelsTest) { auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("ANIMAL")).HasValue()); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("DOG")).HasValue()); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("NICE_DOG")).HasValue()); this->dba.AdvanceCommand(); auto *identifier = this->storage.template Create("n"); auto node_symbol = this->symbol_table.CreateSymbol("n", true); identifier->MapTo(node_symbol); this->frame[node_symbol] = TypedValue(v1); { auto *op = this->storage.template Create( identifier, std::vector{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("ANIMAL")}); auto value = this->Eval(op); EXPECT_EQ(value.ValueBool(), true); } { auto *op = this->storage.template Create( identifier, std::vector{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("BAD_DOG"), this->storage.GetLabelIx("ANIMAL")}); auto value = this->Eval(op); EXPECT_EQ(value.ValueBool(), false); } { this->frame[node_symbol] = TypedValue(); auto *op = this->storage.template Create( identifier, std::vector{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("BAD_DOG"), this->storage.GetLabelIx("ANIMAL")}); auto value = this->Eval(op); EXPECT_TRUE(value.IsNull()); } } TYPED_TEST(ExpressionEvaluatorTest, Aggregation) { auto aggr = this->storage.template Create(this->storage.template Create(42), nullptr, Aggregation::Op::COUNT, false); auto aggr_sym = this->symbol_table.CreateSymbol("aggr", true); aggr->MapTo(aggr_sym); this->frame[aggr_sym] = TypedValue(1); auto value = this->Eval(aggr); EXPECT_EQ(value.ValueInt(), 1); } TYPED_TEST(ExpressionEvaluatorTest, ListLiteral) { auto *list_literal = this->storage.template Create(std::vector{ this->storage.template Create(1), this->storage.template Create("bla"), this->storage.template Create(true)}); TypedValue result = this->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()); ; } TYPED_TEST(ExpressionEvaluatorTest, ParameterLookup) { this->ctx.parameters.Add(0, memgraph::storage::PropertyValue(42)); auto *param_lookup = this->storage.template Create(0); auto value = this->Eval(param_lookup); ASSERT_TRUE(value.IsInt()); EXPECT_EQ(value.ValueInt(), 42); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullList) { AstStorage storage; auto *all = ALL("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); auto value = this->Eval(all); EXPECT_TRUE(value.IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *all = ALL("x", LIST(LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(all); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAllWhereWrongType) { AstStorage storage; auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); all->identifier_->MapTo(x_sym); EXPECT_THROW(this->Eval(all), QueryRuntimeException); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullList) { AstStorage storage; auto *single = SINGLE("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); auto value = this->Eval(single); EXPECT_TRUE(value.IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *single = SINGLE("x", LIST(LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); single->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(single); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(any); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(any); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullList) { AstStorage storage; auto *any = ANY("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); auto value = this->Eval(any); EXPECT_TRUE(value.IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(0), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(any); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = ANY("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(any); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyWhereWrongType) { AstStorage storage; auto *any = ANY("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); EXPECT_THROW(this->Eval(any), QueryRuntimeException); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(none); ASSERT_TRUE(value.IsBool()); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(none); ASSERT_TRUE(value.IsBool()); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullList) { AstStorage storage; auto *none = NONE("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); auto value = this->Eval(none); EXPECT_TRUE(value.IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullElementInList1) { AstStorage storage; auto *ident_x = IDENT("x"); auto *any = NONE("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); any->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(any); EXPECT_TRUE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullElementInList2) { AstStorage storage; auto *ident_x = IDENT("x"); auto *none = NONE("x", LIST(LITERAL(0), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0)))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(none); EXPECT_FALSE(value.ValueBool()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneWhereWrongType) { AstStorage storage; auto *none = NONE("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); none->identifier_->MapTo(x_sym); EXPECT_THROW(this->Eval(none), QueryRuntimeException); } TYPED_TEST(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 = this->symbol_table.CreateSymbol("sum", true); reduce->accumulator_->MapTo(sum_sym); ident_sum->MapTo(sum_sym); const auto x_sym = this->symbol_table.CreateSymbol("x", true); reduce->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(reduce); ASSERT_TRUE(value.IsInt()); EXPECT_EQ(value.ValueInt(), 3); } TYPED_TEST(ExpressionEvaluatorTest, FunctionExtract) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), ADD(ident_x, LITERAL(1))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->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()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionExtractNull) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LITERAL(memgraph::storage::PropertyValue()), ADD(ident_x, LITERAL(1))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); auto value = this->Eval(extract); EXPECT_TRUE(value.IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, FunctionExtractExceptions) { AstStorage storage; auto *ident_x = IDENT("x"); auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1))); const auto x_sym = this->symbol_table.CreateSymbol("x", true); extract->identifier_->MapTo(x_sym); ident_x->MapTo(x_sym); EXPECT_THROW(this->Eval(extract), QueryRuntimeException); } TYPED_TEST(ExpressionEvaluatorTest, Coalesce) { // coalesce() EXPECT_THROW(this->Eval(COALESCE()), QueryRuntimeException); // coalesce(null, null) EXPECT_TRUE(this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(TypedValue()))).IsNull()); // coalesce(null, 2, 3) EXPECT_EQ(this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), LITERAL(3))).ValueInt(), 2); // coalesce(null, 2, assert(false), 3) EXPECT_EQ( this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), FN("ASSERT", LITERAL(false)), LITERAL(3))).ValueInt(), 2); // (null, assert(false)) EXPECT_THROW(this->Eval(COALESCE(LITERAL(TypedValue()), FN("ASSERT", LITERAL(false)))), QueryRuntimeException); // coalesce([null, null]) EXPECT_FALSE(this->Eval(COALESCE(LITERAL(TypedValue(std::vector{TypedValue(), TypedValue()})))).IsNull()); } TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidArguments) { EXPECT_TRUE(this->Eval(this->storage.template Create(LITERAL(TypedValue()), LITERAL("regex"))).IsNull()); EXPECT_TRUE(this->Eval(this->storage.template Create(LITERAL(3), LITERAL("regex"))).IsNull()); EXPECT_TRUE( this->Eval(this->storage.template Create(LIST(LITERAL("string")), LITERAL("regex"))).IsNull()); EXPECT_TRUE(this->Eval(this->storage.template Create(LITERAL("string"), LITERAL(TypedValue()))).IsNull()); EXPECT_THROW(this->Eval(this->storage.template Create(LITERAL("string"), LITERAL(42))), QueryRuntimeException); EXPECT_THROW(this->Eval(this->storage.template Create(LITERAL("string"), LIST(LITERAL("regex")))), QueryRuntimeException); } TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidRegex) { EXPECT_THROW(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL("*ext"))), QueryRuntimeException); EXPECT_THROW(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL("[ext"))), QueryRuntimeException); } TYPED_TEST(ExpressionEvaluatorTest, RegexMatch) { EXPECT_FALSE(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL(".*ex"))).ValueBool()); EXPECT_TRUE(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL(".*ext"))).ValueBool()); EXPECT_FALSE(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL("[ext]"))).ValueBool()); EXPECT_TRUE(this->Eval(this->storage.template Create(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool()); } template class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { protected: std::pair prop_age = std::make_pair("age", this->dba.NameToProperty("age")); std::pair prop_height = std::make_pair("height", this->dba.NameToProperty("height")); Identifier *identifier = this->storage.template Create("element"); Symbol symbol = this->symbol_table.CreateSymbol("element", true); void SetUp() { identifier->MapTo(symbol); } auto Value(std::pair property) { auto *op = this->storage.template Create(identifier, this->storage.GetPropertyIx(property.first)); return this->Eval(op); } }; TYPED_TEST_CASE(ExpressionEvaluatorPropertyLookup, StorageTypes); TYPED_TEST(ExpressionEvaluatorPropertyLookup, Vertex) { auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); this->dba.AdvanceCommand(); this->frame[this->symbol] = TypedValue(v1); EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10); EXPECT_TRUE(this->Value(this->prop_height).IsNull()); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, Duration) { const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45}); this->frame[this->symbol] = TypedValue(dur); const std::pair day = std::make_pair("day", this->dba.NameToProperty("day")); const auto total_days = this->Value(day); EXPECT_TRUE(total_days.IsInt()); EXPECT_EQ(total_days.ValueInt(), 10); const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour")); const auto total_hours = this->Value(hour); EXPECT_TRUE(total_hours.IsInt()); EXPECT_EQ(total_hours.ValueInt(), 1); const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute")); const auto total_mins = this->Value(minute); EXPECT_TRUE(total_mins.IsInt()); EXPECT_EQ(total_mins.ValueInt(), 1 * 60 + 30); const std::pair sec = std::make_pair("second", this->dba.NameToProperty("second")); const auto total_secs = this->Value(sec); EXPECT_TRUE(total_secs.IsInt()); const auto expected_secs = total_mins.ValueInt() * 60 + 2; EXPECT_EQ(total_secs.ValueInt(), expected_secs); const std::pair milli = std::make_pair("millisecond", this->dba.NameToProperty("millisecond")); const auto total_milli = this->Value(milli); EXPECT_TRUE(total_milli.IsInt()); const auto expected_milli = total_secs.ValueInt() * 1000 + 22; EXPECT_EQ(total_milli.ValueInt(), expected_milli); const std::pair micro = std::make_pair("microsecond", this->dba.NameToProperty("microsecond")); const auto total_micros = this->Value(micro); EXPECT_TRUE(total_micros.IsInt()); const auto expected_micros = expected_milli * 1000 + 45; EXPECT_EQ(total_micros.ValueInt(), expected_micros); const std::pair nano = std::make_pair("nanosecond", this->dba.NameToProperty("nanosecond")); const auto total_nano = this->Value(nano); EXPECT_TRUE(total_nano.IsInt()); const auto expected_nano = expected_micros * 1000; EXPECT_EQ(total_nano.ValueInt(), expected_nano); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, Date) { const memgraph::utils::Date date({1996, 11, 22}); this->frame[this->symbol] = TypedValue(date); const std::pair year = std::make_pair("year", this->dba.NameToProperty("year")); const auto y = this->Value(year); EXPECT_TRUE(y.IsInt()); EXPECT_EQ(y.ValueInt(), 1996); const std::pair month = std::make_pair("month", this->dba.NameToProperty("month")); const auto m = this->Value(month); EXPECT_TRUE(m.IsInt()); EXPECT_EQ(m.ValueInt(), 11); const std::pair day = std::make_pair("day", this->dba.NameToProperty("day")); const auto d = this->Value(day); EXPECT_TRUE(d.IsInt()); EXPECT_EQ(d.ValueInt(), 22); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, LocalTime) { const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22}); this->frame[this->symbol] = TypedValue(lt); const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour")); const auto h = this->Value(hour); EXPECT_TRUE(h.IsInt()); EXPECT_EQ(h.ValueInt(), 1); const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute")); const auto min = this->Value(minute); EXPECT_TRUE(min.IsInt()); EXPECT_EQ(min.ValueInt(), 2); const std::pair second = std::make_pair("second", this->dba.NameToProperty("second")); const auto sec = this->Value(second); EXPECT_TRUE(sec.IsInt()); EXPECT_EQ(sec.ValueInt(), 3); const std::pair millis = std::make_pair("millisecond", this->dba.NameToProperty("millisecond")); const auto mil = this->Value(millis); EXPECT_TRUE(mil.IsInt()); EXPECT_EQ(mil.ValueInt(), 11); const std::pair micros = std::make_pair("microsecond", this->dba.NameToProperty("microsecond")); const auto mic = this->Value(micros); EXPECT_TRUE(mic.IsInt()); EXPECT_EQ(mic.ValueInt(), 22); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, LocalDateTime) { const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40}); this->frame[this->symbol] = TypedValue(ldt); const std::pair year = std::make_pair("year", this->dba.NameToProperty("year")); const auto y = this->Value(year); EXPECT_TRUE(y.IsInt()); EXPECT_EQ(y.ValueInt(), 1993); const std::pair month = std::make_pair("month", this->dba.NameToProperty("month")); const auto m = this->Value(month); EXPECT_TRUE(m.IsInt()); EXPECT_EQ(m.ValueInt(), 8); const std::pair day = std::make_pair("day", this->dba.NameToProperty("day")); const auto d = this->Value(day); EXPECT_TRUE(d.IsInt()); EXPECT_EQ(d.ValueInt(), 6); const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour")); const auto h = this->Value(hour); EXPECT_TRUE(h.IsInt()); EXPECT_EQ(h.ValueInt(), 2); const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute")); const auto min = this->Value(minute); EXPECT_TRUE(min.IsInt()); EXPECT_EQ(min.ValueInt(), 3); const std::pair second = std::make_pair("second", this->dba.NameToProperty("second")); const auto sec = this->Value(second); EXPECT_TRUE(sec.IsInt()); EXPECT_EQ(sec.ValueInt(), 4); const std::pair millis = std::make_pair("millisecond", this->dba.NameToProperty("millisecond")); const auto mil = this->Value(millis); EXPECT_TRUE(mil.IsInt()); EXPECT_EQ(mil.ValueInt(), 55); const std::pair micros = std::make_pair("microsecond", this->dba.NameToProperty("microsecond")); const auto mic = this->Value(micros); EXPECT_TRUE(mic.IsInt()); EXPECT_EQ(mic.ValueInt(), 40); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, Edge) { auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("edge_type")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(e12->SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); this->dba.AdvanceCommand(); this->frame[this->symbol] = TypedValue(*e12); EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10); EXPECT_TRUE(this->Value(this->prop_height).IsNull()); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, Null) { this->frame[this->symbol] = TypedValue(); EXPECT_TRUE(this->Value(this->prop_age).IsNull()); } TYPED_TEST(ExpressionEvaluatorPropertyLookup, Map) { this->frame[this->symbol] = TypedValue(std::map{{this->prop_age.first, TypedValue(10)}}); EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10); EXPECT_TRUE(this->Value(this->prop_height).IsNull()); } template class ExpressionEvaluatorAllPropertiesLookup : public ExpressionEvaluatorTest { protected: std::pair prop_age = std::make_pair("age", this->dba.NameToProperty("age")); std::pair prop_height = std::make_pair("height", this->dba.NameToProperty("height")); Identifier *identifier = this->storage.template Create("element"); Symbol symbol = this->symbol_table.CreateSymbol("element", true); void SetUp() { identifier->MapTo(symbol); } auto Value() { auto *op = this->storage.template Create(identifier); return this->Eval(op); } }; TYPED_TEST_CASE(ExpressionEvaluatorAllPropertiesLookup, StorageTypes); TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Vertex) { auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); this->dba.AdvanceCommand(); this->frame[this->symbol] = TypedValue(v1); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Edge) { auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("edge_type")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(e12->SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); this->dba.AdvanceCommand(); this->frame[this->symbol] = TypedValue(*e12); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Duration) { const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45}); this->frame[this->symbol] = TypedValue(dur); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Date) { const memgraph::utils::Date date({1996, 11, 22}); this->frame[this->symbol] = TypedValue(date); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalTime) { const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22}); this->frame[this->symbol] = TypedValue(lt); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalDateTime) { const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40}); this->frame[this->symbol] = TypedValue(ldt); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Null) { this->frame[this->symbol] = TypedValue(); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsNull()); } TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Map) { this->frame[this->symbol] = TypedValue(std::map{{this->prop_age.first, TypedValue(10)}}); auto all_properties = this->Value(); EXPECT_TRUE(all_properties.IsMap()); } template 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 = this->storage.template Create("arg_" + std::to_string(i), true); auto sym = this->symbol_table.CreateSymbol("arg_" + std::to_string(i), true); ident->MapTo(sym); this->frame[sym] = tvs[i]; expressions.push_back(ident); } return expressions; } TypedValue EvaluateFunctionWithExprs(const std::string &function_name, const std::vector &expressions) { auto *op = this->storage.template Create(function_name, expressions); return this->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)...})); } }; TYPED_TEST_CASE(FunctionTest, StorageTypes); template static TypedValue MakeTypedValueList(TArgs &&...args) { return TypedValue(std::vector{TypedValue(args)...}); } TYPED_TEST(FunctionTest, EndNode) { ASSERT_THROW(this->EvaluateFunction("ENDNODE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("ENDNODE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue()); auto v2 = this->dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue()); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(*this->EvaluateFunction("ENDNODE", *e) .ValueVertex() .HasLabel(memgraph::storage::View::NEW, this->dba.NameToLabel("label2"))); ASSERT_THROW(this->EvaluateFunction("ENDNODE", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Head) { ASSERT_THROW(this->EvaluateFunction("HEAD"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("HEAD", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(this->EvaluateFunction("HEAD", argument).ValueInt(), 3); argument.ValueList().clear(); ASSERT_TRUE(this->EvaluateFunction("HEAD", argument).IsNull()); ASSERT_THROW(this->EvaluateFunction("HEAD", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Properties) { ASSERT_THROW(this->EvaluateFunction("PROPERTIES"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("PROPERTIES", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue()); ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue()); auto v2 = this->dba.InsertVertex(); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(3)).HasValue()); ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue()); this->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(this->EvaluateFunction("PROPERTIES", v1)), UnorderedElementsAre(testing::Pair("height", 5), testing::Pair("age", 10))); ASSERT_THAT(prop_values_to_int(this->EvaluateFunction("PROPERTIES", *e)), UnorderedElementsAre(testing::Pair("height", 3), testing::Pair("age", 15))); ASSERT_THROW(this->EvaluateFunction("PROPERTIES", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Last) { ASSERT_THROW(this->EvaluateFunction("LAST"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("LAST", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(this->EvaluateFunction("LAST", argument).ValueInt(), 5); argument.ValueList().clear(); ASSERT_TRUE(this->EvaluateFunction("LAST", argument).IsNull()); ASSERT_THROW(this->EvaluateFunction("LAST", 5), QueryRuntimeException); } TYPED_TEST(FunctionTest, Size) { ASSERT_THROW(this->EvaluateFunction("SIZE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("SIZE", TypedValue()).IsNull()); auto argument = MakeTypedValueList(3, 4, 5); ASSERT_EQ(this->EvaluateFunction("SIZE", argument).ValueInt(), 3); ASSERT_EQ(this->EvaluateFunction("SIZE", "john").ValueInt(), 4); ASSERT_EQ(this->EvaluateFunction("SIZE", std::map{ {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}}) .ValueInt(), 3); ASSERT_THROW(this->EvaluateFunction("SIZE", 5), QueryRuntimeException); auto v0 = this->dba.InsertVertex(); memgraph::query::Path path(v0); EXPECT_EQ(this->EvaluateFunction("SIZE", path).ValueInt(), 0); auto v1 = this->dba.InsertVertex(); auto edge = this->dba.InsertEdge(&v0, &v1, this->dba.NameToEdgeType("type")); ASSERT_TRUE(edge.HasValue()); path.Expand(*edge); path.Expand(v1); EXPECT_EQ(this->EvaluateFunction("SIZE", path).ValueInt(), 1); } TYPED_TEST(FunctionTest, StartNode) { ASSERT_THROW(this->EvaluateFunction("STARTNODE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("STARTNODE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue()); auto v2 = this->dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue()); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(*this->EvaluateFunction("STARTNODE", *e) .ValueVertex() .HasLabel(memgraph::storage::View::NEW, this->dba.NameToLabel("label1"))); ASSERT_THROW(this->EvaluateFunction("STARTNODE", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Degree) { ASSERT_THROW(this->EvaluateFunction("DEGREE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("DEGREE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto v3 = this->dba.InsertVertex(); auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue()); this->dba.AdvanceCommand(); ASSERT_EQ(this->EvaluateFunction("DEGREE", v1).ValueInt(), 1); ASSERT_EQ(this->EvaluateFunction("DEGREE", v2).ValueInt(), 2); ASSERT_EQ(this->EvaluateFunction("DEGREE", v3).ValueInt(), 1); ASSERT_THROW(this->EvaluateFunction("DEGREE", 2), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("DEGREE", *e12), QueryRuntimeException); } TYPED_TEST(FunctionTest, InDegree) { ASSERT_THROW(this->EvaluateFunction("INDEGREE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("INDEGREE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto v3 = this->dba.InsertVertex(); auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue()); this->dba.AdvanceCommand(); ASSERT_EQ(this->EvaluateFunction("INDEGREE", v1).ValueInt(), 0); ASSERT_EQ(this->EvaluateFunction("INDEGREE", v2).ValueInt(), 2); ASSERT_EQ(this->EvaluateFunction("INDEGREE", v3).ValueInt(), 0); ASSERT_THROW(this->EvaluateFunction("INDEGREE", 2), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("INDEGREE", *e12), QueryRuntimeException); } TYPED_TEST(FunctionTest, OutDegree) { ASSERT_THROW(this->EvaluateFunction("OUTDEGREE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("OUTDEGREE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto v3 = this->dba.InsertVertex(); auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t")); ASSERT_TRUE(e12.HasValue()); ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue()); this->dba.AdvanceCommand(); ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1); ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0); ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1); ASSERT_THROW(this->EvaluateFunction("OUTDEGREE", 2), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("OUTDEGREE", *e12), QueryRuntimeException); } TYPED_TEST(FunctionTest, ToBoolean) { ASSERT_THROW(this->EvaluateFunction("TOBOOLEAN"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", 123).ValueBool(), true); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", -213).ValueBool(), true); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", 0).ValueBool(), false); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", " trUE \n\t").ValueBool(), true); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", "\n\tFalsE").ValueBool(), false); ASSERT_TRUE(this->EvaluateFunction("TOBOOLEAN", "\n\tFALSEA ").IsNull()); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", true).ValueBool(), true); ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", false).ValueBool(), false); } TYPED_TEST(FunctionTest, ToFloat) { ASSERT_THROW(this->EvaluateFunction("TOFLOAT"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("TOFLOAT", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("TOFLOAT", " -3.5 \n\t").ValueDouble(), -3.5); ASSERT_EQ(this->EvaluateFunction("TOFLOAT", "\n\t0.5e-1").ValueDouble(), 0.05); ASSERT_TRUE(this->EvaluateFunction("TOFLOAT", "\n\t3.4e-3X ").IsNull()); ASSERT_EQ(this->EvaluateFunction("TOFLOAT", -3.5).ValueDouble(), -3.5); ASSERT_EQ(this->EvaluateFunction("TOFLOAT", -3).ValueDouble(), -3.0); ASSERT_THROW(this->EvaluateFunction("TOFLOAT", true), QueryRuntimeException); } TYPED_TEST(FunctionTest, ToInteger) { ASSERT_THROW(this->EvaluateFunction("TOINTEGER"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("TOINTEGER", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", false).ValueInt(), 0); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", true).ValueInt(), 1); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", "\n\t3").ValueInt(), 3); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", " -3.5 \n\t").ValueInt(), -3); ASSERT_TRUE(this->EvaluateFunction("TOINTEGER", "\n\t3X ").IsNull()); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", -3.5).ValueInt(), -3); ASSERT_EQ(this->EvaluateFunction("TOINTEGER", 3.5).ValueInt(), 3); } TYPED_TEST(FunctionTest, Type) { ASSERT_THROW(this->EvaluateFunction("TYPE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("TYPE", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue()); auto v2 = this->dba.InsertVertex(); ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue()); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_EQ(this->EvaluateFunction("TYPE", *e).ValueString(), "type1"); ASSERT_THROW(this->EvaluateFunction("TYPE", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, ValueType) { ASSERT_THROW(this->EvaluateFunction("VALUETYPE"), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), QueryRuntimeException); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), "BOOLEAN"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), "INTEGER"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), "FLOAT"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), "STRING"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(std::vector{TypedValue(1), TypedValue(2)})) .ValueString(), "LIST"); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(std::map{{"test", TypedValue(1)}})) .ValueString(), "MAP"); auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE"); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", *e).ValueString(), "RELATIONSHIP"); Path p(v1, *e, v2); ASSERT_EQ(this->EvaluateFunction("VALUETYPE", p).ValueString(), "PATH"); } TYPED_TEST(FunctionTest, Labels) { ASSERT_THROW(this->EvaluateFunction("LABELS"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("LABELS", TypedValue()).IsNull()); auto v = this->dba.InsertVertex(); ASSERT_TRUE(v.AddLabel(this->dba.NameToLabel("label1")).HasValue()); ASSERT_TRUE(v.AddLabel(this->dba.NameToLabel("label2")).HasValue()); this->dba.AdvanceCommand(); std::vector labels; auto _labels = this->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(this->EvaluateFunction("LABELS", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, NodesRelationships) { EXPECT_THROW(this->EvaluateFunction("NODES"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RELATIONSHIPS"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("NODES", TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull()); { auto v1 = this->dba.InsertVertex(); auto v2 = this->dba.InsertVertex(); auto v3 = this->dba.InsertVertex(); auto e1 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("Type")); ASSERT_TRUE(e1.HasValue()); auto e2 = this->dba.InsertEdge(&v2, &v3, this->dba.NameToEdgeType("Type")); ASSERT_TRUE(e2.HasValue()); memgraph::query::Path path{v1, *e1, v2, *e2, v3}; this->dba.AdvanceCommand(); auto _nodes = this->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 = this->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(this->EvaluateFunction("NODES", 2), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RELATIONSHIPS", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Range) { EXPECT_THROW(this->EvaluateFunction("RANGE"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull()); EXPECT_THROW(this->EvaluateFunction("RANGE", 1, TypedValue(), 1.3), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RANGE", 1, 2, 0), QueryRuntimeException); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 1, 3)), ElementsAre(1, 2, 3)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", -1, 5, 2)), ElementsAre(-1, 1, 3, 5)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 10, 3)), ElementsAre(2, 5, 8)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 2, 2)), ElementsAre(2)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 3, 0, 5)), ElementsAre()); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 5, 1, -2)), ElementsAre(5, 3, 1)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 6, 1, -2)), ElementsAre(6, 4, 2)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 2, -3)), ElementsAre(2)); EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", -2, 4, -1)), ElementsAre()); } TYPED_TEST(FunctionTest, Keys) { ASSERT_THROW(this->EvaluateFunction("KEYS"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("KEYS", TypedValue()).IsNull()); auto v1 = this->dba.InsertVertex(); ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue()); ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue()); auto v2 = this->dba.InsertVertex(); auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1")); ASSERT_TRUE(e.HasValue()); ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("width"), memgraph::storage::PropertyValue(3)).HasValue()); ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue()); this->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(this->EvaluateFunction("KEYS", v1)), UnorderedElementsAre("height", "age")); ASSERT_THAT(prop_keys_to_string(this->EvaluateFunction("KEYS", *e)), UnorderedElementsAre("width", "age")); ASSERT_THROW(this->EvaluateFunction("KEYS", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, Tail) { ASSERT_THROW(this->EvaluateFunction("TAIL"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("TAIL", TypedValue()).IsNull()); auto argument = MakeTypedValueList(); ASSERT_EQ(this->EvaluateFunction("TAIL", argument).ValueList().size(), 0U); argument = MakeTypedValueList(3, 4, true, "john"); auto list = this->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(this->EvaluateFunction("TAIL", 2), QueryRuntimeException); } TYPED_TEST(FunctionTest, UniformSample) { ASSERT_THROW(this->EvaluateFunction("UNIFORMSAMPLE"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull()); ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull()); ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()).IsNull()); ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull()); ASSERT_THROW(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), -1), QueryRuntimeException); ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 0).ValueList().size(), 0); ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 2).ValueList().size(), 2); ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 3).ValueList().size(), 3); ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 5).ValueList().size(), 5); } TYPED_TEST(FunctionTest, Abs) { ASSERT_THROW(this->EvaluateFunction("ABS"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("ABS", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("ABS", -2).ValueInt(), 2); ASSERT_EQ(this->EvaluateFunction("ABS", -2.5).ValueDouble(), 2.5); ASSERT_THROW(this->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.. TYPED_TEST(FunctionTest, Log) { ASSERT_THROW(this->EvaluateFunction("LOG"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("LOG", TypedValue()).IsNull()); ASSERT_DOUBLE_EQ(this->EvaluateFunction("LOG", 2).ValueDouble(), log(2)); ASSERT_DOUBLE_EQ(this->EvaluateFunction("LOG", 1.5).ValueDouble(), log(1.5)); // Not portable, but should work on most platforms. ASSERT_TRUE(std::isnan(this->EvaluateFunction("LOG", -1.5).ValueDouble())); ASSERT_THROW(this->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. TYPED_TEST(FunctionTest, Round) { ASSERT_THROW(this->EvaluateFunction("ROUND"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("ROUND", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("ROUND", -2).ValueDouble(), -2); ASSERT_EQ(this->EvaluateFunction("ROUND", -2.4).ValueDouble(), -2); ASSERT_EQ(this->EvaluateFunction("ROUND", -2.5).ValueDouble(), -3); ASSERT_EQ(this->EvaluateFunction("ROUND", -2.6).ValueDouble(), -3); ASSERT_EQ(this->EvaluateFunction("ROUND", 2.4).ValueDouble(), 2); ASSERT_EQ(this->EvaluateFunction("ROUND", 2.5).ValueDouble(), 3); ASSERT_EQ(this->EvaluateFunction("ROUND", 2.6).ValueDouble(), 3); ASSERT_THROW(this->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. TYPED_TEST(FunctionTest, WrappedMathFunctions) { for (auto function_name : {"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN", "ATAN", "COS", "SIN", "TAN"}) { this->EvaluateFunction(function_name, 0.5); } } TYPED_TEST(FunctionTest, Atan2) { ASSERT_THROW(this->EvaluateFunction("ATAN2"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("ATAN2", TypedValue(), 1).IsNull()); ASSERT_TRUE(this->EvaluateFunction("ATAN2", 1, TypedValue()).IsNull()); ASSERT_DOUBLE_EQ(this->EvaluateFunction("ATAN2", 2, -1.0).ValueDouble(), atan2(2, -1)); ASSERT_THROW(this->EvaluateFunction("ATAN2", 3.0, true), QueryRuntimeException); } TYPED_TEST(FunctionTest, Sign) { ASSERT_THROW(this->EvaluateFunction("SIGN"), QueryRuntimeException); ASSERT_TRUE(this->EvaluateFunction("SIGN", TypedValue()).IsNull()); ASSERT_EQ(this->EvaluateFunction("SIGN", -2).ValueInt(), -1); ASSERT_EQ(this->EvaluateFunction("SIGN", -0.2).ValueInt(), -1); ASSERT_EQ(this->EvaluateFunction("SIGN", 0.0).ValueInt(), 0); ASSERT_EQ(this->EvaluateFunction("SIGN", 2.5).ValueInt(), 1); ASSERT_THROW(this->EvaluateFunction("SIGN", true), QueryRuntimeException); } TYPED_TEST(FunctionTest, E) { ASSERT_THROW(this->EvaluateFunction("E", 1), QueryRuntimeException); ASSERT_DOUBLE_EQ(this->EvaluateFunction("E").ValueDouble(), M_E); } TYPED_TEST(FunctionTest, Pi) { ASSERT_THROW(this->EvaluateFunction("PI", 1), QueryRuntimeException); ASSERT_DOUBLE_EQ(this->EvaluateFunction("PI").ValueDouble(), M_PI); } TYPED_TEST(FunctionTest, Rand) { ASSERT_THROW(this->EvaluateFunction("RAND", 1), QueryRuntimeException); ASSERT_GE(this->EvaluateFunction("RAND").ValueDouble(), 0.0); ASSERT_LT(this->EvaluateFunction("RAND").ValueDouble(), 1.0); } TYPED_TEST(FunctionTest, StartsWith) { EXPECT_THROW(this->EvaluateFunction(kStartsWith), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "a", TypedValue()).IsNull()); EXPECT_THROW(this->EvaluateFunction(kStartsWith, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "abc", "abc").ValueBool()); EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "abcdef", "abc").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kStartsWith, "abcdef", "aBc").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kStartsWith, "abc", "abcd").ValueBool()); } TYPED_TEST(FunctionTest, EndsWith) { EXPECT_THROW(this->EvaluateFunction(kEndsWith), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "a", TypedValue()).IsNull()); EXPECT_THROW(this->EvaluateFunction(kEndsWith, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "abc", "abc").ValueBool()); EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "abcdef", "def").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kEndsWith, "abcdef", "dEf").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kEndsWith, "bcd", "abcd").ValueBool()); } TYPED_TEST(FunctionTest, Contains) { EXPECT_THROW(this->EvaluateFunction(kContains), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kContains, "a", TypedValue()).IsNull()); EXPECT_THROW(this->EvaluateFunction(kContains, TypedValue(), 1.3), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction(kContains, "abc", "abc").ValueBool()); EXPECT_TRUE(this->EvaluateFunction(kContains, "abcde", "bcd").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kContains, "cde", "abcdef").ValueBool()); EXPECT_FALSE(this->EvaluateFunction(kContains, "abcdef", "dEf").ValueBool()); } TYPED_TEST(FunctionTest, Assert) { // Invalid calls. ASSERT_THROW(this->EvaluateFunction("ASSERT"), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("ASSERT", false, false), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("ASSERT", "string", false), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("ASSERT", false, "reason", true), QueryRuntimeException); // Valid calls, assertion fails. ASSERT_THROW(this->EvaluateFunction("ASSERT", false), QueryRuntimeException); ASSERT_THROW(this->EvaluateFunction("ASSERT", false, "message"), QueryRuntimeException); try { this->EvaluateFunction("ASSERT", false, "bbgba"); } catch (QueryRuntimeException &e) { ASSERT_TRUE(std::string(e.what()).find("bbgba") != std::string::npos); } // Valid calls, assertion passes. ASSERT_TRUE(this->EvaluateFunction("ASSERT", true).ValueBool()); ASSERT_TRUE(this->EvaluateFunction("ASSERT", true, "message").ValueBool()); } TYPED_TEST(FunctionTest, Counter) { EXPECT_THROW(this->EvaluateFunction("COUNTER"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("COUNTER", "a"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("COUNTER", "a", "b"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("COUNTER", "a", "b", "c"), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 1); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 2); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 1); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), -1); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 1); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 5); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 10); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -5); EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -10); EXPECT_THROW(this->EvaluateFunction("COUNTER", "c6", 0, 0), QueryRuntimeException); } TYPED_TEST(FunctionTest, Id) { auto va = this->dba.InsertVertex(); auto ea = this->dba.InsertEdge(&va, &va, this->dba.NameToEdgeType("edge")); ASSERT_TRUE(ea.HasValue()); auto vb = this->dba.InsertVertex(); this->dba.AdvanceCommand(); EXPECT_TRUE(this->EvaluateFunction("ID", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("ID", va).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("ID", *ea).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("ID", vb).ValueInt(), 1); EXPECT_THROW(this->EvaluateFunction("ID"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("ID", 0), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("ID", va, *ea), QueryRuntimeException); } TYPED_TEST(FunctionTest, ToStringNull) { EXPECT_TRUE(this->EvaluateFunction("TOSTRING", TypedValue()).IsNull()); } TYPED_TEST(FunctionTest, ToStringString) { EXPECT_EQ(this->EvaluateFunction("TOSTRING", "").ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("TOSTRING", "this is a string").ValueString(), "this is a string"); } TYPED_TEST(FunctionTest, ToStringInteger) { EXPECT_EQ(this->EvaluateFunction("TOSTRING", -23321312).ValueString(), "-23321312"); EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0).ValueString(), "0"); EXPECT_EQ(this->EvaluateFunction("TOSTRING", 42).ValueString(), "42"); } TYPED_TEST(FunctionTest, ToStringDouble) { EXPECT_EQ(this->EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000"); EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000"); EXPECT_EQ(this->EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322"); } TYPED_TEST(FunctionTest, ToStringBool) { EXPECT_EQ(this->EvaluateFunction("TOSTRING", true).ValueString(), "true"); EXPECT_EQ(this->EvaluateFunction("TOSTRING", false).ValueString(), "false"); } TYPED_TEST(FunctionTest, ToStringDate) { const auto date = memgraph::utils::Date({1970, 1, 2}); EXPECT_EQ(this->EvaluateFunction("TOSTRING", date).ValueString(), "1970-01-02"); } TYPED_TEST(FunctionTest, ToStringLocalTime) { const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); EXPECT_EQ(this->EvaluateFunction("TOSTRING", lt).ValueString(), "13:02:40.100050"); } TYPED_TEST(FunctionTest, ToStringLocalDateTime) { const auto ldt = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59}); EXPECT_EQ(this->EvaluateFunction("TOSTRING", ldt).ValueString(), "1970-01-02T23:02:59.000000"); } TYPED_TEST(FunctionTest, ToStringDuration) { memgraph::utils::Duration duration{{.minute = 2, .second = 2, .microsecond = 33}}; EXPECT_EQ(this->EvaluateFunction("TOSTRING", duration).ValueString(), "P0DT0H2M2.000033S"); } TYPED_TEST(FunctionTest, ToStringExceptions) { EXPECT_THROW(this->EvaluateFunction("TOSTRING", 1, 2, 3), QueryRuntimeException); } TYPED_TEST(FunctionTest, TimestampVoid) { this->ctx.timestamp = 42; EXPECT_EQ(this->EvaluateFunction("TIMESTAMP").ValueInt(), 42); } TYPED_TEST(FunctionTest, TimestampDate) { this->ctx.timestamp = 42; EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1970, 1, 1})).ValueInt(), 0); EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1971, 1, 1})).ValueInt(), 31536000000000); } TYPED_TEST(FunctionTest, TimestampLocalTime) { this->ctx.timestamp = 42; const memgraph::utils::LocalTime time(10000); EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000); } TYPED_TEST(FunctionTest, TimestampLocalDateTime) { this->ctx.timestamp = 42; const memgraph::utils::LocalDateTime time(20000); EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); } TYPED_TEST(FunctionTest, TimestampDuration) { this->ctx.timestamp = 42; const memgraph::utils::Duration time(20000); EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); } TYPED_TEST(FunctionTest, TimestampExceptions) { this->ctx.timestamp = 42; EXPECT_THROW(this->EvaluateFunction("TIMESTAMP", 1).ValueInt(), QueryRuntimeException); } TYPED_TEST(FunctionTest, Left) { EXPECT_THROW(this->EvaluateFunction("LEFT"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("LEFT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("LEFT", TypedValue(), 10).IsNull()); EXPECT_THROW(this->EvaluateFunction("LEFT", TypedValue(), -10), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 0).ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 3).ValueString(), "mem"); EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 1000).ValueString(), "memgraph"); EXPECT_THROW(this->EvaluateFunction("LEFT", "memgraph", -10), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("LEFT", "memgraph", "graph"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("LEFT", 132, 10), QueryRuntimeException); } TYPED_TEST(FunctionTest, Right) { EXPECT_THROW(this->EvaluateFunction("RIGHT"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("RIGHT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("RIGHT", TypedValue(), 10).IsNull()); EXPECT_THROW(this->EvaluateFunction("RIGHT", TypedValue(), -10), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 0).ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 3).ValueString(), "aph"); EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 1000).ValueString(), "memgraph"); EXPECT_THROW(this->EvaluateFunction("RIGHT", "memgraph", -10), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RIGHT", "memgraph", "graph"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RIGHT", 132, 10), QueryRuntimeException); } TYPED_TEST(FunctionTest, Trimming) { EXPECT_TRUE(this->EvaluateFunction("LTRIM", TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("RTRIM", TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("TRIM", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("LTRIM", " abc ").ValueString(), "abc "); EXPECT_EQ(this->EvaluateFunction("RTRIM", " abc ").ValueString(), " abc"); EXPECT_EQ(this->EvaluateFunction("TRIM", "abc").ValueString(), "abc"); EXPECT_THROW(this->EvaluateFunction("LTRIM", "x", "y"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("RTRIM", "x", "y"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TRIM", "x", "y"), QueryRuntimeException); } TYPED_TEST(FunctionTest, Reverse) { EXPECT_TRUE(this->EvaluateFunction("REVERSE", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("REVERSE", "abc").ValueString(), "cba"); EXPECT_THROW(this->EvaluateFunction("REVERSE", "x", "y"), QueryRuntimeException); } TYPED_TEST(FunctionTest, Replace) { EXPECT_THROW(this->EvaluateFunction("REPLACE"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("REPLACE", TypedValue(), "l", "w").IsNull()); EXPECT_TRUE(this->EvaluateFunction("REPLACE", "hello", TypedValue(), "w").IsNull()); EXPECT_TRUE(this->EvaluateFunction("REPLACE", "hello", "l", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("REPLACE", "hello", "l", "w").ValueString(), "hewwo"); EXPECT_THROW(this->EvaluateFunction("REPLACE", 1, "l", "w"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("REPLACE", "hello", 1, "w"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("REPLACE", "hello", "l", 1), QueryRuntimeException); } TYPED_TEST(FunctionTest, Split) { EXPECT_THROW(this->EvaluateFunction("SPLIT"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("SPLIT", "one,two", 1), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("SPLIT", 1, "one,two"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("SPLIT", TypedValue(), TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("SPLIT", "one,two", TypedValue()).IsNull()); EXPECT_TRUE(this->EvaluateFunction("SPLIT", TypedValue(), ",").IsNull()); auto result = this->EvaluateFunction("SPLIT", "one,two", ","); EXPECT_TRUE(result.IsList()); EXPECT_EQ(result.ValueList()[0].ValueString(), "one"); EXPECT_EQ(result.ValueList()[1].ValueString(), "two"); } TYPED_TEST(FunctionTest, Substring) { EXPECT_THROW(this->EvaluateFunction("SUBSTRING"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, 10).IsNull()); EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), TypedValue()), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), -10), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, TypedValue()), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, -10), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 2).ValueString(), "llo"); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 10).ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 2, 0).ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 3).ValueString(), "ell"); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 4).ValueString(), "ello"); EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 10).ValueString(), "ello"); } TYPED_TEST(FunctionTest, ToLower) { EXPECT_THROW(this->EvaluateFunction("TOLOWER"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("TOLOWER", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("TOLOWER", "Ab__C").ValueString(), "ab__c"); } TYPED_TEST(FunctionTest, ToUpper) { EXPECT_THROW(this->EvaluateFunction("TOUPPER"), QueryRuntimeException); EXPECT_TRUE(this->EvaluateFunction("TOUPPER", TypedValue()).IsNull()); EXPECT_EQ(this->EvaluateFunction("TOUPPER", "Ab__C").ValueString(), "AB__C"); } TYPED_TEST(FunctionTest, ToByteString) { EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", 42), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", TypedValue()), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "", 42), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "ff"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "00"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "0xG"), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "").ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x").ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0X").ValueString(), ""); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x0123456789aAbBcCdDeEfF").ValueString(), "\x01\x23\x45\x67\x89\xAA\xBB\xCC\xDD\xEE\xFF"); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x042").ValueString().size(), 2); EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x042").ValueString(), memgraph::utils::pmr::string("\x00\x42", 2, memgraph::utils::NewDeleteResource())); } TYPED_TEST(FunctionTest, FromByteString) { EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING"), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING", 42), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING", TypedValue()), QueryRuntimeException); EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", "").ValueString(), ""); auto bytestring = this->EvaluateFunction("TOBYTESTRING", "0x123456789aAbBcCdDeEfF"); EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", bytestring).ValueString(), "0x0123456789aabbccddeeff"); EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042"); } TYPED_TEST(FunctionTest, Date) { const auto unix_epoch = memgraph::utils::Date({1970, 1, 1}); EXPECT_EQ(this->EvaluateFunction("DATE", "1970-01-01").ValueDate(), unix_epoch); const auto map_param = TypedValue( std::map{{"year", TypedValue(1970)}, {"month", TypedValue(1)}, {"day", TypedValue(1)}}); EXPECT_EQ(this->EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch); const auto today = memgraph::utils::CurrentDate(); EXPECT_EQ(this->EvaluateFunction("DATE").ValueDate(), today); EXPECT_THROW(this->EvaluateFunction("DATE", "{}"), memgraph::utils::BasicException); EXPECT_THROW(this->EvaluateFunction("DATE", std::map{{"years", TypedValue(1970)}}), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("DATE", std::map{{"mnths", TypedValue(1970)}}), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("DATE", std::map{{"dayz", TypedValue(1970)}}), QueryRuntimeException); } TYPED_TEST(FunctionTest, LocalTime) { const auto local_time = memgraph::utils::LocalTime({13, 3, 2, 0, 0}); EXPECT_EQ(this->EvaluateFunction("LOCALTIME", "130302").ValueLocalTime(), local_time); const auto one_sec_in_microseconds = 1000000; const auto map_param = TypedValue(std::map{{"hour", TypedValue(1)}, {"minute", TypedValue(2)}, {"second", TypedValue(3)}, {"millisecond", TypedValue(4)}, {"microsecond", TypedValue(5)}}); EXPECT_EQ(this->EvaluateFunction("LOCALTIME", map_param).ValueLocalTime(), memgraph::utils::LocalTime({1, 2, 3, 4, 5})); const auto today = memgraph::utils::CurrentLocalTime(); EXPECT_NEAR(this->EvaluateFunction("LOCALTIME").ValueLocalTime().MicrosecondsSinceEpoch(), today.MicrosecondsSinceEpoch(), one_sec_in_microseconds); EXPECT_THROW(this->EvaluateFunction("LOCALTIME", "{}"), memgraph::utils::BasicException); EXPECT_THROW( this->EvaluateFunction("LOCALTIME", TypedValue(std::map{{"hous", TypedValue(1970)}})), QueryRuntimeException); EXPECT_THROW( this->EvaluateFunction("LOCALTIME", TypedValue(std::map{{"minut", TypedValue(1970)}})), QueryRuntimeException); EXPECT_THROW( this->EvaluateFunction("LOCALTIME", TypedValue(std::map{{"seconds", TypedValue(1970)}})), QueryRuntimeException); } TYPED_TEST(FunctionTest, LocalDateTime) { const auto local_date_time = memgraph::utils::LocalDateTime({1970, 1, 1}, {13, 3, 2, 0, 0}); EXPECT_EQ(this->EvaluateFunction("LOCALDATETIME", "1970-01-01T13:03:02").ValueLocalDateTime(), local_date_time); const auto today = memgraph::utils::CurrentLocalDateTime(); const auto one_sec_in_microseconds = 1000000; const auto map_param = TypedValue(std::map{{"year", TypedValue(1972)}, {"month", TypedValue(2)}, {"day", TypedValue(3)}, {"hour", TypedValue(4)}, {"minute", TypedValue(5)}, {"second", TypedValue(6)}, {"millisecond", TypedValue(7)}, {"microsecond", TypedValue(8)}}); EXPECT_EQ(this->EvaluateFunction("LOCALDATETIME", map_param).ValueLocalDateTime(), memgraph::utils::LocalDateTime({1972, 2, 3}, {4, 5, 6, 7, 8})); EXPECT_NEAR(this->EvaluateFunction("LOCALDATETIME").ValueLocalDateTime().MicrosecondsSinceEpoch(), today.MicrosecondsSinceEpoch(), one_sec_in_microseconds); EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME", "{}"), memgraph::utils::BasicException); EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"hours", TypedValue(1970)}})), QueryRuntimeException); EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"seconds", TypedValue(1970)}})), QueryRuntimeException); } TYPED_TEST(FunctionTest, Duration) { const auto map_param = TypedValue(std::map{{"day", TypedValue(3)}, {"hour", TypedValue(4)}, {"minute", TypedValue(5)}, {"second", TypedValue(6)}, {"millisecond", TypedValue(7)}, {"microsecond", TypedValue(8)}}); EXPECT_EQ(this->EvaluateFunction("DURATION", map_param).ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 7, 8})); EXPECT_THROW(this->EvaluateFunction("DURATION", "{}"), memgraph::utils::BasicException); EXPECT_THROW( this->EvaluateFunction("DURATION", TypedValue(std::map{{"hours", TypedValue(1970)}})), QueryRuntimeException); EXPECT_THROW( this->EvaluateFunction("DURATION", TypedValue(std::map{{"seconds", TypedValue(1970)}})), QueryRuntimeException); const auto map_param_negative = TypedValue(std::map{{"day", TypedValue(-3)}, {"hour", TypedValue(-4)}, {"minute", TypedValue(-5)}, {"second", TypedValue(-6)}, {"millisecond", TypedValue(-7)}, {"microsecond", TypedValue(-8)}}); EXPECT_EQ(this->EvaluateFunction("DURATION", map_param_negative).ValueDuration(), memgraph::utils::Duration({-3, -4, -5, -6, -7, -8})); EXPECT_EQ(this->EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(), memgraph::utils::Duration({4, 4, 5, 6, 0, 200000})); EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 0, 100000})); EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 100, 110})); } } // namespace