memgraph/tests/unit/query_expression_evaluator.cpp

2353 lines
110 KiB
C++
Raw Normal View History

2023-02-17 20:09:25 +08:00
// 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 <chrono>
#include <cmath>
#include <iterator>
#include <memory>
#include <unordered_map>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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"
2023-02-17 20:09:25 +08:00
#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"
2022-02-22 20:33:45 +08:00
using namespace memgraph::query;
using memgraph::query::test_common::ToIntList;
using testing::ElementsAre;
using testing::UnorderedElementsAre;
namespace {
template <typename StorageType>
class ExpressionEvaluatorTest : public ::testing::Test {
protected:
const std::string testSuite = "expression_evaluator";
memgraph::storage::Config config;
std::unique_ptr<memgraph::storage::Storage> db;
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba;
memgraph::query::DbAccessor dba;
AstStorage storage;
2022-02-22 20:33:45 +08:00
memgraph::utils::MonotonicBufferResource mem{1024};
EvaluationContext ctx{.memory = &mem, .timestamp = memgraph::query::QueryTimestamp()};
SymbolTable symbol_table;
Frame frame{128};
2022-02-22 20:33:45 +08:00
ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, memgraph::storage::View::OLD};
Clean-up TypedValue misuse Summary: In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future. After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache. Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`. Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
ExpressionEvaluatorTest()
: config(disk_test_utils::GenerateOnDiskConfig(testSuite)),
db(new StorageType(config)),
storage_dba(db->Access()),
dba(storage_dba.get()) {}
~ExpressionEvaluatorTest() {
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
disk_test_utils::RemoveRocksDbDirs(testSuite);
}
}
Identifier *CreateIdentifierWithValue(std::string name, const TypedValue &value) {
auto id = storage.template Create<Identifier>(name, true);
Clean-up TypedValue misuse Summary: In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future. After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache. Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`. Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
auto symbol = symbol_table.CreateSymbol(name, true);
id->MapTo(symbol);
Clean-up TypedValue misuse Summary: In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future. After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache. Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`. Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
frame[symbol] = value;
return id;
}
template <class TExpression>
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<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
using StorageTypes = ::testing::Types<memgraph::storage::DiskStorage>;
TYPED_TEST_CASE(ExpressionEvaluatorTest, StorageTypes);
TYPED_TEST(ExpressionEvaluatorTest, OrOperator) {
auto *op = this->storage.template Create<OrOperator>(this->storage.template Create<PrimitiveLiteral>(true),
this->storage.template Create<PrimitiveLiteral>(false));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<OrOperator>(this->storage.template Create<PrimitiveLiteral>(true),
this->storage.template Create<PrimitiveLiteral>(true));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), true);
}
TYPED_TEST(ExpressionEvaluatorTest, XorOperator) {
auto *op = this->storage.template Create<XorOperator>(this->storage.template Create<PrimitiveLiteral>(true),
this->storage.template Create<PrimitiveLiteral>(false));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<XorOperator>(this->storage.template Create<PrimitiveLiteral>(true),
this->storage.template Create<PrimitiveLiteral>(true));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), false);
}
TYPED_TEST(ExpressionEvaluatorTest, AndOperator) {
auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(true),
this->storage.template Create<PrimitiveLiteral>(true));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(false),
this->storage.template Create<PrimitiveLiteral>(true));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), false);
}
TYPED_TEST(ExpressionEvaluatorTest, AndOperatorShortCircuit) {
{
auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(false),
this->storage.template Create<PrimitiveLiteral>(5));
auto value = this->Eval(op);
EXPECT_EQ(value.ValueBool(), false);
}
{
auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(5),
this->storage.template Create<PrimitiveLiteral>(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<AndOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(5));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
}
{
auto *op = this->storage.template Create<AndOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(true));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
auto *op = this->storage.template Create<AndOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(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<AdditionOperator>(this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(3));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 5);
}
TYPED_TEST(ExpressionEvaluatorTest, SubtractionOperator) {
auto *op = this->storage.template Create<SubtractionOperator>(this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(3));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), -1);
}
TYPED_TEST(ExpressionEvaluatorTest, MultiplicationOperator) {
auto *op = this->storage.template Create<MultiplicationOperator>(this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(3));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 6);
}
TYPED_TEST(ExpressionEvaluatorTest, DivisionOperator) {
auto *op = this->storage.template Create<DivisionOperator>(this->storage.template Create<PrimitiveLiteral>(50),
this->storage.template Create<PrimitiveLiteral>(10));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 5);
}
TYPED_TEST(ExpressionEvaluatorTest, ModOperator) {
auto *op = this->storage.template Create<ModOperator>(this->storage.template Create<PrimitiveLiteral>(65),
this->storage.template Create<PrimitiveLiteral>(10));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 5);
}
TYPED_TEST(ExpressionEvaluatorTest, EqualOperator) {
auto *op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), false);
op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), true);
op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), false);
}
TYPED_TEST(ExpressionEvaluatorTest, NotEqualOperator) {
auto *op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), false);
op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), true);
}
TYPED_TEST(ExpressionEvaluatorTest, LessOperator) {
auto *op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), false);
op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), false);
}
TYPED_TEST(ExpressionEvaluatorTest, GreaterOperator) {
auto *op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), false);
op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), false);
op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), true);
}
TYPED_TEST(ExpressionEvaluatorTest, LessEqualOperator) {
auto *op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), true);
op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), true);
op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), false);
}
TYPED_TEST(ExpressionEvaluatorTest, GreaterEqualOperator) {
auto *op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
this->storage.template Create<PrimitiveLiteral>(15));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), false);
op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
this->storage.template Create<PrimitiveLiteral>(15));
auto val2 = this->Eval(op);
ASSERT_EQ(val2.ValueBool(), true);
op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
this->storage.template Create<PrimitiveLiteral>(15));
auto val3 = this->Eval(op);
ASSERT_EQ(val3.ValueBool(), true);
}
TYPED_TEST(ExpressionEvaluatorTest, InListOperator) {
auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>("a")});
{
// Element exists in list.
auto *op =
this->storage.template Create<InListOperator>(this->storage.template Create<PrimitiveLiteral>(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<InListOperator>(this->storage.template Create<PrimitiveLiteral>("x"),
list_literal);
auto value = this->Eval(op);
EXPECT_EQ(value.ValueBool(), false);
}
{
auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>("a")});
// Element doesn't exist in list with null element.
auto *op = this->storage.template Create<InListOperator>(this->storage.template Create<PrimitiveLiteral>("x"),
list_literal);
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Null list.
auto *op = this->storage.template Create<InListOperator>(
this->storage.template Create<PrimitiveLiteral>("x"),
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Null literal.
auto *op = this->storage.template Create<InListOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()), list_literal);
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Null literal, empty list.
auto *op = this->storage.template Create<InListOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<ListLiteral>(std::vector<Expression *>()));
auto value = this->Eval(op);
EXPECT_FALSE(value.ValueBool());
}
}
TYPED_TEST(ExpressionEvaluatorTest, ListIndexing) {
auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(3), this->storage.template Create<PrimitiveLiteral>(4)});
{
// Legal indexing.
auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(2));
auto value = this->Eval(op);
EXPECT_EQ(value.ValueInt(), 3);
}
{
// Out of bounds indexing.
auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(4));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Out of bounds indexing with negative bound.
auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(-100));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Legal indexing with negative index.
auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(-2));
auto value = this->Eval(op);
EXPECT_EQ(value.ValueInt(), 3);
}
{
// Indexing with one operator being null.
auto *op = this->storage.template Create<SubscriptOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(-2));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Indexing with incompatible type.
auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>("bla"));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
}
}
TYPED_TEST(ExpressionEvaluatorTest, MapIndexing) {
auto *map_literal = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("a"), this->storage.template Create<PrimitiveLiteral>(1)},
{this->storage.GetPropertyIx("b"), this->storage.template Create<PrimitiveLiteral>(2)},
{this->storage.GetPropertyIx("c"), this->storage.template Create<PrimitiveLiteral>(3)}});
{
// Legal indexing.
auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
this->storage.template Create<PrimitiveLiteral>("b"));
auto value = this->Eval(op);
EXPECT_EQ(value.ValueInt(), 2);
}
{
// Legal indexing, non-existing key.
auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
this->storage.template Create<PrimitiveLiteral>("z"));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
{
// Wrong key type.
auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
this->storage.template Create<PrimitiveLiteral>(42));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
}
{
// Indexing with Null.
auto *op = this->storage.template Create<SubscriptOperator>(
map_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
}
}
TYPED_TEST(ExpressionEvaluatorTest, MapProjectionIndexing) {
auto *map_variable = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
map_variable, std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("a"), this->storage.template Create<PrimitiveLiteral>(1)},
{this->storage.GetPropertyIx("y"), this->storage.template Create<PropertyLookup>(
map_variable, this->storage.GetPropertyIx("y"))}});
2023-05-17 02:05:35 +08:00
{
// Legal indexing.
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>("a"));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
EXPECT_EQ(value.ValueInt(), 1);
}
{
// Legal indexing; property created by PropertyLookup of a non-existent map variable key
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>("y"));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(value.IsNull());
}
{
// Legal indexing, non-existing property.
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>("z"));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(value.IsNull());
}
{
// Wrong key type.
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>(42));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
2023-05-17 02:05:35 +08:00
}
{
// Indexing with Null.
auto *op = this->storage.template Create<SubscriptOperator>(
map_projection_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(value.IsNull());
}
}
TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupBefore) {
2023-05-17 02:05:35 +08:00
// 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<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
map_variable,
std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("*"), this->storage.template Create<AllPropertiesLookup>(map_variable)},
{this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(1)}});
2023-05-17 02:05:35 +08:00
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>("x"));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
EXPECT_EQ(value.ValueInt(), 1);
}
TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupAfter) {
2023-05-17 02:05:35 +08:00
// 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<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
map_variable,
std::unordered_map<PropertyIx, Expression *>{
{this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(1)},
{this->storage.GetPropertyIx("*"), this->storage.template Create<AllPropertiesLookup>(map_variable)}});
2023-05-17 02:05:35 +08:00
auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
this->storage.template Create<PrimitiveLiteral>("x"));
auto value = this->Eval(op);
2023-05-17 02:05:35 +08:00
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());
2022-02-22 20:33:45 +08:00
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<SubscriptOperator>(
vertex_id, this->storage.template Create<PrimitiveLiteral>("prop"));
auto value1 = this->Eval(op1);
EXPECT_EQ(value1.ValueInt(), 42);
auto *op2 = this->storage.template Create<SubscriptOperator>(
edge_id, this->storage.template Create<PrimitiveLiteral>("prop"));
auto value2 = this->Eval(op2);
EXPECT_EQ(value2.ValueInt(), 43);
}
{
// Legal indexing, non-existing key.
auto *op1 = this->storage.template Create<SubscriptOperator>(
vertex_id, this->storage.template Create<PrimitiveLiteral>("blah"));
auto value1 = this->Eval(op1);
EXPECT_TRUE(value1.IsNull());
auto *op2 = this->storage.template Create<SubscriptOperator>(
edge_id, this->storage.template Create<PrimitiveLiteral>("blah"));
auto value2 = this->Eval(op2);
EXPECT_TRUE(value2.IsNull());
}
{
// Wrong key type.
auto *op1 =
this->storage.template Create<SubscriptOperator>(vertex_id, this->storage.template Create<PrimitiveLiteral>(1));
EXPECT_THROW(this->Eval(op1), QueryRuntimeException);
auto *op2 =
this->storage.template Create<SubscriptOperator>(edge_id, this->storage.template Create<PrimitiveLiteral>(1));
EXPECT_THROW(this->Eval(op2), QueryRuntimeException);
}
{
// Indexing with Null.
auto *op1 = this->storage.template Create<SubscriptOperator>(
vertex_id, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value1 = this->Eval(op1);
EXPECT_TRUE(value1.IsNull());
auto *op2 = this->storage.template Create<SubscriptOperator>(
edge_id, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value2 = this->Eval(op2);
EXPECT_TRUE(value2.IsNull());
}
}
TYPED_TEST(ExpressionEvaluatorTest, TypedValueListIndexing) {
auto list_vector = memgraph::utils::pmr::vector<TypedValue>(this->ctx.memory);
2023-02-17 20:09:25 +08:00
list_vector.emplace_back("string1");
list_vector.emplace_back(TypedValue("string2"));
auto *identifier = this->storage.template Create<Identifier>("n");
auto node_symbol = this->symbol_table.CreateSymbol("n", true);
2023-02-17 20:09:25 +08:00
identifier->MapTo(node_symbol);
this->frame[node_symbol] = TypedValue(list_vector, this->ctx.memory);
2023-02-17 20:09:25 +08:00
{
// Legal indexing.
auto *op = this->storage.template Create<SubscriptOperator>(identifier,
this->storage.template Create<PrimitiveLiteral>(0));
auto value = this->Eval(op);
2023-02-17 20:09:25 +08:00
EXPECT_EQ(value.ValueString(), "string1");
}
{
// Out of bounds indexing
auto *op = this->storage.template Create<SubscriptOperator>(identifier,
this->storage.template Create<PrimitiveLiteral>(3));
auto value = this->Eval(op);
2023-02-17 20:09:25 +08:00
EXPECT_TRUE(value.IsNull());
}
{
// Out of bounds indexing with negative bound.
auto *op = this->storage.template Create<SubscriptOperator>(identifier,
this->storage.template Create<PrimitiveLiteral>(-100));
auto value = this->Eval(op);
2023-02-17 20:09:25 +08:00
EXPECT_TRUE(value.IsNull());
}
{
// Legal indexing with negative index.
auto *op = this->storage.template Create<SubscriptOperator>(identifier,
this->storage.template Create<PrimitiveLiteral>(-2));
auto value = this->Eval(op);
2023-02-17 20:09:25 +08:00
EXPECT_EQ(value.ValueString(), "string1");
}
{
// Indexing with incompatible type.
auto *op = this->storage.template Create<SubscriptOperator>(identifier,
this->storage.template Create<PrimitiveLiteral>("bla"));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
2023-02-17 20:09:25 +08:00
}
}
TYPED_TEST(ExpressionEvaluatorTest, ListSlicingOperator) {
auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(3), this->storage.template Create<PrimitiveLiteral>(4)});
auto extract_ints = [](TypedValue list) {
std::vector<int64_t> 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<ListSlicingOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(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<ListSlicingOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(-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<ListSlicingOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(2),
this->storage.template Create<PrimitiveLiteral>(-4));
auto value = this->Eval(op);
EXPECT_THAT(extract_ints(value), ElementsAre());
}
{
// Bounds ouf or range.
auto *op = this->storage.template Create<ListSlicingOperator>(list_literal,
this->storage.template Create<PrimitiveLiteral>(-100),
this->storage.template Create<PrimitiveLiteral>(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<ListSlicingOperator>(list_literal, nullptr,
this->storage.template Create<PrimitiveLiteral>(3));
auto value = this->Eval(op);
EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3));
}
{
// Upper bound undefined.
auto *op = this->storage.template Create<ListSlicingOperator>(
list_literal, this->storage.template Create<PrimitiveLiteral>(-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<ListSlicingOperator>(
list_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>("mirko"));
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
}
{
// List of illegal type.
auto *op = this->storage.template Create<ListSlicingOperator>(this->storage.template Create<PrimitiveLiteral>("a"),
this->storage.template Create<PrimitiveLiteral>(-2),
nullptr);
EXPECT_THROW(this->Eval(op), QueryRuntimeException);
}
{
// Null value list with undefined upper bound.
auto *op = this->storage.template Create<ListSlicingOperator>(
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
this->storage.template Create<PrimitiveLiteral>(-2), nullptr);
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
;
}
{
// Null value index.
auto *op = this->storage.template Create<ListSlicingOperator>(
list_literal, this->storage.template Create<PrimitiveLiteral>(-2),
this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
auto value = this->Eval(op);
EXPECT_TRUE(value.IsNull());
;
}
}
TYPED_TEST(ExpressionEvaluatorTest, IfOperator) {
auto *then_expression = this->storage.template Create<PrimitiveLiteral>(10);
auto *else_expression = this->storage.template Create<PrimitiveLiteral>(20);
{
auto *condition_true = this->storage.template Create<EqualOperator>(
this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(2));
auto *op = this->storage.template Create<IfOperator>(condition_true, then_expression, else_expression);
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 10);
}
{
auto *condition_false = this->storage.template Create<EqualOperator>(
this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(3));
auto *op = this->storage.template Create<IfOperator>(condition_false, then_expression, else_expression);
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 20);
}
{
auto *condition_exception = this->storage.template Create<AdditionOperator>(
this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(3));
auto *op = this->storage.template Create<IfOperator>(condition_exception, then_expression, else_expression);
ASSERT_THROW(this->Eval(op), QueryRuntimeException);
}
}
TYPED_TEST(ExpressionEvaluatorTest, NotOperator) {
auto *op = this->storage.template Create<NotOperator>(this->storage.template Create<PrimitiveLiteral>(false));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueBool(), true);
}
TYPED_TEST(ExpressionEvaluatorTest, UnaryPlusOperator) {
auto *op = this->storage.template Create<UnaryPlusOperator>(this->storage.template Create<PrimitiveLiteral>(5));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), 5);
}
TYPED_TEST(ExpressionEvaluatorTest, UnaryMinusOperator) {
auto *op = this->storage.template Create<UnaryMinusOperator>(this->storage.template Create<PrimitiveLiteral>(5));
auto value = this->Eval(op);
ASSERT_EQ(value.ValueInt(), -5);
}
TYPED_TEST(ExpressionEvaluatorTest, IsNullOperator) {
auto *op = this->storage.template Create<IsNullOperator>(this->storage.template Create<PrimitiveLiteral>(1));
auto val1 = this->Eval(op);
ASSERT_EQ(val1.ValueBool(), false);
op = this->storage.template Create<IsNullOperator>(
this->storage.template Create<PrimitiveLiteral>(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<Identifier>("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<LabelsTest>(
identifier, std::vector<LabelIx>{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("ANIMAL")});
auto value = this->Eval(op);
EXPECT_EQ(value.ValueBool(), true);
}
{
auto *op = this->storage.template Create<LabelsTest>(
identifier, std::vector<LabelIx>{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<LabelsTest>(
identifier, std::vector<LabelIx>{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<Aggregation>(this->storage.template Create<PrimitiveLiteral>(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<ListLiteral>(std::vector<Expression *>{
this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>("bla"),
this->storage.template Create<PrimitiveLiteral>(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<ParameterLookup>(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;
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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;
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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;
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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;
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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 =
2022-02-22 20:33:45 +08:00
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");
2022-02-22 20:33:45 +08:00
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(), TypedValue()})))).IsNull());
}
TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidArguments) {
EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL(TypedValue()), LITERAL("regex"))).IsNull());
EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL(3), LITERAL("regex"))).IsNull());
EXPECT_TRUE(
this->Eval(this->storage.template Create<RegexMatch>(LIST(LITERAL("string")), LITERAL("regex"))).IsNull());
EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LITERAL(TypedValue()))).IsNull());
EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LITERAL(42))),
QueryRuntimeException);
EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LIST(LITERAL("regex")))),
QueryRuntimeException);
}
TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidRegex) {
EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("*ext"))),
QueryRuntimeException);
EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("[ext"))),
QueryRuntimeException);
}
TYPED_TEST(ExpressionEvaluatorTest, RegexMatch) {
EXPECT_FALSE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".*ex"))).ValueBool());
EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".*ext"))).ValueBool());
EXPECT_FALSE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("[ext]"))).ValueBool());
EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool());
}
template <typename StorageType>
class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest<StorageType> {
protected:
std::pair<std::string, memgraph::storage::PropertyId> prop_age =
std::make_pair("age", this->dba.NameToProperty("age"));
2022-02-22 20:33:45 +08:00
std::pair<std::string, memgraph::storage::PropertyId> prop_height =
std::make_pair("height", this->dba.NameToProperty("height"));
Identifier *identifier = this->storage.template Create<Identifier>("element");
Symbol symbol = this->symbol_table.CreateSymbol("element", true);
void SetUp() { identifier->MapTo(symbol); }
2022-02-22 20:33:45 +08:00
auto Value(std::pair<std::string, memgraph::storage::PropertyId> property) {
auto *op = this->storage.template Create<PropertyLookup>(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) {
2022-02-22 20:33:45 +08:00
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) {
2022-02-22 20:33:45 +08:00
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) {
2022-02-22 20:33:45 +08:00
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) {
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{this->prop_age.first, TypedValue(10)}});
EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10);
EXPECT_TRUE(this->Value(this->prop_height).IsNull());
}
template <typename StorageType>
class ExpressionEvaluatorAllPropertiesLookup : public ExpressionEvaluatorTest<StorageType> {
2023-05-17 02:05:35 +08:00
protected:
std::pair<std::string, memgraph::storage::PropertyId> prop_age =
std::make_pair("age", this->dba.NameToProperty("age"));
2023-05-17 02:05:35 +08:00
std::pair<std::string, memgraph::storage::PropertyId> prop_height =
std::make_pair("height", this->dba.NameToProperty("height"));
Identifier *identifier = this->storage.template Create<Identifier>("element");
Symbol symbol = this->symbol_table.CreateSymbol("element", true);
2023-05-17 02:05:35 +08:00
void SetUp() { identifier->MapTo(symbol); }
auto Value() {
auto *op = this->storage.template Create<AllPropertiesLookup>(identifier);
return this->Eval(op);
2023-05-17 02:05:35 +08:00
}
};
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();
2023-05-17 02:05:35 +08:00
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"));
2023-05-17 02:05:35 +08:00
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();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Duration) {
2023-05-17 02:05:35 +08:00
const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45});
this->frame[this->symbol] = TypedValue(dur);
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Date) {
2023-05-17 02:05:35 +08:00
const memgraph::utils::Date date({1996, 11, 22});
this->frame[this->symbol] = TypedValue(date);
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalTime) {
2023-05-17 02:05:35 +08:00
const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22});
this->frame[this->symbol] = TypedValue(lt);
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalDateTime) {
2023-05-17 02:05:35 +08:00
const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40});
this->frame[this->symbol] = TypedValue(ldt);
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Null) {
this->frame[this->symbol] = TypedValue();
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsNull());
}
TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Map) {
this->frame[this->symbol] = TypedValue(std::map<std::string, TypedValue>{{this->prop_age.first, TypedValue(10)}});
auto all_properties = this->Value();
2023-05-17 02:05:35 +08:00
EXPECT_TRUE(all_properties.IsMap());
}
template <typename StorageType>
class FunctionTest : public ExpressionEvaluatorTest<StorageType> {
protected:
std::vector<Expression *> ExpressionsFromTypedValues(const std::vector<TypedValue> &tvs) {
std::vector<Expression *> expressions;
expressions.reserve(tvs.size());
for (size_t i = 0; i < tvs.size(); ++i) {
auto *ident = this->storage.template Create<Identifier>("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];
Clean-up TypedValue misuse Summary: In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future. After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache. Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`. Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
expressions.push_back(ident);
}
return expressions;
}
TypedValue EvaluateFunctionWithExprs(const std::string &function_name, const std::vector<Expression *> &expressions) {
auto *op = this->storage.template Create<Function>(function_name, expressions);
return this->Eval(op);
}
template <class... TArgs>
TypedValue EvaluateFunction(const std::string &function_name, std::tuple<TArgs...> args) {
std::vector<TypedValue> 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 <class... TArgs>
TypedValue EvaluateFunction(const std::string &function_name, TArgs &&...args) {
return EvaluateFunctionWithExprs(function_name,
ExpressionsFromTypedValues(std::vector<TypedValue>{TypedValue(args)...}));
}
};
TYPED_TEST_CASE(FunctionTest, StorageTypes);
template <class... TArgs>
static TypedValue MakeTypedValueList(TArgs &&...args) {
return TypedValue(std::vector<TypedValue>{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<std::string, int> 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<std::string, TypedValue>{
{"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}})
.ValueInt(),
3);
ASSERT_THROW(this->EvaluateFunction("SIZE", 5), QueryRuntimeException);
auto v0 = this->dba.InsertVertex();
2022-02-22 20:33:45 +08:00
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)
2022-02-22 20:33:45 +08:00
.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>{TypedValue(1), TypedValue(2)}))
.ValueString(),
"LIST");
ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(std::map<std::string, TypedValue>{{"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<std::string> 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());
2022-02-22 20:33:45 +08:00
memgraph::query::Path path{v1, *e1, v2, *e2, v3};
this->dba.AdvanceCommand();
auto _nodes = this->EvaluateFunction("NODES", path).ValueList();
2022-02-22 20:33:45 +08:00
std::vector<memgraph::query::VertexAccessor> 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();
2022-02-22 20:33:45 +08:00
std::vector<memgraph::query::EdgeAccessor> 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<std::string> 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;
2022-02-22 20:33:45 +08:00
const memgraph::utils::LocalTime time(10000);
EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000);
}
TYPED_TEST(FunctionTest, TimestampLocalDateTime) {
this->ctx.timestamp = 42;
2022-02-22 20:33:45 +08:00
const memgraph::utils::LocalDateTime time(20000);
EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000);
}
TYPED_TEST(FunctionTest, TimestampDuration) {
this->ctx.timestamp = 42;
2022-02-22 20:33:45 +08:00
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(),
2022-02-22 20:33:45 +08:00
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) {
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{"year", TypedValue(1970)}, {"month", TypedValue(1)}, {"day", TypedValue(1)}});
EXPECT_EQ(this->EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch);
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{"years", TypedValue(1970)}}),
QueryRuntimeException);
EXPECT_THROW(this->EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"mnths", TypedValue(1970)}}),
QueryRuntimeException);
EXPECT_THROW(this->EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"dayz", TypedValue(1970)}}),
QueryRuntimeException);
}
TYPED_TEST(FunctionTest, LocalTime) {
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{"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}));
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{"hous", TypedValue(1970)}})),
QueryRuntimeException);
EXPECT_THROW(
this->EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"minut", TypedValue(1970)}})),
QueryRuntimeException);
EXPECT_THROW(
this->EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
QueryRuntimeException);
}
TYPED_TEST(FunctionTest, LocalDateTime) {
2022-02-22 20:33:45 +08:00
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);
2022-02-22 20:33:45 +08:00
const auto today = memgraph::utils::CurrentLocalDateTime();
const auto one_sec_in_microseconds = 1000000;
const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"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(),
2022-02-22 20:33:45 +08:00
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<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
QueryRuntimeException);
EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME",
TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
QueryRuntimeException);
}
TYPED_TEST(FunctionTest, Duration) {
const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"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<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
QueryRuntimeException);
EXPECT_THROW(
this->EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
QueryRuntimeException);
2021-09-20 22:53:42 +08:00
const auto map_param_negative = TypedValue(std::map<std::string, TypedValue>{{"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(),
2022-02-22 20:33:45 +08:00
memgraph::utils::Duration({-3, -4, -5, -6, -7, -8}));
EXPECT_EQ(this->EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(),
2022-02-22 20:33:45 +08:00
memgraph::utils::Duration({4, 4, 5, 6, 0, 200000}));
EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(),
2022-02-22 20:33:45 +08:00
memgraph::utils::Duration({3, 4, 5, 6, 0, 100000}));
EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(),
2022-02-22 20:33:45 +08:00
memgraph::utils::Duration({3, 4, 5, 6, 100, 110}));
}
} // namespace