2017-04-26 22:29:57 +08:00
|
|
|
#include <cmath>
|
2017-03-27 19:09:14 +08:00
|
|
|
#include <iterator>
|
|
|
|
#include <memory>
|
2017-10-02 16:34:38 +08:00
|
|
|
#include <unordered_map>
|
2017-03-27 19:09:14 +08:00
|
|
|
#include <vector>
|
|
|
|
|
2018-10-04 21:23:07 +08:00
|
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <gtest/gtest.h>
|
2017-03-27 19:09:14 +08:00
|
|
|
|
2018-10-05 18:37:23 +08:00
|
|
|
#include "database/single_node/graph_db_accessor.hpp"
|
2018-06-27 17:08:21 +08:00
|
|
|
#include "query/context.hpp"
|
2017-03-27 19:09:14 +08:00
|
|
|
#include "query/frontend/ast/ast.hpp"
|
|
|
|
#include "query/frontend/opencypher/parser.hpp"
|
2017-04-13 16:01:16 +08:00
|
|
|
#include "query/interpret/awesome_memgraph_functions.hpp"
|
|
|
|
#include "query/interpret/eval.hpp"
|
2017-05-05 18:16:04 +08:00
|
|
|
#include "query/interpret/frame.hpp"
|
2017-10-30 17:45:36 +08:00
|
|
|
#include "query/path.hpp"
|
2018-10-04 21:23:07 +08:00
|
|
|
#include "storage/common/types.hpp"
|
2017-09-05 17:26:46 +08:00
|
|
|
#include "utils/string.hpp"
|
2017-07-25 19:01:08 +08:00
|
|
|
|
2017-05-19 21:49:25 +08:00
|
|
|
#include "query_common.hpp"
|
2017-03-27 19:09:14 +08:00
|
|
|
|
|
|
|
using namespace query;
|
2017-08-21 21:44:35 +08:00
|
|
|
using query::test_common::ToList;
|
2017-10-30 17:43:25 +08:00
|
|
|
using testing::ElementsAre;
|
|
|
|
using testing::UnorderedElementsAre;
|
2017-04-24 22:01:00 +08:00
|
|
|
|
|
|
|
namespace {
|
2017-05-20 01:25:54 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
class ExpressionEvaluatorTest : public ::testing::Test {
|
|
|
|
protected:
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
std::unique_ptr<database::GraphDbAccessor> dba{db.Access()};
|
2017-03-27 19:09:14 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2018-09-05 00:02:30 +08:00
|
|
|
EvaluationContext ctx;
|
|
|
|
SymbolTable symbol_table;
|
2017-09-13 23:09:04 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
Frame frame{128};
|
2018-09-11 19:32:25 +08:00
|
|
|
ExpressionEvaluator eval{&frame, symbol_table, ctx, dba.get(),
|
|
|
|
GraphView::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
|
|
|
|
|
|
|
Identifier *CreateIdentifierWithValue(std::string name,
|
|
|
|
const TypedValue &value) {
|
|
|
|
auto id = storage.Create<Identifier>(name, true);
|
|
|
|
auto symbol = symbol_table.CreateSymbol(name, true);
|
|
|
|
symbol_table[*id] = symbol;
|
|
|
|
frame[symbol] = value;
|
|
|
|
return id;
|
|
|
|
}
|
2018-09-05 00:02:30 +08:00
|
|
|
};
|
2017-04-24 22:01:00 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, OrOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true),
|
|
|
|
storage.Create<PrimitiveLiteral>(false));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true),
|
|
|
|
storage.Create<PrimitiveLiteral>(true));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), true);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, XorOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true),
|
|
|
|
storage.Create<PrimitiveLiteral>(false));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true),
|
|
|
|
storage.Create<PrimitiveLiteral>(true));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), false);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, AndOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(true),
|
|
|
|
storage.Create<PrimitiveLiteral>(true));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false),
|
|
|
|
storage.Create<PrimitiveLiteral>(true));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), false);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) {
|
2017-05-08 21:49:02 +08:00
|
|
|
{
|
2017-09-20 21:31:23 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false),
|
|
|
|
storage.Create<PrimitiveLiteral>(5));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueBool(), false);
|
2017-05-08 21:49:02 +08:00
|
|
|
}
|
|
|
|
{
|
2017-11-08 20:13:58 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(5),
|
|
|
|
storage.Create<PrimitiveLiteral>(false));
|
2018-09-05 00:02:30 +08:00
|
|
|
// 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(op->Accept(eval), QueryRuntimeException);
|
2017-11-08 20:13:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, AndOperatorNull) {
|
2017-11-08 20:13:58 +08:00
|
|
|
{
|
|
|
|
// Null doesn't short circuit
|
2017-09-20 21:31:23 +08:00
|
|
|
auto *op = storage.Create<AndOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-05-08 21:49:02 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(5));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-11-08 20:13:58 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto *op = storage.Create<AndOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-11-08 20:13:58 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(true));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-08 21:49:02 +08:00
|
|
|
}
|
2017-11-08 20:13:58 +08:00
|
|
|
{
|
|
|
|
auto *op = storage.Create<AndOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-11-08 20:13:58 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(false));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-11-08 20:13:58 +08:00
|
|
|
ASSERT_TRUE(value.IsBool());
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_EQ(value.ValueBool(), false);
|
2017-11-08 20:13:58 +08:00
|
|
|
}
|
2017-05-08 21:49:02 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, AdditionOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<AdditionOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 5);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, SubtractionOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<SubtractionOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), -1);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<MultiplicationOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 6);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, DivisionOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<DivisionOperator>(storage.Create<PrimitiveLiteral>(50),
|
|
|
|
storage.Create<PrimitiveLiteral>(10));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 5);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, ModOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<ModOperator>(storage.Create<PrimitiveLiteral>(65),
|
|
|
|
storage.Create<PrimitiveLiteral>(10));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 5);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, EqualOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), false);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, NotEqualOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), true);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, LessOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), false);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, GreaterOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), true);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, LessEqualOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), false);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op = storage.Create<GreaterEqualOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(10),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<GreaterEqualOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(15),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), true);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<GreaterEqualOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(20),
|
|
|
|
storage.Create<PrimitiveLiteral>(15));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val3 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val3.ValueBool(), true);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, InListOperator) {
|
2017-05-07 19:48:34 +08:00
|
|
|
auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{
|
|
|
|
storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>("a")});
|
|
|
|
{
|
|
|
|
// Element exists in list.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>(2), list_literal);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueBool(), true);
|
2017-05-07 19:48:34 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Element doesn't exist in list.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>("x"), list_literal);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueBool(), false);
|
2017-05-07 19:48:34 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-05-07 19:48:34 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>("a")});
|
|
|
|
// Element doesn't exist in list with null element.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>("x"), list_literal);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-07 19:48:34 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Null list.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>("x"),
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-07 19:48:34 +08:00
|
|
|
}
|
2017-11-14 21:42:58 +08:00
|
|
|
{
|
|
|
|
// Null literal.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null), list_literal);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-11-14 21:42:58 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Null literal, empty list.
|
|
|
|
auto *op = storage.Create<InListOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-11-14 21:42:58 +08:00
|
|
|
storage.Create<ListLiteral>(std::vector<Expression *>()));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-11-14 21:42:58 +08:00
|
|
|
EXPECT_FALSE(value.ValueBool());
|
|
|
|
}
|
2017-05-07 19:48:34 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, ListIndexing) {
|
2017-05-04 21:16:57 +08:00
|
|
|
auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{
|
|
|
|
storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(3),
|
|
|
|
storage.Create<PrimitiveLiteral>(4)});
|
|
|
|
{
|
|
|
|
// Legal indexing.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-05-04 21:16:57 +08:00
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(2));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueInt(), 3);
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Out of bounds indexing.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-05-04 21:16:57 +08:00
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(4));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Out of bounds indexing with negative bound.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-05-04 21:16:57 +08:00
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(-100));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Legal indexing with negative index.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-05-04 21:16:57 +08:00
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(-2));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueInt(), 3);
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Indexing with one operator being null.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-05-04 21:16:57 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(-2));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Indexing with incompatible type.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>("bla"));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, MapIndexing) {
|
2017-09-02 20:30:05 +08:00
|
|
|
auto *map_literal = storage.Create<MapLiteral>(
|
2018-01-16 17:09:15 +08:00
|
|
|
std::unordered_map<std::pair<std::string, storage::Property>,
|
2018-09-05 00:02:30 +08:00
|
|
|
Expression *>{{std::make_pair("a", dba->Property("a")),
|
|
|
|
storage.Create<PrimitiveLiteral>(1)},
|
|
|
|
{std::make_pair("b", dba->Property("b")),
|
|
|
|
storage.Create<PrimitiveLiteral>(2)},
|
|
|
|
{std::make_pair("c", dba->Property("c")),
|
|
|
|
storage.Create<PrimitiveLiteral>(3)}});
|
2017-09-02 20:30:05 +08:00
|
|
|
{
|
|
|
|
// Legal indexing.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-09-02 20:30:05 +08:00
|
|
|
map_literal, storage.Create<PrimitiveLiteral>("b"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueInt(), 2);
|
2017-09-02 20:30:05 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Legal indexing, non-existing key.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-09-02 20:30:05 +08:00
|
|
|
map_literal, storage.Create<PrimitiveLiteral>("z"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-09-02 20:30:05 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Wrong key type.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
2017-09-02 20:30:05 +08:00
|
|
|
map_literal, storage.Create<PrimitiveLiteral>(42));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-09-02 20:30:05 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Indexing with Null.
|
2018-07-02 16:46:40 +08:00
|
|
|
auto *op = storage.Create<SubscriptOperator>(
|
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
|
|
|
map_literal, storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-09-02 20:30:05 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) {
|
|
|
|
auto edge_type = dba->EdgeType("edge_type");
|
|
|
|
auto prop = dba->Property("prop");
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto e11 = dba->InsertEdge(v1, v1, edge_type);
|
2018-07-02 16:46:40 +08:00
|
|
|
v1.PropsSet(prop, 42);
|
|
|
|
e11.PropsSet(prop, 43);
|
|
|
|
|
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 *vertex_id = CreateIdentifierWithValue("v1", v1);
|
|
|
|
auto *edge_id = CreateIdentifierWithValue("e11", e11);
|
2018-07-02 16:46:40 +08:00
|
|
|
{
|
|
|
|
// Legal indexing.
|
|
|
|
auto *op1 = storage.Create<SubscriptOperator>(
|
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
|
|
|
vertex_id, storage.Create<PrimitiveLiteral>("prop"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value1 = op1->Accept(eval);
|
|
|
|
EXPECT_EQ(value1.ValueInt(), 42);
|
2018-07-02 16:46:40 +08:00
|
|
|
|
|
|
|
auto *op2 = storage.Create<SubscriptOperator>(
|
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
|
|
|
edge_id, storage.Create<PrimitiveLiteral>("prop"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value2 = op2->Accept(eval);
|
|
|
|
EXPECT_EQ(value2.ValueInt(), 43);
|
2018-07-02 16:46:40 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Legal indexing, non-existing key.
|
|
|
|
auto *op1 = storage.Create<SubscriptOperator>(
|
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
|
|
|
vertex_id, storage.Create<PrimitiveLiteral>("blah"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value1 = op1->Accept(eval);
|
2018-07-02 16:46:40 +08:00
|
|
|
EXPECT_TRUE(value1.IsNull());
|
|
|
|
|
|
|
|
auto *op2 = storage.Create<SubscriptOperator>(
|
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
|
|
|
edge_id, storage.Create<PrimitiveLiteral>("blah"));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value2 = op2->Accept(eval);
|
2018-07-02 16:46:40 +08:00
|
|
|
EXPECT_TRUE(value2.IsNull());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Wrong key type.
|
|
|
|
auto *op1 = storage.Create<SubscriptOperator>(
|
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
|
|
|
vertex_id, storage.Create<PrimitiveLiteral>(1));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op1->Accept(eval), QueryRuntimeException);
|
2018-07-02 16:46:40 +08:00
|
|
|
|
|
|
|
auto *op2 = storage.Create<SubscriptOperator>(
|
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
|
|
|
edge_id, storage.Create<PrimitiveLiteral>(1));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op2->Accept(eval), QueryRuntimeException);
|
2018-07-02 16:46:40 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Indexing with Null.
|
|
|
|
auto *op1 = storage.Create<SubscriptOperator>(
|
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
|
|
|
vertex_id, storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value1 = op1->Accept(eval);
|
2018-07-02 16:46:40 +08:00
|
|
|
EXPECT_TRUE(value1.IsNull());
|
|
|
|
|
|
|
|
auto *op2 = storage.Create<SubscriptOperator>(
|
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
|
|
|
edge_id, storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value2 = op2->Accept(eval);
|
2018-07-02 16:46:40 +08:00
|
|
|
EXPECT_TRUE(value2.IsNull());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) {
|
2017-05-04 21:16:57 +08:00
|
|
|
auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{
|
|
|
|
storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(3),
|
|
|
|
storage.Create<PrimitiveLiteral>(4)});
|
|
|
|
|
|
|
|
auto extract_ints = [](TypedValue list) {
|
|
|
|
std::vector<int64_t> int_list;
|
2018-09-05 00:02:30 +08:00
|
|
|
for (auto x : list.ValueList()) {
|
|
|
|
int_list.push_back(x.ValueInt());
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
return int_list;
|
|
|
|
};
|
|
|
|
{
|
|
|
|
// Legal slicing with both bounds defined.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(4));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre(3, 4));
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Legal slicing with negative bound.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(-1));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre(3));
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Lower bound larger than upper bound.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(-4));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre());
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Bounds ouf or range.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(-100),
|
|
|
|
storage.Create<PrimitiveLiteral>(10));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4));
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Lower bound undefined.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, nullptr, storage.Create<PrimitiveLiteral>(3));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3));
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Upper bound undefined.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(-2), nullptr);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_THAT(extract_ints(value), ElementsAre(3, 4));
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Bound of illegal type and null value bound.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
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
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-05-04 21:16:57 +08:00
|
|
|
storage.Create<PrimitiveLiteral>("mirko"));
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// List of illegal type.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
storage.Create<PrimitiveLiteral>("a"),
|
|
|
|
storage.Create<PrimitiveLiteral>(-2), nullptr);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Null value list with undefined upper bound.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null),
|
2017-05-04 21:16:57 +08:00
|
|
|
storage.Create<PrimitiveLiteral>(-2), nullptr);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
;
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Null value index.
|
|
|
|
auto *op = storage.Create<ListSlicingOperator>(
|
|
|
|
list_literal, storage.Create<PrimitiveLiteral>(-2),
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
;
|
2017-05-04 21:16:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, IfOperator) {
|
2017-08-20 19:53:50 +08:00
|
|
|
auto *then_expression = storage.Create<PrimitiveLiteral>(10);
|
|
|
|
auto *else_expression = storage.Create<PrimitiveLiteral>(20);
|
|
|
|
{
|
|
|
|
auto *condition_true =
|
|
|
|
storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(2));
|
|
|
|
auto *op = storage.Create<IfOperator>(condition_true, then_expression,
|
|
|
|
else_expression);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 10);
|
2017-08-20 19:53:50 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto *condition_false =
|
|
|
|
storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(3));
|
|
|
|
auto *op = storage.Create<IfOperator>(condition_false, then_expression,
|
|
|
|
else_expression);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 20);
|
2017-08-20 19:53:50 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto *condition_exception =
|
|
|
|
storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2),
|
|
|
|
storage.Create<PrimitiveLiteral>(3));
|
|
|
|
auto *op = storage.Create<IfOperator>(condition_exception, then_expression,
|
|
|
|
else_expression);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THROW(op->Accept(eval), QueryRuntimeException);
|
2017-08-20 19:53:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, NotOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<NotOperator>(storage.Create<PrimitiveLiteral>(false));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueBool(), true);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<UnaryPlusOperator>(storage.Create<PrimitiveLiteral>(5));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), 5);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<UnaryMinusOperator>(storage.Create<PrimitiveLiteral>(5));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
ASSERT_EQ(value.ValueInt(), -5);
|
2017-03-27 19:09:14 +08:00
|
|
|
}
|
2017-04-10 22:33:53 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, IsNullOperator) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *op =
|
|
|
|
storage.Create<IsNullOperator>(storage.Create<PrimitiveLiteral>(1));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val1 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val1.ValueBool(), false);
|
2017-04-28 18:06:18 +08:00
|
|
|
op = storage.Create<IsNullOperator>(
|
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
|
|
|
storage.Create<PrimitiveLiteral>(PropertyValue::Null));
|
2018-09-05 00:02:30 +08:00
|
|
|
auto val2 = op->Accept(eval);
|
|
|
|
ASSERT_EQ(val2.ValueBool(), true);
|
2017-08-08 19:43:42 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, LabelsTest) {
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.add_label(dba->Label("ANIMAL"));
|
|
|
|
v1.add_label(dba->Label("DOG"));
|
|
|
|
v1.add_label(dba->Label("NICE_DOG"));
|
2017-05-05 18:16:04 +08:00
|
|
|
auto *identifier = storage.Create<Identifier>("n");
|
2018-09-05 00:02:30 +08:00
|
|
|
auto node_symbol = symbol_table.CreateSymbol("n", true);
|
|
|
|
symbol_table[*identifier] = node_symbol;
|
|
|
|
frame[node_symbol] = v1;
|
2017-05-05 18:16:04 +08:00
|
|
|
{
|
|
|
|
auto *op = storage.Create<LabelsTest>(
|
2018-01-12 22:17:04 +08:00
|
|
|
identifier,
|
2018-07-26 15:08:21 +08:00
|
|
|
std::vector<storage::Label>{dba->Label("DOG"), dba->Label("ANIMAL")});
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueBool(), true);
|
2017-05-05 18:16:04 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto *op = storage.Create<LabelsTest>(
|
|
|
|
identifier,
|
2018-07-26 15:08:21 +08:00
|
|
|
std::vector<storage::Label>{dba->Label("DOG"), dba->Label("BAD_DOG"),
|
|
|
|
dba->Label("ANIMAL")});
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
|
|
|
EXPECT_EQ(value.ValueBool(), false);
|
2017-05-05 18:16:04 +08:00
|
|
|
}
|
|
|
|
{
|
2018-09-05 00:02:30 +08:00
|
|
|
frame[node_symbol] = TypedValue::Null;
|
2017-05-05 18:16:04 +08:00
|
|
|
auto *op = storage.Create<LabelsTest>(
|
|
|
|
identifier,
|
2018-07-26 15:08:21 +08:00
|
|
|
std::vector<storage::Label>{dba->Label("DOG"), dba->Label("BAD_DOG"),
|
|
|
|
dba->Label("ANIMAL")});
|
2018-09-05 00:02:30 +08:00
|
|
|
auto value = op->Accept(eval);
|
2017-05-16 16:55:02 +08:00
|
|
|
EXPECT_TRUE(value.IsNull());
|
2017-05-05 18:16:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, Aggregation) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto aggr = storage.Create<Aggregation>(storage.Create<PrimitiveLiteral>(42),
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
nullptr, Aggregation::Op::COUNT);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto aggr_sym = symbol_table.CreateSymbol("aggr", true);
|
|
|
|
symbol_table[*aggr] = aggr_sym;
|
2018-06-27 17:08:21 +08:00
|
|
|
frame[aggr_sym] = TypedValue(1);
|
2017-05-16 16:55:02 +08:00
|
|
|
auto value = aggr->Accept(eval);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_EQ(value.ValueInt(), 1);
|
2017-04-12 18:58:10 +08:00
|
|
|
}
|
2017-04-24 22:01:00 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, ListLiteral) {
|
2017-04-28 18:06:18 +08:00
|
|
|
auto *list_literal = storage.Create<ListLiteral>(
|
|
|
|
std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1),
|
|
|
|
storage.Create<PrimitiveLiteral>("bla"),
|
|
|
|
storage.Create<PrimitiveLiteral>(true)});
|
2018-09-05 00:02:30 +08:00
|
|
|
TypedValue result = list_literal->Accept(eval);
|
|
|
|
ASSERT_TRUE(result.IsList());
|
|
|
|
auto &result_elems = result.ValueList();
|
2017-04-28 18:06:18 +08:00
|
|
|
ASSERT_EQ(3, result_elems.size());
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_TRUE(result_elems[0].IsInt());
|
|
|
|
;
|
|
|
|
EXPECT_TRUE(result_elems[1].IsString());
|
|
|
|
;
|
|
|
|
EXPECT_TRUE(result_elems[2].IsBool());
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, ParameterLookup) {
|
2018-09-11 19:32:25 +08:00
|
|
|
ctx.parameters.Add(0, 42);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto *param_lookup = storage.Create<ParameterLookup>(0);
|
|
|
|
auto value = param_lookup->Accept(eval);
|
|
|
|
ASSERT_TRUE(value.IsInt());
|
|
|
|
EXPECT_EQ(value.ValueInt(), 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, All) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *all =
|
|
|
|
ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*all->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = all->Accept(eval);
|
|
|
|
ASSERT_TRUE(value.IsBool());
|
|
|
|
EXPECT_FALSE(value.ValueBool());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) {
|
|
|
|
AstStorage storage;
|
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 *all = ALL("x", LITERAL(PropertyValue::Null), WHERE(LITERAL(true)));
|
2018-09-05 00:02:30 +08:00
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*all->identifier_] = x_sym;
|
|
|
|
auto value = all->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2)));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*all->identifier_] = x_sym;
|
|
|
|
EXPECT_THROW(all->Accept(eval), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionSingle) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *single =
|
|
|
|
SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*single->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = single->Accept(eval);
|
|
|
|
ASSERT_TRUE(value.IsBool());
|
|
|
|
EXPECT_TRUE(value.ValueBool());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionSingle2) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)),
|
|
|
|
WHERE(GREATER(ident_x, LITERAL(0))));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*single->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = single->Accept(eval);
|
|
|
|
ASSERT_TRUE(value.IsBool());
|
|
|
|
EXPECT_FALSE(value.ValueBool());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) {
|
|
|
|
AstStorage storage;
|
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 *single =
|
|
|
|
SINGLE("x", LITERAL(PropertyValue::Null), WHERE(LITERAL(true)));
|
2018-09-05 00:02:30 +08:00
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*single->identifier_] = x_sym;
|
|
|
|
auto value = single->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionReduce) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_sum = IDENT("sum");
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)),
|
|
|
|
ADD(ident_sum, ident_x));
|
|
|
|
const auto sum_sym = symbol_table.CreateSymbol("sum", true);
|
|
|
|
symbol_table[*reduce->accumulator_] = sum_sym;
|
|
|
|
symbol_table[*ident_sum] = sum_sym;
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*reduce->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = reduce->Accept(eval);
|
|
|
|
ASSERT_TRUE(value.IsInt());
|
|
|
|
EXPECT_EQ(value.ValueInt(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionExtract) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *extract =
|
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
|
|
|
EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(PropertyValue::Null)),
|
2018-09-05 00:02:30 +08:00
|
|
|
ADD(ident_x, LITERAL(1)));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*extract->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = extract->Accept(eval);
|
|
|
|
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());
|
2017-04-28 18:06:18 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionExtractNull) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *extract =
|
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
|
|
|
EXTRACT("x", LITERAL(PropertyValue::Null), ADD(ident_x, LITERAL(1)));
|
2018-09-05 00:02:30 +08:00
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*extract->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
auto value = extract->Accept(eval);
|
|
|
|
EXPECT_TRUE(value.IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorTest, FunctionExtractExceptions) {
|
|
|
|
AstStorage storage;
|
|
|
|
auto *ident_x = IDENT("x");
|
|
|
|
auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1)));
|
|
|
|
const auto x_sym = symbol_table.CreateSymbol("x", true);
|
|
|
|
symbol_table[*extract->identifier_] = x_sym;
|
|
|
|
symbol_table[*ident_x] = x_sym;
|
|
|
|
EXPECT_THROW(extract->Accept(eval), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-10-25 20:51:17 +08:00
|
|
|
TEST_F(ExpressionEvaluatorTest, Coalesce) {
|
|
|
|
// coalesce()
|
|
|
|
EXPECT_THROW(COALESCE()->Accept(eval), QueryRuntimeException);
|
|
|
|
|
|
|
|
// coalesce(null, null)
|
|
|
|
EXPECT_TRUE(COALESCE(LITERAL(TypedValue::Null), LITERAL(TypedValue::Null))
|
|
|
|
->Accept(eval)
|
|
|
|
.IsNull());
|
|
|
|
|
|
|
|
// coalesce(null, 2, 3)
|
|
|
|
EXPECT_EQ(COALESCE(LITERAL(TypedValue::Null), LITERAL(2), LITERAL(3))
|
|
|
|
->Accept(eval)
|
|
|
|
.ValueInt(),
|
|
|
|
2);
|
|
|
|
|
|
|
|
// coalesce(null, 2, assert(false), 3)
|
|
|
|
EXPECT_EQ(COALESCE(LITERAL(TypedValue::Null), LITERAL(2),
|
|
|
|
FN("ASSERT", LITERAL(false)), LITERAL(3))
|
|
|
|
->Accept(eval)
|
|
|
|
.ValueInt(),
|
|
|
|
2);
|
|
|
|
|
|
|
|
// (null, assert(false))
|
|
|
|
EXPECT_THROW(COALESCE(LITERAL(TypedValue::Null), FN("ASSERT", LITERAL(false)))
|
|
|
|
->Accept(eval),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
// coalesce([null, null])
|
|
|
|
EXPECT_FALSE(COALESCE(LITERAL(TypedValue(std::vector<TypedValue>{
|
|
|
|
TypedValue::Null, TypedValue::Null})))
|
|
|
|
->Accept(eval)
|
|
|
|
.IsNull());
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest {
|
|
|
|
protected:
|
|
|
|
std::pair<std::string, storage::Property> prop_age =
|
|
|
|
std::make_pair("age", dba->Property("age"));
|
|
|
|
std::pair<std::string, storage::Property> prop_height =
|
|
|
|
std::make_pair("height", dba->Property("height"));
|
|
|
|
Expression *identifier = storage.Create<Identifier>("element");
|
|
|
|
Symbol symbol = symbol_table.CreateSymbol("element", true);
|
|
|
|
|
|
|
|
void SetUp() { symbol_table[*identifier] = symbol; }
|
|
|
|
|
|
|
|
auto Value(std::pair<std::string, storage::Property> property) {
|
|
|
|
auto *op = storage.Create<PropertyLookup>(identifier, property);
|
|
|
|
return op->Accept(eval);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) {
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.PropsSet(prop_age.second, 10);
|
|
|
|
frame[symbol] = v1;
|
|
|
|
EXPECT_EQ(Value(prop_age).ValueInt(), 10);
|
|
|
|
EXPECT_TRUE(Value(prop_height).IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorPropertyLookup, Edge) {
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("edge_type"));
|
|
|
|
e12.PropsSet(prop_age.second, 10);
|
|
|
|
frame[symbol] = e12;
|
|
|
|
EXPECT_EQ(Value(prop_age).ValueInt(), 10);
|
|
|
|
EXPECT_TRUE(Value(prop_height).IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorPropertyLookup, Null) {
|
|
|
|
frame[symbol] = TypedValue::Null;
|
|
|
|
EXPECT_TRUE(Value(prop_age).IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) {
|
|
|
|
frame[symbol] = std::map<std::string, TypedValue>{{prop_age.first, 10}};
|
|
|
|
EXPECT_EQ(Value(prop_age).ValueInt(), 10);
|
|
|
|
EXPECT_TRUE(Value(prop_height).IsNull());
|
|
|
|
}
|
|
|
|
|
|
|
|
class FunctionTest : public ExpressionEvaluatorTest {
|
|
|
|
protected:
|
2018-10-11 21:04:49 +08:00
|
|
|
std::vector<Expression *> ExpressionsFromTypedValues(
|
|
|
|
const std::vector<TypedValue> &tvs) {
|
2018-09-05 00:02:30 +08:00
|
|
|
std::vector<Expression *> expressions;
|
2018-10-11 21:04:49 +08:00
|
|
|
expressions.reserve(tvs.size());
|
|
|
|
|
|
|
|
for (size_t i = 0; i < tvs.size(); ++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
|
|
|
auto *ident =
|
|
|
|
storage.Create<Identifier>("arg_" + std::to_string(i), true);
|
|
|
|
auto sym = symbol_table.CreateSymbol("arg_" + std::to_string(i), true);
|
|
|
|
symbol_table[*ident] = sym;
|
2018-10-11 21:04:49 +08:00
|
|
|
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);
|
2018-09-05 00:02:30 +08:00
|
|
|
}
|
2018-10-11 21:04:49 +08:00
|
|
|
|
|
|
|
return expressions;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypedValue EvaluateFunctionWithExprs(
|
|
|
|
const std::string &function_name,
|
|
|
|
const std::vector<Expression *> &expressions) {
|
2018-09-05 00:02:30 +08:00
|
|
|
auto *op = storage.Create<Function>(function_name, expressions);
|
|
|
|
return op->Accept(eval);
|
|
|
|
}
|
2018-10-11 21:04:49 +08:00
|
|
|
|
|
|
|
TypedValue EvaluateFunction(const std::string &function_name,
|
|
|
|
const std::vector<TypedValue> &args) {
|
|
|
|
return EvaluateFunctionWithExprs(function_name,
|
|
|
|
ExpressionsFromTypedValues(args));
|
|
|
|
}
|
2018-09-05 00:02:30 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(FunctionTest, EndNode) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ENDNODE", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("ENDNODE", {TypedValue::Null}).IsNull());
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.add_label(dba->Label("label1"));
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
v2.add_label(dba->Label("label2"));
|
|
|
|
auto e = dba->InsertEdge(v1, v2, dba->EdgeType("t"));
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("ENDNODE", {e})
|
2018-09-05 00:02:30 +08:00
|
|
|
.ValueVertex()
|
2018-07-26 15:08:21 +08:00
|
|
|
.has_label(dba->Label("label2")));
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ENDNODE", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Head) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("HEAD", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("HEAD", {TypedValue::Null}).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
std::vector<TypedValue> arguments;
|
|
|
|
arguments.push_back(std::vector<TypedValue>{3, 4, 5});
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("HEAD", arguments).ValueInt(), 3);
|
|
|
|
arguments[0].ValueList().clear();
|
|
|
|
ASSERT_TRUE(EvaluateFunction("HEAD", arguments).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("HEAD", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Properties) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("PROPERTIES", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("PROPERTIES", {TypedValue::Null}).IsNull());
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.PropsSet(dba->Property("height"), 5);
|
|
|
|
v1.PropsSet(dba->Property("age"), 10);
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1"));
|
|
|
|
e.PropsSet(dba->Property("height"), 3);
|
|
|
|
e.PropsSet(dba->Property("age"), 15);
|
2017-04-24 22:01:00 +08:00
|
|
|
|
|
|
|
auto prop_values_to_int = [](TypedValue t) {
|
|
|
|
std::unordered_map<std::string, int> properties;
|
|
|
|
for (auto property : t.Value<std::map<std::string, TypedValue>>()) {
|
2018-09-05 00:02:30 +08:00
|
|
|
properties[property.first] = property.second.ValueInt();
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
return properties;
|
|
|
|
};
|
2017-11-23 23:36:54 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {v1})),
|
|
|
|
UnorderedElementsAre(testing::Pair("height", 5),
|
|
|
|
testing::Pair("age", 10)));
|
|
|
|
ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {e})),
|
|
|
|
UnorderedElementsAre(testing::Pair("height", 3),
|
|
|
|
testing::Pair("age", 15)));
|
|
|
|
ASSERT_THROW(EvaluateFunction("PROPERTIES", {2}), QueryRuntimeException);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Last) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LAST", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("LAST", {TypedValue::Null}).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
std::vector<TypedValue> arguments;
|
|
|
|
arguments.push_back(std::vector<TypedValue>{3, 4, 5});
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("LAST", arguments).ValueInt(), 5);
|
|
|
|
arguments[0].ValueList().clear();
|
|
|
|
ASSERT_TRUE(EvaluateFunction("LAST", arguments).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LAST", {5}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Size) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("SIZE", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("SIZE", {TypedValue::Null}).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
std::vector<TypedValue> arguments;
|
|
|
|
arguments.push_back(std::vector<TypedValue>{3, 4, 5});
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("SIZE", arguments).ValueInt(), 3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("SIZE", {"john"}).ValueInt(), 4);
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("SIZE", {std::map<std::string, TypedValue>{
|
|
|
|
{"a", 5}, {"b", true}, {"c", "123"}}})
|
2018-09-05 00:02:30 +08:00
|
|
|
.ValueInt(),
|
2017-04-24 22:01:00 +08:00
|
|
|
3);
|
|
|
|
ASSERT_THROW(EvaluateFunction("SIZE", {5}), QueryRuntimeException);
|
2017-09-18 20:40:36 +08:00
|
|
|
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v0 = dba->InsertVertex();
|
2017-09-18 20:40:36 +08:00
|
|
|
query::Path path(v0);
|
|
|
|
EXPECT_EQ(EvaluateFunction("SIZE", {path}).ValueInt(), 0);
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
path.Expand(dba->InsertEdge(v0, v1, dba->EdgeType("type")));
|
2017-09-18 20:40:36 +08:00
|
|
|
path.Expand(v1);
|
|
|
|
EXPECT_EQ(EvaluateFunction("SIZE", {path}).ValueInt(), 1);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, StartNode) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("STARTNODE", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("STARTNODE", {TypedValue::Null}).IsNull());
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.add_label(dba->Label("label1"));
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
v2.add_label(dba->Label("label2"));
|
|
|
|
auto e = dba->InsertEdge(v1, v2, dba->EdgeType("t"));
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("STARTNODE", {e})
|
2018-09-05 00:02:30 +08:00
|
|
|
.ValueVertex()
|
2018-07-26 15:08:21 +08:00
|
|
|
.has_label(dba->Label("label1")));
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("STARTNODE", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Degree) {
|
2017-08-09 17:48:36 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("DEGREE", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("DEGREE", {TypedValue::Null}).IsNull());
|
2018-07-26 15:08:21 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto v3 = dba->InsertVertex();
|
|
|
|
auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("t"));
|
|
|
|
dba->InsertEdge(v3, v2, dba->EdgeType("t"));
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("DEGREE", {v1}).ValueInt(), 1);
|
|
|
|
ASSERT_EQ(EvaluateFunction("DEGREE", {v2}).ValueInt(), 2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("DEGREE", {v3}).ValueInt(), 1);
|
2017-08-09 17:48:36 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("DEGREE", {2}), QueryRuntimeException);
|
|
|
|
ASSERT_THROW(EvaluateFunction("DEGREE", {e12}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-10-11 19:39:36 +08:00
|
|
|
TEST_F(FunctionTest, InDegree) {
|
|
|
|
ASSERT_THROW(EvaluateFunction("INDEGREE", {}), QueryRuntimeException);
|
|
|
|
ASSERT_TRUE(EvaluateFunction("INDEGREE", {TypedValue::Null}).IsNull());
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto v3 = dba->InsertVertex();
|
|
|
|
auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("t"));
|
|
|
|
dba->InsertEdge(v3, v2, dba->EdgeType("t"));
|
|
|
|
ASSERT_EQ(EvaluateFunction("INDEGREE", {v1}).ValueInt(), 0);
|
|
|
|
ASSERT_EQ(EvaluateFunction("INDEGREE", {v2}).ValueInt(), 2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("INDEGREE", {v3}).ValueInt(), 0);
|
|
|
|
ASSERT_THROW(EvaluateFunction("INDEGREE", {2}), QueryRuntimeException);
|
|
|
|
ASSERT_THROW(EvaluateFunction("INDEGREE", {e12}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FunctionTest, OutDegree) {
|
|
|
|
ASSERT_THROW(EvaluateFunction("OUTDEGREE", {}), QueryRuntimeException);
|
|
|
|
ASSERT_TRUE(EvaluateFunction("OUTDEGREE", {TypedValue::Null}).IsNull());
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto v3 = dba->InsertVertex();
|
|
|
|
auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("t"));
|
|
|
|
dba->InsertEdge(v3, v2, dba->EdgeType("t"));
|
|
|
|
ASSERT_EQ(EvaluateFunction("OUTDEGREE", {v1}).ValueInt(), 1);
|
|
|
|
ASSERT_EQ(EvaluateFunction("OUTDEGREE", {v2}).ValueInt(), 0);
|
|
|
|
ASSERT_EQ(EvaluateFunction("OUTDEGREE", {v3}).ValueInt(), 1);
|
|
|
|
ASSERT_THROW(EvaluateFunction("OUTDEGREE", {2}), QueryRuntimeException);
|
|
|
|
ASSERT_THROW(EvaluateFunction("OUTDEGREE", {e12}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToBoolean) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TOBOOLEAN", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", {TypedValue::Null}).IsNull());
|
2017-11-22 17:23:33 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {123}).ValueBool(), true);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {-213}).ValueBool(), true);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {0}).ValueBool(), false);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {" trUE \n\t"}).ValueBool(), true);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {"\n\tFalsE"}).ValueBool(), false);
|
|
|
|
ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", {"\n\tFALSEA "}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {true}).ValueBool(), true);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {false}).ValueBool(), false);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToFloat) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TOFLOAT", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("TOFLOAT", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOFLOAT", {" -3.5 \n\t"}).ValueDouble(), -3.5);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOFLOAT", {"\n\t0.5e-1"}).ValueDouble(), 0.05);
|
|
|
|
ASSERT_TRUE(EvaluateFunction("TOFLOAT", {"\n\t3.4e-3X "}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3.5}).ValueDouble(), -3.5);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3}).ValueDouble(), -3.0);
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TOFLOAT", {true}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToInteger) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TOINTEGER", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("TOINTEGER", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {false}).ValueInt(), 0);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {true}).ValueInt(), 1);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {"\n\t3"}).ValueInt(), 3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {" -3.5 \n\t"}).ValueInt(), -3);
|
|
|
|
ASSERT_TRUE(EvaluateFunction("TOINTEGER", {"\n\t3X "}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {-3.5}).ValueInt(), -3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("TOINTEGER", {3.5}).ValueInt(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FunctionTest, Type) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TYPE", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("TYPE", {TypedValue::Null}).IsNull());
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.add_label(dba->Label("label1"));
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
v2.add_label(dba->Label("label2"));
|
|
|
|
auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1"));
|
|
|
|
ASSERT_EQ(EvaluateFunction("TYPE", {e}).ValueString(), "type1");
|
|
|
|
ASSERT_THROW(EvaluateFunction("TYPE", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FunctionTest, Labels) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LABELS", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("LABELS", {TypedValue::Null}).IsNull());
|
|
|
|
auto v = dba->InsertVertex();
|
|
|
|
v.add_label(dba->Label("label1"));
|
|
|
|
v.add_label(dba->Label("label2"));
|
2017-04-24 22:01:00 +08:00
|
|
|
std::vector<std::string> labels;
|
2018-09-05 00:02:30 +08:00
|
|
|
auto _labels = EvaluateFunction("LABELS", {v}).ValueList();
|
2017-04-24 22:01:00 +08:00
|
|
|
for (auto label : _labels) {
|
2018-09-05 00:02:30 +08:00
|
|
|
labels.push_back(label.ValueString());
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
ASSERT_THAT(labels, UnorderedElementsAre("label1", "label2"));
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LABELS", {2}), QueryRuntimeException);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, NodesRelationships) {
|
2017-10-30 17:45:36 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("NODES", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction("NODES", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", {TypedValue::Null}).IsNull());
|
|
|
|
|
|
|
|
{
|
2018-09-05 00:02:30 +08:00
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto v3 = dba->InsertVertex();
|
|
|
|
auto e1 = dba->InsertEdge(v1, v2, dba->EdgeType("Type"));
|
|
|
|
auto e2 = dba->InsertEdge(v2, v3, dba->EdgeType("Type"));
|
2017-10-30 17:45:36 +08:00
|
|
|
query::Path path(v1, e1, v2, e2, v3);
|
|
|
|
|
|
|
|
auto _nodes = EvaluateFunction("NODES", {path}).ValueList();
|
|
|
|
std::vector<VertexAccessor> nodes;
|
|
|
|
for (const auto &node : _nodes) {
|
|
|
|
nodes.push_back(node.ValueVertex());
|
|
|
|
}
|
|
|
|
EXPECT_THAT(nodes, ElementsAre(v1, v2, v3));
|
|
|
|
|
|
|
|
auto _edges = EvaluateFunction("RELATIONSHIPS", {path}).ValueList();
|
|
|
|
std::vector<EdgeAccessor> edges;
|
|
|
|
for (const auto &edge : _edges) {
|
|
|
|
edges.push_back(edge.ValueEdge());
|
|
|
|
}
|
|
|
|
EXPECT_THAT(edges, ElementsAre(e1, e2));
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_THROW(EvaluateFunction("NODES", {2}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Range) {
|
2017-05-10 21:17:43 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("RANGE", {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction("RANGE", {1, 2, TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction("RANGE", {1, TypedValue::Null, 1.3}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("RANGE", {1, 2, 0}), QueryRuntimeException);
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {1, 3})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(1, 2, 3));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {-1, 5, 2})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(-1, 1, 3, 5));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {2, 10, 3})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(2, 5, 8));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {2, 2, 2})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(2));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {3, 0, 5})),
|
|
|
|
ElementsAre());
|
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {5, 1, -2})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(5, 3, 1));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {6, 1, -2})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(6, 4, 2));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {2, 2, -3})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre(2));
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(EvaluateFunction("RANGE", {-2, 4, -1})),
|
2017-05-10 21:17:43 +08:00
|
|
|
ElementsAre());
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Keys) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("KEYS", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("KEYS", {TypedValue::Null}).IsNull());
|
|
|
|
auto v1 = dba->InsertVertex();
|
|
|
|
v1.PropsSet(dba->Property("height"), 5);
|
|
|
|
v1.PropsSet(dba->Property("age"), 10);
|
|
|
|
auto v2 = dba->InsertVertex();
|
|
|
|
auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1"));
|
|
|
|
e.PropsSet(dba->Property("width"), 3);
|
|
|
|
e.PropsSet(dba->Property("age"), 15);
|
2017-04-24 22:01:00 +08:00
|
|
|
|
|
|
|
auto prop_keys_to_string = [](TypedValue t) {
|
|
|
|
std::vector<std::string> keys;
|
2018-09-05 00:02:30 +08:00
|
|
|
for (auto property : t.ValueList()) {
|
|
|
|
keys.push_back(property.ValueString());
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
return keys;
|
|
|
|
};
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {v1})),
|
2017-04-24 22:01:00 +08:00
|
|
|
UnorderedElementsAre("height", "age"));
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {e})),
|
2017-04-24 22:01:00 +08:00
|
|
|
UnorderedElementsAre("width", "age"));
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("KEYS", {2}), QueryRuntimeException);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Tail) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TAIL", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("TAIL", {TypedValue::Null}).IsNull());
|
2017-04-24 22:01:00 +08:00
|
|
|
std::vector<TypedValue> arguments;
|
|
|
|
arguments.push_back(std::vector<TypedValue>{});
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(EvaluateFunction("TAIL", arguments).ValueList().size(), 0U);
|
2017-04-24 22:01:00 +08:00
|
|
|
arguments[0] = std::vector<TypedValue>{3, 4, true, "john"};
|
2018-09-05 00:02:30 +08:00
|
|
|
auto list = EvaluateFunction("TAIL", arguments).ValueList();
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_EQ(list.size(), 3U);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_EQ(list[0].ValueInt(), 4);
|
|
|
|
ASSERT_EQ(list[1].ValueBool(), true);
|
|
|
|
ASSERT_EQ(list[2].ValueString(), "john");
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("TAIL", {2}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Abs) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ABS", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("ABS", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("ABS", {-2}).ValueInt(), 2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ABS", {-2.5}).ValueDouble(), 2.5);
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ABS", {true}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2017-04-26 22:29:57 +08:00
|
|
|
// 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..
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Log) {
|
2017-04-26 22:29:57 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LOG", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("LOG", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {2}).ValueDouble(), log(2));
|
|
|
|
ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {1.5}).ValueDouble(), log(1.5));
|
2017-04-26 22:29:57 +08:00
|
|
|
// Not portable, but should work on most platforms.
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", {-1.5}).ValueDouble()));
|
2017-04-26 22:29:57 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("LOG", {true}), QueryRuntimeException);
|
2017-04-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
// Function Round wraps round from cmath and will work if FunctionTest.Log test
|
2017-04-26 22:29:57 +08:00
|
|
|
// passes. This test is used to show behavior of round since it differs from
|
|
|
|
// neo4j's round.
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Round) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ROUND", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("ROUND", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {-2}).ValueDouble(), -2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {-2.4}).ValueDouble(), -2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {-2.5}).ValueDouble(), -3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {-2.6}).ValueDouble(), -3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {2.4}).ValueDouble(), 2);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {2.5}).ValueDouble(), 3);
|
|
|
|
ASSERT_EQ(EvaluateFunction("ROUND", {2.6}).ValueDouble(), 3);
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ROUND", {true}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2017-04-26 22:29:57 +08:00
|
|
|
// Check if wrapped functions are callable (check if everything was spelled
|
2018-09-05 00:02:30 +08:00
|
|
|
// correctly...). Wrapper correctnes is checked in FunctionTest.Log function
|
|
|
|
// test.
|
|
|
|
TEST_F(FunctionTest, WrappedMathFunctions) {
|
2017-04-26 22:29:57 +08:00
|
|
|
for (auto function_name :
|
|
|
|
{"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN",
|
|
|
|
"ATAN", "COS", "SIN", "TAN"}) {
|
|
|
|
EvaluateFunction(function_name, {0.5});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Atan2) {
|
2017-04-26 22:29:57 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ATAN2", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("ATAN2", {TypedValue::Null, 1}).IsNull());
|
|
|
|
ASSERT_TRUE(EvaluateFunction("ATAN2", {1, TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", {2, -1.0}).ValueDouble(),
|
2017-04-26 22:29:57 +08:00
|
|
|
atan2(2, -1));
|
|
|
|
ASSERT_THROW(EvaluateFunction("ATAN2", {3.0, true}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Sign) {
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("SIGN", {}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_TRUE(EvaluateFunction("SIGN", {TypedValue::Null}).IsNull());
|
|
|
|
ASSERT_EQ(EvaluateFunction("SIGN", {-2}).ValueInt(), -1);
|
|
|
|
ASSERT_EQ(EvaluateFunction("SIGN", {-0.2}).ValueInt(), -1);
|
|
|
|
ASSERT_EQ(EvaluateFunction("SIGN", {0.0}).ValueInt(), 0);
|
|
|
|
ASSERT_EQ(EvaluateFunction("SIGN", {2.5}).ValueInt(), 1);
|
2017-04-24 22:01:00 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("SIGN", {true}), QueryRuntimeException);
|
|
|
|
}
|
2017-04-26 22:29:57 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, E) {
|
2017-04-26 22:29:57 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("E", {1}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_DOUBLE_EQ(EvaluateFunction("E", {}).ValueDouble(), M_E);
|
2017-04-26 22:29:57 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Pi) {
|
2017-04-26 22:29:57 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("PI", {1}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_DOUBLE_EQ(EvaluateFunction("PI", {}).ValueDouble(), M_PI);
|
2017-04-26 22:29:57 +08:00
|
|
|
}
|
2017-05-20 01:25:54 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Rand) {
|
2017-08-22 21:18:29 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("RAND", {1}), QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
ASSERT_GE(EvaluateFunction("RAND", {}).ValueDouble(), 0.0);
|
|
|
|
ASSERT_LT(EvaluateFunction("RAND", {}).ValueDouble(), 1.0);
|
2017-08-22 21:18:29 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, StartsWith) {
|
2017-05-20 01:25:54 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction(kStartsWith, {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kStartsWith, {"a", TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction(kStartsWith, {TypedValue::Null, 1.3}),
|
|
|
|
QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abc", "abc"}).ValueBool());
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abcdef", "abc"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abcdef", "aBc"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abc", "abcd"}).ValueBool());
|
2017-05-20 01:25:54 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, EndsWith) {
|
2017-05-20 01:25:54 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction(kEndsWith, {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kEndsWith, {"a", TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction(kEndsWith, {TypedValue::Null, 1.3}),
|
|
|
|
QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abc", "abc"}).ValueBool());
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abcdef", "def"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kEndsWith, {"abcdef", "dEf"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kEndsWith, {"bcd", "abcd"}).ValueBool());
|
2017-05-20 01:25:54 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Contains) {
|
2017-05-20 01:25:54 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction(kContains, {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kContains, {"a", TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction(kContains, {TypedValue::Null, 1.3}),
|
|
|
|
QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction(kContains, {"abc", "abc"}).ValueBool());
|
|
|
|
EXPECT_TRUE(EvaluateFunction(kContains, {"abcde", "bcd"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kContains, {"cde", "abcdef"}).ValueBool());
|
|
|
|
EXPECT_FALSE(EvaluateFunction(kContains, {"abcdef", "dEf"}).ValueBool());
|
2017-07-25 19:01:08 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Assert) {
|
2017-09-05 17:26:46 +08:00
|
|
|
// Invalid calls.
|
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {}), QueryRuntimeException);
|
2017-09-13 23:09:04 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, false}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {"string", false}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, "reason", true}),
|
|
|
|
QueryRuntimeException);
|
2017-09-05 17:26:46 +08:00
|
|
|
|
|
|
|
// Valid calls, assertion fails.
|
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false}), QueryRuntimeException);
|
2017-09-13 23:09:04 +08:00
|
|
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, "message"}),
|
|
|
|
QueryRuntimeException);
|
2017-09-05 17:26:46 +08:00
|
|
|
try {
|
|
|
|
EvaluateFunction("ASSERT", {false, "bbgba"});
|
|
|
|
} catch (QueryRuntimeException &e) {
|
2018-08-29 17:05:34 +08:00
|
|
|
ASSERT_TRUE(std::string(e.what()).find("bbgba") != std::string::npos);
|
2017-09-05 17:26:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Valid calls, assertion passes.
|
|
|
|
ASSERT_TRUE(EvaluateFunction("ASSERT", {true}).ValueBool());
|
|
|
|
ASSERT_TRUE(EvaluateFunction("ASSERT", {true, "message"}).ValueBool());
|
|
|
|
}
|
2017-09-13 16:27:12 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Counter) {
|
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTER", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTER", {"a", "b"}), QueryRuntimeException);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 0);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 1);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 0);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 2);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 1);
|
2017-09-13 23:09:04 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, CounterSet) {
|
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTERSET", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a"}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", "b"}),
|
2018-06-27 17:08:21 +08:00
|
|
|
QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", 11, 12}),
|
2018-06-27 17:08:21 +08:00
|
|
|
QueryRuntimeException);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 0);
|
|
|
|
EvaluateFunction("COUNTERSET", {"c1", 12});
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 12);
|
|
|
|
EvaluateFunction("COUNTERSET", {"c2", 42});
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 42);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 13);
|
|
|
|
EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 43);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FunctionTest, IndexInfo) {
|
|
|
|
EXPECT_THROW(EvaluateFunction("INDEXINFO", {1}), QueryRuntimeException);
|
|
|
|
EXPECT_EQ(EvaluateFunction("INDEXINFO", {}).ValueList().size(), 0);
|
|
|
|
dba->InsertVertex().add_label(dba->Label("l1"));
|
2017-09-16 20:08:52 +08:00
|
|
|
{
|
2018-09-05 00:02:30 +08:00
|
|
|
auto info = ToList<std::string>(EvaluateFunction("INDEXINFO", {}));
|
2017-09-16 20:08:52 +08:00
|
|
|
EXPECT_EQ(info.size(), 1);
|
|
|
|
EXPECT_EQ(info[0], ":l1");
|
|
|
|
}
|
|
|
|
{
|
2018-10-15 22:12:02 +08:00
|
|
|
dba->BuildIndex(dba->Label("l1"), dba->Property("prop"), false);
|
2018-09-05 00:02:30 +08:00
|
|
|
auto info = ToList<std::string>(EvaluateFunction("INDEXINFO", {}));
|
2017-09-16 20:08:52 +08:00
|
|
|
EXPECT_EQ(info.size(), 2);
|
|
|
|
EXPECT_THAT(info, testing::UnorderedElementsAre(":l1", ":l1(prop)"));
|
|
|
|
}
|
2018-10-15 22:12:02 +08:00
|
|
|
{
|
|
|
|
dba->BuildIndex(dba->Label("l1"), dba->Property("prop1"), true);
|
|
|
|
auto info = ToList<std::string>(EvaluateFunction("INDEXINFO", {}));
|
|
|
|
EXPECT_EQ(info.size(), 3);
|
|
|
|
EXPECT_THAT(info, testing::UnorderedElementsAre(":l1", ":l1(prop)",
|
|
|
|
":l1(prop1) unique"));
|
|
|
|
}
|
2017-09-16 20:08:52 +08:00
|
|
|
}
|
2018-05-23 16:16:54 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Id) {
|
|
|
|
auto va = dba->InsertVertex();
|
|
|
|
auto ea = dba->InsertEdge(va, va, dba->EdgeType("edge"));
|
|
|
|
auto vb = dba->InsertVertex();
|
|
|
|
EXPECT_EQ(EvaluateFunction("ID", {va}).ValueInt(), 0);
|
|
|
|
EXPECT_EQ(EvaluateFunction("ID", {ea}).ValueInt(), 0);
|
2018-10-11 17:37:59 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("ID", {vb}).ValueInt(), 1);
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("ID", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("ID", {0}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("ID", {va, ea}), QueryRuntimeException);
|
2018-05-23 16:16:54 +08:00
|
|
|
}
|
2018-06-12 16:59:11 +08:00
|
|
|
|
2018-10-11 17:37:59 +08:00
|
|
|
/* TODO: FIXME
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, WorkerIdException) {
|
|
|
|
auto va = dba->InsertVertex();
|
|
|
|
EXPECT_THROW(EvaluateFunction("WORKERID", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("WORKERID", {va, va}), QueryRuntimeException);
|
2018-06-12 16:59:11 +08:00
|
|
|
}
|
2018-10-11 17:37:59 +08:00
|
|
|
*/
|
2018-06-12 16:59:11 +08:00
|
|
|
|
2018-10-11 17:37:59 +08:00
|
|
|
/* TODO: FIXME
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, WorkerIdSingleNode) {
|
|
|
|
auto va = dba->InsertVertex();
|
|
|
|
EXPECT_EQ(EvaluateFunction("WORKERID", {va}).ValueInt(), 0);
|
2018-06-12 16:59:11 +08:00
|
|
|
}
|
2018-10-11 17:37:59 +08:00
|
|
|
*/
|
2018-06-15 16:40:51 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringNull) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction("TOSTRING", {TypedValue::Null}).IsNull());
|
2018-06-15 16:40:51 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringString) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {""}).ValueString(), "");
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {"this is a string"}).ValueString(),
|
|
|
|
"this is a string");
|
2018-06-15 16:40:51 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringInteger) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {-23321312}).ValueString(),
|
2018-06-15 16:40:51 +08:00
|
|
|
"-23321312");
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {0}).ValueString(), "0");
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {42}).ValueString(), "42");
|
2018-06-15 16:40:51 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringDouble) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {-42.42}).ValueString(), "-42.420000");
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {0.0}).ValueString(), "0.000000");
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {238910.2313217}).ValueString(),
|
2018-06-15 16:40:51 +08:00
|
|
|
"238910.231322");
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringBool) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {true}).ValueString(), "true");
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOSTRING", {false}).ValueString(), "false");
|
2018-06-15 16:40:51 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToStringExceptions) {
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("TOSTRING", {1, 2, 3}), QueryRuntimeException);
|
2018-06-15 16:40:51 +08:00
|
|
|
std::vector<TypedValue> l{1, 2, 3};
|
2018-06-27 17:08:21 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("TOSTRING", l), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Timestamp) {
|
|
|
|
ctx.timestamp = 42;
|
|
|
|
EXPECT_EQ(EvaluateFunction("TIMESTAMP", {}).ValueInt(), 42);
|
2018-06-27 17:08:21 +08:00
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, TimestampExceptions) {
|
|
|
|
ctx.timestamp = 42;
|
|
|
|
EXPECT_THROW(EvaluateFunction("TIMESTAMP", {1}).ValueInt(),
|
2018-06-27 17:08:21 +08:00
|
|
|
QueryRuntimeException);
|
2018-06-15 16:40:51 +08:00
|
|
|
}
|
2018-06-29 20:58:10 +08:00
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Left) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("LEFT", {}), QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("LEFT", {TypedValue::Null, TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("LEFT", {TypedValue::Null, 10}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction("LEFT", {TypedValue::Null, -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_EQ(EvaluateFunction("LEFT", {"memgraph", 0}).ValueString(), "");
|
|
|
|
EXPECT_EQ(EvaluateFunction("LEFT", {"memgraph", 3}).ValueString(), "mem");
|
|
|
|
EXPECT_EQ(EvaluateFunction("LEFT", {"memgraph", 1000}).ValueString(),
|
|
|
|
"memgraph");
|
|
|
|
EXPECT_THROW(EvaluateFunction("LEFT", {"memgraph", -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("LEFT", {"memgraph", "graph"}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_THROW(EvaluateFunction("LEFT", {132, 10}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Right) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("RIGHT", {}), QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("RIGHT", {TypedValue::Null, TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("RIGHT", {TypedValue::Null, 10}).IsNull());
|
|
|
|
EXPECT_THROW(EvaluateFunction("RIGHT", {TypedValue::Null, -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_EQ(EvaluateFunction("RIGHT", {"memgraph", 0}).ValueString(), "");
|
|
|
|
EXPECT_EQ(EvaluateFunction("RIGHT", {"memgraph", 3}).ValueString(), "aph");
|
|
|
|
EXPECT_EQ(EvaluateFunction("RIGHT", {"memgraph", 1000}).ValueString(),
|
|
|
|
"memgraph");
|
|
|
|
EXPECT_THROW(EvaluateFunction("RIGHT", {"memgraph", -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("RIGHT", {"memgraph", "graph"}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_THROW(EvaluateFunction("RIGHT", {132, 10}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Trimming) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction("LTRIM", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("RTRIM", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("TRIM", {TypedValue::Null}).IsNull());
|
|
|
|
|
|
|
|
EXPECT_EQ(EvaluateFunction("LTRIM", {" abc "}).ValueString(), "abc ");
|
2018-09-05 00:02:30 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("RTRIM", {" abc "}).ValueString(), " abc");
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_EQ(EvaluateFunction("TRIM", {"abc"}).ValueString(), "abc");
|
|
|
|
|
|
|
|
EXPECT_THROW(EvaluateFunction("LTRIM", {"x", "y"}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("RTRIM", {"x", "y"}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("TRIM", {"x", "y"}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Reverse) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_TRUE(EvaluateFunction("REVERSE", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_EQ(EvaluateFunction("REVERSE", {"abc"}).ValueString(), "cba");
|
|
|
|
EXPECT_THROW(EvaluateFunction("REVERSE", {"x", "y"}), QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Replace) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("REPLACE", {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("REPLACE", {TypedValue::Null, "l", "w"}).IsNull());
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("REPLACE", {"hello", TypedValue::Null, "w"}).IsNull());
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("REPLACE", {"hello", "l", TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_EQ(EvaluateFunction("REPLACE", {"hello", "l", "w"}).ValueString(),
|
|
|
|
"hewwo");
|
|
|
|
|
|
|
|
EXPECT_THROW(EvaluateFunction("REPLACE", {1, "l", "w"}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("REPLACE", {"hello", 1, "w"}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("REPLACE", {"hello", "l", 1}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Split) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("SPLIT", {}), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("SPLIT", {"one,two", 1}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("SPLIT", {1, "one,two"}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("SPLIT", {TypedValue::Null, TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("SPLIT", {"one,two", TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_TRUE(EvaluateFunction("SPLIT", {TypedValue::Null, ","}).IsNull());
|
|
|
|
|
|
|
|
auto result = EvaluateFunction("SPLIT", {"one,two", ","});
|
|
|
|
EXPECT_TRUE(result.IsList());
|
|
|
|
EXPECT_EQ(result.ValueList()[0].ValueString(), "one");
|
|
|
|
EXPECT_EQ(result.ValueList()[1].ValueString(), "two");
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, Substring) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("SUBSTRING", {}), QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_TRUE(
|
|
|
|
EvaluateFunction("SUBSTRING", {TypedValue::Null, 0, 10}).IsNull());
|
|
|
|
EXPECT_THROW(
|
|
|
|
EvaluateFunction("SUBSTRING", {TypedValue::Null, TypedValue::Null}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("SUBSTRING", {TypedValue::Null, -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(
|
|
|
|
EvaluateFunction("SUBSTRING", {TypedValue::Null, 0, TypedValue::Null}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
EXPECT_THROW(EvaluateFunction("SUBSTRING", {TypedValue::Null, 0, -10}),
|
|
|
|
QueryRuntimeException);
|
|
|
|
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 2}).ValueString(), "llo");
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 10}).ValueString(), "");
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 2, 0}).ValueString(), "");
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 1, 3}).ValueString(),
|
|
|
|
"ell");
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 1, 4}).ValueString(),
|
|
|
|
"ello");
|
|
|
|
EXPECT_EQ(EvaluateFunction("SUBSTRING", {"hello", 1, 10}).ValueString(),
|
|
|
|
"ello");
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToLower) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("TOLOWER", {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction("TOLOWER", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOLOWER", {"Ab__C"}).ValueString(), "ab__c");
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:02:30 +08:00
|
|
|
TEST_F(FunctionTest, ToUpper) {
|
2018-06-29 20:58:10 +08:00
|
|
|
EXPECT_THROW(EvaluateFunction("TOUPPER", {}), QueryRuntimeException);
|
|
|
|
EXPECT_TRUE(EvaluateFunction("TOUPPER", {TypedValue::Null}).IsNull());
|
|
|
|
EXPECT_EQ(EvaluateFunction("TOUPPER", {"Ab__C"}).ValueString(), "AB__C");
|
|
|
|
}
|
|
|
|
|
2017-09-13 16:27:12 +08:00
|
|
|
} // namespace
|