6fc6a27288
Summary: GraphDb is refactored to become an API exposing different parts necessary for the database to function. These different parts can have different implementations in SingleNode or distributed Master/Server GraphDb implementations. Interally GraphDb is implemented using two class heirarchies. One contains all the members and correct wiring for each situation. The other takes care of initialization and shutdown. This architecture is practical because it can guarantee that the initialization of the object structure is complete, before initializing state. Reviewers: buda, mislav.bradac, dgleich, teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1093
259 lines
8.1 KiB
C++
259 lines
8.1 KiB
C++
#include "bolt_common.hpp"
|
|
#include "bolt_testdata.hpp"
|
|
|
|
#include "communication/bolt/v1/encoder/encoder.hpp"
|
|
#include "database/graph_db.hpp"
|
|
#include "database/graph_db_accessor.hpp"
|
|
#include "query/typed_value.hpp"
|
|
|
|
using query::TypedValue;
|
|
|
|
/**
|
|
* TODO (mferencevic): document
|
|
*/
|
|
|
|
constexpr const int SIZE = 131072;
|
|
uint8_t data[SIZE];
|
|
|
|
uint64_t GetBigEndianInt(std::vector<uint8_t> &v, uint8_t len,
|
|
uint8_t offset = 1) {
|
|
uint64_t ret = 0;
|
|
v.erase(v.begin(), v.begin() + offset);
|
|
for (int i = 0; i < len; ++i) {
|
|
ret <<= 8;
|
|
ret += v[i];
|
|
}
|
|
v.erase(v.begin(), v.begin() + len);
|
|
return ret;
|
|
}
|
|
|
|
void CheckTypeSize(std::vector<uint8_t> &v, int typ, uint64_t size) {
|
|
uint64_t len;
|
|
if ((v[0] & 0xF0) == type_tiny_magic[typ]) {
|
|
len = v[0] & 0x0F;
|
|
v.erase(v.begin(), v.begin() + 1);
|
|
} else if (v[0] == type_8_magic[typ]) {
|
|
len = GetBigEndianInt(v, 1);
|
|
} else if (v[0] == type_16_magic[typ]) {
|
|
len = GetBigEndianInt(v, 2);
|
|
} else if (v[0] == type_32_magic[typ]) {
|
|
len = GetBigEndianInt(v, 4);
|
|
} else {
|
|
FAIL() << "Got wrong marker!";
|
|
}
|
|
ASSERT_EQ(len, size);
|
|
}
|
|
|
|
void CheckInt(std::vector<uint8_t> &output, int64_t value) {
|
|
TestSocket test_socket(20);
|
|
TestBuffer encoder_buffer(test_socket);
|
|
communication::bolt::BaseEncoder<TestBuffer> bolt_encoder(encoder_buffer);
|
|
std::vector<uint8_t> &encoded = test_socket.output;
|
|
bolt_encoder.WriteInt(value);
|
|
CheckOutput(output, encoded.data(), encoded.size(), false);
|
|
}
|
|
|
|
void CheckRecordHeader(std::vector<uint8_t> &v, uint64_t size) {
|
|
CheckOutput(v, (const uint8_t *)"\xB1\x71", 2, false);
|
|
CheckTypeSize(v, LIST, size);
|
|
}
|
|
|
|
TestSocket test_socket(10);
|
|
TestBuffer encoder_buffer(test_socket);
|
|
communication::bolt::Encoder<TestBuffer> bolt_encoder(encoder_buffer);
|
|
std::vector<uint8_t> &output = test_socket.output;
|
|
|
|
TEST(BoltEncoder, NullAndBool) {
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
vals.push_back(TypedValue::Null);
|
|
vals.push_back(TypedValue(true));
|
|
vals.push_back(TypedValue(false));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, 3);
|
|
CheckOutput(output, (const uint8_t *)"\xC0\xC3\xC2", 3);
|
|
}
|
|
|
|
TEST(BoltEncoder, Int) {
|
|
int N = 28;
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
for (int i = 0; i < N; ++i) vals.push_back(TypedValue(int_decoded[i]));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, N);
|
|
for (int i = 0; i < N; ++i)
|
|
CheckOutput(output, int_encoded[i], int_encoded_len[i], false);
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST(BoltEncoder, Double) {
|
|
int N = 4;
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
for (int i = 0; i < N; ++i) vals.push_back(TypedValue(double_decoded[i]));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, N);
|
|
for (int i = 0; i < N; ++i) CheckOutput(output, double_encoded[i], 9, false);
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST(BoltEncoder, String) {
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
for (uint64_t i = 0; i < sizes_num; ++i)
|
|
vals.push_back(TypedValue(std::string((const char *)data, sizes[i])));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, STRING, sizes[i]);
|
|
CheckOutput(output, data, sizes[i], false);
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST(BoltEncoder, List) {
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
std::vector<TypedValue> val;
|
|
for (uint64_t j = 0; j < sizes[i]; ++j)
|
|
val.push_back(TypedValue(std::string((const char *)&data[j], 1)));
|
|
vals.push_back(TypedValue(val));
|
|
}
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, LIST, sizes[i]);
|
|
for (uint64_t j = 0; j < sizes[i]; ++j) {
|
|
CheckTypeSize(output, STRING, 1);
|
|
CheckOutput(output, &data[j], 1, false);
|
|
}
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST(BoltEncoder, Map) {
|
|
output.clear();
|
|
std::vector<TypedValue> vals;
|
|
uint8_t buff[10];
|
|
for (int i = 0; i < sizes_num; ++i) {
|
|
std::map<std::string, TypedValue> val;
|
|
for (int j = 0; j < sizes[i]; ++j) {
|
|
sprintf((char *)buff, "%05X", j);
|
|
std::string tmp((char *)buff, 5);
|
|
val.insert(std::make_pair(tmp, TypedValue(tmp)));
|
|
}
|
|
vals.push_back(TypedValue(val));
|
|
}
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (int i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, MAP, sizes[i]);
|
|
for (int j = 0; j < sizes[i]; ++j) {
|
|
sprintf((char *)buff, "%05X", j);
|
|
CheckTypeSize(output, STRING, 5);
|
|
CheckOutput(output, buff, 5, false);
|
|
CheckTypeSize(output, STRING, 5);
|
|
CheckOutput(output, buff, 5, false);
|
|
}
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST(BoltEncoder, VertexAndEdge) {
|
|
output.clear();
|
|
|
|
// create vertex
|
|
database::SingleNode db;
|
|
database::GraphDbAccessor db_accessor(db);
|
|
auto va1 = db_accessor.InsertVertex();
|
|
auto va2 = db_accessor.InsertVertex();
|
|
auto l1 = db_accessor.Label("label1");
|
|
auto l2 = db_accessor.Label("label2");
|
|
va1.add_label(l1);
|
|
va1.add_label(l2);
|
|
auto p1 = db_accessor.Property("prop1");
|
|
auto p2 = db_accessor.Property("prop2");
|
|
PropertyValue pv1(12), pv2(200);
|
|
va1.PropsSet(p1, pv1);
|
|
va1.PropsSet(p2, pv2);
|
|
|
|
// create edge
|
|
auto et = db_accessor.EdgeType("edgetype");
|
|
auto ea = db_accessor.InsertEdge(va1, va2, et);
|
|
auto p3 = db_accessor.Property("prop3");
|
|
auto p4 = db_accessor.Property("prop4");
|
|
PropertyValue pv3(42), pv4(1234);
|
|
ea.PropsSet(p3, pv3);
|
|
ea.PropsSet(p4, pv4);
|
|
|
|
// check everything
|
|
std::vector<TypedValue> vals;
|
|
vals.push_back(TypedValue(va1));
|
|
vals.push_back(TypedValue(va2));
|
|
vals.push_back(TypedValue(ea));
|
|
bolt_encoder.MessageRecord(vals);
|
|
|
|
// The vertexedge_encoded testdata has hardcoded zeros for IDs,
|
|
// and Memgraph now encodes IDs so we need to check the output
|
|
// part by part.
|
|
CheckOutput(output, vertexedge_encoded, 5, false);
|
|
CheckInt(output, va1.gid());
|
|
CheckOutput(output, vertexedge_encoded + 6, 34, false);
|
|
CheckInt(output, va2.gid());
|
|
CheckOutput(output, vertexedge_encoded + 41, 4, false);
|
|
CheckInt(output, ea.gid());
|
|
CheckInt(output, va1.gid());
|
|
CheckInt(output, va2.gid());
|
|
CheckOutput(output, vertexedge_encoded + 48, 26);
|
|
}
|
|
|
|
TEST(BoltEncoder, BoltV1ExampleMessages) {
|
|
// this test checks example messages from: http://boltprotocol.org/v1/
|
|
|
|
output.clear();
|
|
|
|
// record message
|
|
std::vector<TypedValue> rvals;
|
|
for (int i = 1; i < 4; ++i) rvals.push_back(TypedValue(i));
|
|
bolt_encoder.MessageRecord(rvals);
|
|
CheckOutput(output, (const uint8_t *)"\xB1\x71\x93\x01\x02\x03", 6);
|
|
|
|
// success message
|
|
std::string sv1("name"), sv2("age"), sk("fields");
|
|
std::vector<TypedValue> svec;
|
|
svec.push_back(TypedValue(sv1));
|
|
svec.push_back(TypedValue(sv2));
|
|
TypedValue slist(svec);
|
|
std::map<std::string, TypedValue> svals;
|
|
svals.insert(std::make_pair(sk, slist));
|
|
bolt_encoder.MessageSuccess(svals);
|
|
CheckOutput(output,
|
|
(const uint8_t *) "\xB1\x70\xA1\x86\x66\x69\x65\x6C\x64\x73\x92\x84\x6E\x61\x6D\x65\x83\x61\x67\x65",
|
|
20);
|
|
|
|
// failure message
|
|
std::string fv1("Neo.ClientError.Statement.SyntaxError"),
|
|
fv2("Invalid syntax.");
|
|
std::string fk1("code"), fk2("message");
|
|
TypedValue ftv1(fv1), ftv2(fv2);
|
|
std::map<std::string, TypedValue> fvals;
|
|
fvals.insert(std::make_pair(fk1, ftv1));
|
|
fvals.insert(std::make_pair(fk2, ftv2));
|
|
bolt_encoder.MessageFailure(fvals);
|
|
CheckOutput(output,
|
|
(const uint8_t *) "\xB1\x7F\xA2\x84\x63\x6F\x64\x65\xD0\x25\x4E\x65\x6F\x2E\x43\x6C\x69\x65\x6E\x74\x45\x72\x72\x6F\x72\x2E\x53\x74\x61\x74\x65\x6D\x65\x6E\x74\x2E\x53\x79\x6E\x74\x61\x78\x45\x72\x72\x6F\x72\x87\x6D\x65\x73\x73\x61\x67\x65\x8F\x49\x6E\x76\x61\x6C\x69\x64\x20\x73\x79\x6E\x74\x61\x78\x2E",
|
|
71);
|
|
|
|
// ignored message
|
|
bolt_encoder.MessageIgnored();
|
|
CheckOutput(output, (const uint8_t *)"\xB0\x7E", 2);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
InitializeData(data, SIZE);
|
|
google::InitGoogleLogging(argv[0]);
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|