First version of bolt cpp client.

Reviewers: buda, mislav.bradac

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D689
This commit is contained in:
Matej Ferencevic 2017-08-24 15:23:33 +02:00
parent a6b8d6b4cf
commit c507e74384
17 changed files with 1422 additions and 313 deletions

View File

@ -266,6 +266,7 @@ target_link_libraries(antlr_opencypher_parser_lib antlr4_static)
# all memgraph src files # all memgraph src files
set(memgraph_src_files set(memgraph_src_files
${src_dir}/communication/bolt/v1/decoder/decoded_value.cpp
${src_dir}/data_structures/concurrent/skiplist_gc.cpp ${src_dir}/data_structures/concurrent/skiplist_gc.cpp
${src_dir}/database/graph_db.cpp ${src_dir}/database/graph_db.cpp
${src_dir}/database/graph_db_accessor.cpp ${src_dir}/database/graph_db_accessor.cpp

View File

@ -0,0 +1,279 @@
#pragma once
#include <glog/logging.h>
#include "communication/bolt/v1/decoder/buffer.hpp"
#include "communication/bolt/v1/decoder/chunked_decoder_buffer.hpp"
#include "communication/bolt/v1/decoder/decoder.hpp"
#include "communication/bolt/v1/encoder/chunked_encoder_buffer.hpp"
#include "communication/bolt/v1/encoder/client_encoder.hpp"
#include "query/typed_value.hpp"
#include "utils/exceptions.hpp"
namespace communication::bolt {
class ClientException : public utils::BasicException {
using utils::BasicException::BasicException;
};
class ClientSocketException : public ClientException {
public:
using ClientException::ClientException;
ClientSocketException()
: ClientException("Couldn't write/read data to/from the socket!") {}
};
class ClientInvalidDataException : public ClientException {
public:
using ClientException::ClientException;
ClientInvalidDataException()
: ClientException("The server sent invalid data!") {}
};
class ClientQueryException : public ClientException {
public:
using ClientException::ClientException;
ClientQueryException() : ClientException("Couldn't execute query!") {}
};
struct QueryData {
std::vector<std::string> fields;
std::vector<std::vector<DecodedValue>> records;
std::map<std::string, DecodedValue> metadata;
};
template <typename Socket>
class Client {
public:
Client(Socket &&socket, std::string &username, std::string &password,
std::string client_name = "")
: socket_(std::move(socket)) {
DLOG(INFO) << "Sending handshake";
if (!socket_.Write(kPreamble, sizeof(kPreamble))) {
throw ClientSocketException();
}
for (int i = 0; i < 4; ++i) {
if (!socket_.Write(kProtocol, sizeof(kProtocol))) {
throw ClientSocketException();
}
}
DLOG(INFO) << "Reading handshake response";
if (!GetDataByLen(4)) {
throw ClientSocketException();
}
if (memcmp(kProtocol, buffer_.data(), sizeof(kProtocol)) != 0) {
throw ClientException("Server negotiated unsupported protocol version!");
}
buffer_.Shift(sizeof(kProtocol));
if (client_name == "") {
client_name = "memgraph-bolt/0.0.1";
}
DLOG(INFO) << "Sending init message";
if (!encoder_.MessageInit(client_name, {{"scheme", "basic"},
{"principal", username},
{"secret", password}})) {
throw ClientSocketException();
}
DLOG(INFO) << "Reading init message response";
if (!GetDataByChunk()) {
throw ClientSocketException();
}
Signature signature;
DecodedValue metadata;
if (!ReadMessage(&signature, &metadata)) {
throw ClientException("Couldn't read init message response!");
}
if (signature != Signature::Success) {
throw ClientInvalidDataException();
}
}
QueryData Execute(const std::string &query,
const std::map<std::string, DecodedValue> &parameters) {
DLOG(INFO) << "Sending run message with statement: '" << query
<< "'; parameters: " << parameters;
std::map<std::string, query::TypedValue> params_tv(parameters.begin(),
parameters.end());
encoder_.MessageRun(query, params_tv, false);
encoder_.MessagePullAll();
DLOG(INFO) << "Reading run message response";
if (!GetDataByChunk()) {
throw ClientSocketException();
}
Signature signature;
DecodedValue fields;
if (!ReadMessage(&signature, &fields)) {
throw ClientInvalidDataException();
}
if (fields.type() != DecodedValue::Type::Map) {
throw ClientInvalidDataException();
}
if (signature == Signature::Failure) {
HandleFailure();
auto &tmp = fields.ValueMap();
auto it = tmp.find("message");
if (it != tmp.end()) {
throw ClientQueryException(it->second.ValueString());
}
throw ClientQueryException();
} else if (signature != Signature::Success) {
throw ClientInvalidDataException();
}
DLOG(INFO) << "Reading pull_all message response";
Marker marker;
DecodedValue metadata;
std::vector<std::vector<DecodedValue>> records;
while (true) {
if (!GetDataByChunk()) {
throw ClientInvalidDataException();
}
if (!decoder_.ReadMessageHeader(&signature, &marker)) {
throw ClientInvalidDataException();
}
if (signature == Signature::Record) {
DecodedValue record;
if (!decoder_.ReadValue(&record, DecodedValue::Type::List)) {
throw ClientInvalidDataException();
}
records.push_back(record.ValueList());
} else if (signature == Signature::Success) {
if (!decoder_.ReadValue(&metadata)) {
throw ClientInvalidDataException();
}
break;
} else if (signature == Signature::Failure) {
DecodedValue data;
if (!decoder_.ReadValue(&data)) {
throw ClientInvalidDataException();
}
HandleFailure();
auto &tmp = data.ValueMap();
auto it = tmp.find("message");
if (it != tmp.end()) {
throw ClientQueryException(it->second.ValueString());
}
throw ClientQueryException();
} else {
throw ClientInvalidDataException();
}
}
if (metadata.type() != DecodedValue::Type::Map) {
throw ClientInvalidDataException();
}
QueryData ret{{}, records, metadata.ValueMap()};
auto &header = fields.ValueMap();
if (header.find("fields") == header.end()) {
throw ClientInvalidDataException();
}
if (header["fields"].type() != DecodedValue::Type::List) {
throw ClientInvalidDataException();
}
auto &field_vector = header["fields"].ValueList();
for (auto &field_item : field_vector) {
if (field_item.type() != DecodedValue::Type::String) {
throw ClientInvalidDataException();
}
ret.fields.push_back(field_item.ValueString());
}
return ret;
}
void Close() { socket_.Close(); };
~Client() { Close(); }
private:
bool GetDataByLen(uint64_t len) {
while (buffer_.size() < len) {
auto buff = buffer_.Allocate();
int ret = socket_.Read(buff.data, buff.len);
if (ret == -1) return false;
buffer_.Written(ret);
}
return true;
}
bool GetDataByChunk() {
// If there is more data in the buffer then don't read data.
if (decoder_buffer_.Size() > 0) return true;
ChunkState state;
while ((state = decoder_buffer_.GetChunk()) == ChunkState::Partial) {
auto buff = buffer_.Allocate();
int ret = socket_.Read(buff.data, buff.len);
if (ret == -1) return false;
buffer_.Written(ret);
}
if (state == ChunkState::Whole) {
return true;
}
return false;
}
bool ReadMessage(Signature *signature, DecodedValue *ret) {
Marker marker;
if (!decoder_.ReadMessageHeader(signature, &marker)) {
return false;
}
return ReadMessageData(marker, ret);
}
bool ReadMessageData(Marker marker, DecodedValue *ret) {
if (marker == Marker::TinyStruct) {
*ret = DecodedValue();
return true;
} else if (marker == Marker::TinyStruct1) {
return decoder_.ReadValue(ret);
}
return false;
}
void HandleFailure() {
if (!encoder_.MessageAckFailure()) {
throw ClientSocketException();
}
while (true) {
Signature signature;
DecodedValue data;
if (!GetDataByChunk()) {
throw ClientInvalidDataException();
}
if (!ReadMessage(&signature, &data)) {
throw ClientInvalidDataException();
}
if (signature == Signature::Success) {
break;
} else if (signature != Signature::Ignored) {
throw ClientInvalidDataException();
}
}
}
// socket
Socket socket_;
// decoder objects
Buffer<> buffer_;
ChunkedDecoderBuffer decoder_buffer_{buffer_};
Decoder<ChunkedDecoderBuffer> decoder_{decoder_buffer_};
// encoder objects
ChunkedEncoderBuffer<Socket> encoder_buffer_{socket_};
ClientEncoder<ChunkedEncoderBuffer<Socket>> encoder_{encoder_buffer_};
};
}

View File

@ -5,6 +5,9 @@
namespace communication::bolt { namespace communication::bolt {
static constexpr uint8_t kPreamble[4] = {0x60, 0x60, 0xB0, 0x17};
static constexpr uint8_t kProtocol[4] = {0x00, 0x00, 0x00, 0x01};
enum class Signature : uint8_t { enum class Signature : uint8_t {
Init = 0x01, Init = 0x01,
AckFailure = 0x0E, AckFailure = 0x0E,
@ -39,6 +42,9 @@ enum class Marker : uint8_t {
// marker == Marker::TinyStruct1 // marker == Marker::TinyStruct1
TinyStruct1 = 0xB1, TinyStruct1 = 0xB1,
TinyStruct2 = 0xB2, TinyStruct2 = 0xB2,
TinyStruct3 = 0xB3,
TinyStruct4 = 0xB4,
TinyStruct5 = 0xB5,
Null = 0xC0, Null = 0xC0,
Float64 = 0xC1, Float64 = 0xC1,

View File

@ -61,6 +61,22 @@ class ChunkedDecoderBuffer {
return true; return true;
} }
/**
* Peeks data from the internal buffer.
* Reads data, but doesn't remove it from the buffer.
*
* @param data a pointer to where the data should be read
* @param len the length of data that should be read
* @param offset offset from the beginning of the data
* @returns true if exactly len of data was copied into data,
* false otherwise
*/
bool Peek(uint8_t *data, size_t len, size_t offset = 0) {
if (len + offset > size_ - pos_) return false;
memcpy(data, &data_[pos_ + offset], len);
return true;
}
/** /**
* Gets a chunk from the underlying raw data buffer. * Gets a chunk from the underlying raw data buffer.
* When getting a chunk this function validates the chunk. * When getting a chunk this function validates the chunk.

View File

@ -0,0 +1,298 @@
#include "communication/bolt/v1/decoder/decoded_value.hpp"
namespace communication::bolt {
bool DecodedValue::ValueBool() const {
if (type_ != Type::Bool) {
throw DecodedValueException();
}
return bool_v;
}
int64_t DecodedValue::ValueInt() const {
if (type_ != Type::Int) {
throw DecodedValueException();
}
return int_v;
}
double DecodedValue::ValueDouble() const {
if (type_ != Type::Double) {
throw DecodedValueException();
}
return double_v;
}
const std::string &DecodedValue::ValueString() const {
if (type_ != Type::String) {
throw DecodedValueException();
}
return string_v;
}
const std::vector<DecodedValue> &DecodedValue::ValueList() const {
if (type_ != Type::List) {
throw DecodedValueException();
}
return list_v;
}
const std::map<std::string, DecodedValue> &DecodedValue::ValueMap() const {
if (type_ != Type::Map) {
throw DecodedValueException();
}
return map_v;
}
const DecodedVertex &DecodedValue::ValueVertex() const {
if (type_ != Type::Vertex) {
throw DecodedValueException();
}
return vertex_v;
}
const DecodedEdge &DecodedValue::ValueEdge() const {
if (type_ != Type::Edge) {
throw DecodedValueException();
}
return edge_v;
}
bool &DecodedValue::ValueBool() {
if (type_ != Type::Bool) {
throw DecodedValueException();
}
return bool_v;
}
int64_t &DecodedValue::ValueInt() {
if (type_ != Type::Int) {
throw DecodedValueException();
}
return int_v;
}
double &DecodedValue::ValueDouble() {
if (type_ != Type::Double) {
throw DecodedValueException();
}
return double_v;
}
std::string &DecodedValue::ValueString() {
if (type_ != Type::String) {
throw DecodedValueException();
}
return string_v;
}
std::vector<DecodedValue> &DecodedValue::ValueList() {
if (type_ != Type::List) {
throw DecodedValueException();
}
return list_v;
}
std::map<std::string, DecodedValue> &DecodedValue::ValueMap() {
if (type_ != Type::Map) {
throw DecodedValueException();
}
return map_v;
}
DecodedVertex &DecodedValue::ValueVertex() {
if (type_ != Type::Vertex) {
throw DecodedValueException();
}
return vertex_v;
}
DecodedEdge &DecodedValue::ValueEdge() {
if (type_ != Type::Edge) {
throw DecodedValueException();
}
return edge_v;
}
DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
switch (other.type_) {
case Type::Null:
return;
case Type::Bool:
this->bool_v = other.bool_v;
return;
case Type::Int:
this->int_v = other.int_v;
return;
case Type::Double:
this->double_v = other.double_v;
return;
case Type::String:
new (&string_v) std::string(other.string_v);
return;
case Type::List:
new (&list_v) std::vector<DecodedValue>(other.list_v);
return;
case Type::Map:
new (&map_v) std::map<std::string, DecodedValue>(other.map_v);
return;
case Type::Vertex:
new (&vertex_v) DecodedVertex(other.vertex_v);
return;
case Type::Edge:
new (&edge_v) DecodedEdge(other.edge_v);
return;
}
permanent_fail("Unsupported DecodedValue::Type");
}
DecodedValue &DecodedValue::operator=(const DecodedValue &other) {
if (this != &other) {
this->~DecodedValue();
// set the type of this
type_ = other.type_;
switch (other.type_) {
case Type::Null:
return *this;
case Type::Bool:
this->bool_v = other.bool_v;
return *this;
case Type::Int:
this->int_v = other.int_v;
return *this;
case Type::Double:
this->double_v = other.double_v;
return *this;
case Type::String:
new (&string_v) std::string(other.string_v);
return *this;
case Type::List:
new (&list_v) std::vector<DecodedValue>(other.list_v);
return *this;
case Type::Map:
new (&map_v) std::map<std::string, DecodedValue>(other.map_v);
return *this;
case Type::Vertex:
new (&vertex_v) DecodedVertex(other.vertex_v);
return *this;
case Type::Edge:
new (&edge_v) DecodedEdge(other.edge_v);
return *this;
}
permanent_fail("Unsupported DecodedValue::Type");
}
return *this;
}
DecodedValue::~DecodedValue() {
switch (type_) {
// destructor for primitive types does nothing
case Type::Null:
case Type::Bool:
case Type::Int:
case Type::Double:
return;
// we need to call destructors for non primitive types since we used
// placement new
case Type::String:
// Clang fails to compile ~std::string. It seems it is a bug in some
// versions of clang. using namespace std statement solves the issue.
using namespace std;
string_v.~string();
return;
case Type::List:
using namespace std;
list_v.~vector<DecodedValue>();
return;
case Type::Map:
using namespace std;
map_v.~map<std::string, DecodedValue>();
return;
case Type::Vertex:
vertex_v.~DecodedVertex();
return;
case Type::Edge:
edge_v.~DecodedEdge();
return;
}
permanent_fail("Unsupported DecodedValue::Type");
}
DecodedValue::operator query::TypedValue() const {
switch (type_) {
case Type::Null:
return query::TypedValue::Null;
case Type::Bool:
return query::TypedValue(bool_v);
case Type::Int:
return query::TypedValue(int_v);
case Type::Double:
return query::TypedValue(double_v);
case Type::String:
return query::TypedValue(string_v);
case Type::List:
return query::TypedValue(
std::vector<query::TypedValue>(list_v.begin(), list_v.end()));
case Type::Map:
return query::TypedValue(
std::map<std::string, query::TypedValue>(map_v.begin(), map_v.end()));
default:
throw DecodedValueException(
"Unsupported conversion from DecodedValue to TypedValue");
}
}
std::ostream &operator<<(std::ostream &os, const DecodedVertex &vertex) {
os << "V(";
PrintIterable(os, vertex.labels, ":",
[&](auto &stream, auto label) { stream << label; });
os << " {";
PrintIterable(os, vertex.properties, ", ",
[&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "})";
}
std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge) {
os << "E[" << edge.type;
os << " {";
PrintIterable(os, edge.properties, ", ", [&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}]";
}
std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
switch (value.type_) {
case DecodedValue::Type::Null:
return os << "Null";
case DecodedValue::Type::Bool:
return os << (value.ValueBool() ? "true" : "false");
case DecodedValue::Type::Int:
return os << value.ValueInt();
case DecodedValue::Type::Double:
return os << value.ValueDouble();
case DecodedValue::Type::String:
return os << value.ValueString();
case DecodedValue::Type::List:
os << "[";
PrintIterable(os, value.ValueList());
return os << "]";
case DecodedValue::Type::Map:
os << "{";
PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}";
case DecodedValue::Type::Vertex:
return os << value.ValueVertex();
case DecodedValue::Type::Edge:
return os << value.ValueEdge();
}
permanent_fail("Unsupported DecodedValue::Type");
}
}

View File

@ -0,0 +1,153 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include "query/typed_value.hpp"
#include "storage/property_value.hpp"
#include "utils/algorithm.hpp"
#include "utils/assert.hpp"
#include "utils/exceptions.hpp"
namespace communication::bolt {
/** Forward declaration of DecodedValue class. */
class DecodedValue;
/**
* Structure used when reading a Vertex with the decoder.
* The decoder writes data into this structure.
*/
struct DecodedVertex {
int64_t id;
std::vector<std::string> labels;
std::map<std::string, DecodedValue> properties;
};
/**
* Structure used when reading an Edge with the decoder.
* The decoder writes data into this structure.
*/
struct DecodedEdge {
int64_t id;
int64_t from;
int64_t to;
std::string type;
std::map<std::string, DecodedValue> properties;
};
/**
* DecodedValue provides an encapsulation arround TypedValue, DecodedVertex
* and DecodedEdge. This is necessary because TypedValue stores vertices and
* edges as our internal accessors. Because of that the Bolt decoder can't
* decode vertices and edges directly into a TypedValue so a DecodedValue is
* used instead.
*/
class DecodedValue {
public:
/** Default constructor, makes Null */
DecodedValue() : type_(Type::Null) {}
/** Types that can be stored in a DecodedValue. */
// TODO: Path isn't supported yet!
enum class Type : unsigned {
Null,
Bool,
Int,
Double,
String,
List,
Map,
Vertex,
Edge
};
// constructors for primitive types
DecodedValue(bool value) : type_(Type::Bool) { bool_v = value; }
DecodedValue(int value) : type_(Type::Int) { int_v = value; }
DecodedValue(int64_t value) : type_(Type::Int) { int_v = value; }
DecodedValue(double value) : type_(Type::Double) { double_v = value; }
// constructors for non-primitive types
DecodedValue(const std::string &value) : type_(Type::String) {
new (&string_v) std::string(value);
}
DecodedValue(const std::vector<DecodedValue> &value) : type_(Type::List) {
new (&list_v) std::vector<DecodedValue>(value);
}
DecodedValue(const std::map<std::string, DecodedValue> &value)
: type_(Type::Map) {
new (&map_v) std::map<std::string, DecodedValue>(value);
}
DecodedValue(const DecodedVertex &value) : type_(Type::Vertex) {
new (&vertex_v) DecodedVertex(value);
}
DecodedValue(const DecodedEdge &value) : type_(Type::Edge) {
new (&edge_v) DecodedEdge(value);
}
DecodedValue &operator=(const DecodedValue &other);
DecodedValue(const DecodedValue &other);
~DecodedValue();
Type type() const { return type_; }
// constant getters
bool ValueBool() const;
int64_t ValueInt() const;
double ValueDouble() const;
const std::string &ValueString() const;
const std::vector<DecodedValue> &ValueList() const;
const std::map<std::string, DecodedValue> &ValueMap() const;
const DecodedVertex &ValueVertex() const;
const DecodedEdge &ValueEdge() const;
// getters
bool &ValueBool();
int64_t &ValueInt();
double &ValueDouble();
std::string &ValueString();
std::vector<DecodedValue> &ValueList();
std::map<std::string, DecodedValue> &ValueMap();
DecodedVertex &ValueVertex();
DecodedEdge &ValueEdge();
// conversion function to TypedValue
operator query::TypedValue() const;
friend std::ostream &operator<<(std::ostream &os, const DecodedValue &value);
private:
Type type_;
// storage for the value of the property
union {
bool bool_v;
int64_t int_v;
double double_v;
std::string string_v;
std::vector<DecodedValue> list_v;
std::map<std::string, DecodedValue> map_v;
DecodedVertex vertex_v;
DecodedEdge edge_v;
};
};
/**
* An exception raised by the DecodedValue system.
*/
class DecodedValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
DecodedValueException()
: BasicException("Incompatible template param and type!") {}
};
/**
* Output operators.
*/
std::ostream &operator<<(std::ostream &os, const DecodedVertex &vertex);
std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge);
std::ostream &operator<<(std::ostream &os, const DecodedValue &value);
}

View File

@ -5,40 +5,16 @@
#include <glog/logging.h> #include <glog/logging.h>
#include "communication/bolt/v1/codes.hpp" #include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/decoder/decoded_value.hpp"
#include "database/graph_db_accessor.hpp" #include "database/graph_db_accessor.hpp"
#include "query/typed_value.hpp"
#include "utils/bswap.hpp" #include "utils/bswap.hpp"
#include "utils/underlying_cast.hpp" #include "utils/underlying_cast.hpp"
namespace communication::bolt { namespace communication::bolt {
/**
* Structure used when reading a Vertex with the decoder.
* The decoder writes data into this structure.
*/
struct DecodedVertex {
int64_t id;
std::vector<std::string> labels;
std::map<std::string, query::TypedValue> properties;
};
/**
* Structure used when reading an Edge with the decoder.
* The decoder writes data into this structure.
*/
struct DecodedEdge {
int64_t id;
int64_t from;
int64_t to;
std::string type;
std::map<std::string, query::TypedValue> properties;
};
/** /**
* Bolt Decoder. * Bolt Decoder.
* Has public interfaces for reading Bolt encoded data. * Has public interfaces for reading Bolt encoded data.
* Supports reading: TypedValue (without Vertex, Edge and Path),
* Vertex, Edge
* *
* @tparam Buffer the input buffer that should be used * @tparam Buffer the input buffer that should be used
*/ */
@ -48,20 +24,20 @@ class Decoder {
Decoder(Buffer &buffer) : buffer_(buffer) {} Decoder(Buffer &buffer) : buffer_(buffer) {}
/** /**
* Reads a TypedValue from the available data in the buffer. * Reads a DecodedValue from the available data in the buffer.
* This function tries to read a TypedValue from the available data. * This function tries to read a DecodedValue from the available data.
* *
* @param data pointer to a TypedValue where the read data should be stored * @param data pointer to a DecodedValue where the read data should be stored
* @returns true if data has been written to the data pointer, * @returns true if data has been written to the data pointer,
* false otherwise * false otherwise
*/ */
bool ReadTypedValue(query::TypedValue *data) { bool ReadValue(DecodedValue *data) {
uint8_t value; uint8_t value;
DLOG(INFO) << "[ReadTypedValue] Start"; DLOG(INFO) << "[ReadValue] Start";
if (!buffer_.Read(&value, 1)) { if (!buffer_.Read(&value, 1)) {
DLOG(WARNING) << "[ReadTypedValue] Marker data missing!"; DLOG(WARNING) << "[ReadValue] Marker data missing!";
return false; return false;
} }
@ -99,6 +75,12 @@ class Decoder {
case Marker::Map32: case Marker::Map32:
return ReadMap(marker, data); return ReadMap(marker, data);
case Marker::TinyStruct3:
return ReadVertex(marker, data);
case Marker::TinyStruct5:
return ReadEdge(marker, data);
default: default:
if ((value & 0xF0) == underlying_cast(Marker::TinyString)) { if ((value & 0xF0) == underlying_cast(Marker::TinyString)) {
return ReadString(marker, data); return ReadString(marker, data);
@ -114,21 +96,21 @@ class Decoder {
} }
/** /**
* Reads a TypedValue from the available data in the buffer and checks * Reads a DecodedValue from the available data in the buffer and checks
* whether the read data type matches the supplied data type. * whether the read data type matches the supplied data type.
* *
* @param data pointer to a TypedValue where the read data should be stored * @param data pointer to a DecodedValue where the read data should be stored
* @param type the expected type that should be read * @param type the expected type that should be read
* @returns true if data has been written to the data pointer and the type * @returns true if data has been written to the data pointer and the type
* matches the expected type, false otherwise * matches the expected type, false otherwise
*/ */
bool ReadTypedValue(query::TypedValue *data, query::TypedValue::Type type) { bool ReadValue(DecodedValue *data, DecodedValue::Type type) {
if (!ReadTypedValue(data)) { if (!ReadValue(data)) {
DLOG(WARNING) << "[ReadTypedValue] ReadTypedValue call failed!"; DLOG(WARNING) << "[ReadValue] ReadValue call failed!";
return false; return false;
} }
if (data->type() != type) { if (data->type() != type) {
DLOG(WARNING) << "[ReadTypedValue] Typed value has wrong type!"; DLOG(WARNING) << "[ReadValue] Decoded value has wrong type!";
return false; return false;
} }
return true; return true;
@ -159,164 +141,32 @@ class Decoder {
return true; return true;
} }
/**
* Reads a Vertex from the available data in the buffer.
* This function tries to read a Vertex from the available data.
*
* @param data pointer to a DecodedVertex where the data should be stored
* @returns true if data has been written into the data pointer,
* false otherwise
*/
bool ReadVertex(DecodedVertex *data) {
uint8_t value[2];
query::TypedValue tv;
DLOG(INFO) << "[ReadVertex] Start";
if (!buffer_.Read(value, 2)) {
DLOG(WARNING) << "[ReadVertex] Missing marker and/or signature data!";
return false;
}
// check header
if (value[0] != underlying_cast(Marker::TinyStruct) + 3) {
DLOG(WARNING) << "[ReadVertex] Received invalid marker " << value[0];
return false;
}
if (value[1] != underlying_cast(Signature::Node)) {
DLOG(WARNING) << "[ReadVertex] Received invalid signature " << value[1];
return false;
}
// read ID
if (!ReadTypedValue(&tv, query::TypedValue::Type::Int)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read ID!";
return false;
}
data->id = tv.Value<int64_t>();
// read labels
if (!ReadTypedValue(&tv, query::TypedValue::Type::List)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read labels!";
return false;
}
auto &labels = tv.Value<std::vector<query::TypedValue>>();
data->labels.resize(labels.size());
for (size_t i = 0; i < labels.size(); ++i) {
if (labels[i].type() != query::TypedValue::Type::String) {
DLOG(WARNING) << "[ReadVertex] Label has wrong type!";
return false;
}
data->labels[i] = labels[i].Value<std::string>();
}
// read properties
if (!ReadTypedValue(&tv, query::TypedValue::Type::Map)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read properties!";
return false;
}
data->properties = tv.Value<std::map<std::string, query::TypedValue>>();
DLOG(INFO) << "[ReadVertex] Success";
return true;
}
/**
* Reads an Edge from the available data in the buffer.
* This function tries to read an Edge from the available data.
*
* @param data pointer to a DecodedEdge where the data should be stored
* @returns true if data has been written into the data pointer,
* false otherwise
*/
bool ReadEdge(DecodedEdge *data) {
uint8_t value[2];
query::TypedValue tv;
DLOG(INFO) << "[ReadEdge] Start";
if (!buffer_.Read(value, 2)) {
DLOG(WARNING) << "[ReadEdge] Missing marker and/or signature data!";
return false;
}
// check header
if (value[0] != underlying_cast(Marker::TinyStruct) + 5) {
DLOG(WARNING) << "[ReadEdge] Received invalid marker " << value[0];
return false;
}
if (value[1] != underlying_cast(Signature::Relationship)) {
DLOG(WARNING) << "[ReadEdge] Received invalid signature " << value[1];
return false;
}
// read ID
if (!ReadTypedValue(&tv, query::TypedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] couldn't read ID!";
return false;
}
data->id = tv.Value<int64_t>();
// read from
if (!ReadTypedValue(&tv, query::TypedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read from_id!";
return false;
}
data->from = tv.Value<int64_t>();
// read to
if (!ReadTypedValue(&tv, query::TypedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read to_id!";
return false;
}
data->to = tv.Value<int64_t>();
// read type
if (!ReadTypedValue(&tv, query::TypedValue::Type::String)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read type!";
return false;
}
data->type = tv.Value<std::string>();
// read properties
if (!ReadTypedValue(&tv, query::TypedValue::Type::Map)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read properties!";
return false;
}
data->properties = tv.Value<std::map<std::string, query::TypedValue>>();
DLOG(INFO) << "ReadEdge] Success";
return true;
}
protected: protected:
Buffer &buffer_; Buffer &buffer_;
private: private:
bool ReadNull(const Marker &marker, query::TypedValue *data) { bool ReadNull(const Marker &marker, DecodedValue *data) {
DLOG(INFO) << "[ReadNull] Start"; DLOG(INFO) << "[ReadNull] Start";
debug_assert(marker == Marker::Null, "Received invalid marker!"); debug_assert(marker == Marker::Null, "Received invalid marker!");
*data = query::TypedValue::Null; *data = DecodedValue();
DLOG(INFO) << "[ReadNull] Success"; DLOG(INFO) << "[ReadNull] Success";
return true; return true;
} }
bool ReadBool(const Marker &marker, query::TypedValue *data) { bool ReadBool(const Marker &marker, DecodedValue *data) {
DLOG(INFO) << "[ReadBool] Start"; DLOG(INFO) << "[ReadBool] Start";
debug_assert(marker == Marker::False || marker == Marker::True, debug_assert(marker == Marker::False || marker == Marker::True,
"Received invalid marker!"); "Received invalid marker!");
if (marker == Marker::False) { if (marker == Marker::False) {
*data = query::TypedValue(false); *data = DecodedValue(false);
} else { } else {
*data = query::TypedValue(true); *data = DecodedValue(true);
} }
DLOG(INFO) << "[ReadBool] Success"; DLOG(INFO) << "[ReadBool] Success";
return true; return true;
} }
bool ReadInt(const Marker &marker, query::TypedValue *data) { bool ReadInt(const Marker &marker, DecodedValue *data) {
uint8_t value = underlying_cast(marker); uint8_t value = underlying_cast(marker);
bool success = true; bool success = true;
int64_t ret; int64_t ret;
@ -362,13 +212,13 @@ class Decoder {
return false; return false;
} }
if (success) { if (success) {
*data = query::TypedValue(ret); *data = DecodedValue(ret);
DLOG(INFO) << "[ReadInt] Success"; DLOG(INFO) << "[ReadInt] Success";
} }
return success; return success;
} }
bool ReadDouble(const Marker marker, query::TypedValue *data) { bool ReadDouble(const Marker marker, DecodedValue *data) {
uint64_t value; uint64_t value;
double ret; double ret;
DLOG(INFO) << "[ReadDouble] Start"; DLOG(INFO) << "[ReadDouble] Start";
@ -379,7 +229,7 @@ class Decoder {
} }
value = bswap(value); value = bswap(value);
ret = *reinterpret_cast<double *>(&value); ret = *reinterpret_cast<double *>(&value);
*data = query::TypedValue(ret); *data = DecodedValue(ret);
DLOG(INFO) << "[ReadDouble] Success"; DLOG(INFO) << "[ReadDouble] Success";
return true; return true;
} }
@ -422,7 +272,7 @@ class Decoder {
} }
} }
bool ReadString(const Marker &marker, query::TypedValue *data) { bool ReadString(const Marker &marker, DecodedValue *data) {
DLOG(INFO) << "[ReadString] Start"; DLOG(INFO) << "[ReadString] Start";
auto size = ReadTypeSize(marker, MarkerString); auto size = ReadTypeSize(marker, MarkerString);
if (size == -1) { if (size == -1) {
@ -434,32 +284,32 @@ class Decoder {
DLOG(WARNING) << "[ReadString] Missing data!"; DLOG(WARNING) << "[ReadString] Missing data!";
return false; return false;
} }
*data = query::TypedValue( *data =
std::string(reinterpret_cast<char *>(ret.get()), size)); DecodedValue(std::string(reinterpret_cast<char *>(ret.get()), size));
DLOG(INFO) << "[ReadString] Success"; DLOG(INFO) << "[ReadString] Success";
return true; return true;
} }
bool ReadList(const Marker &marker, query::TypedValue *data) { bool ReadList(const Marker &marker, DecodedValue *data) {
DLOG(INFO) << "[ReadList] Start"; DLOG(INFO) << "[ReadList] Start";
auto size = ReadTypeSize(marker, MarkerList); auto size = ReadTypeSize(marker, MarkerList);
if (size == -1) { if (size == -1) {
DLOG(WARNING) << "[ReadList] Couldn't get size!"; DLOG(WARNING) << "[ReadList] Couldn't get size!";
return false; return false;
} }
std::vector<query::TypedValue> ret(size); std::vector<DecodedValue> ret(size);
for (int64_t i = 0; i < size; ++i) { for (int64_t i = 0; i < size; ++i) {
if (!ReadTypedValue(&ret[i])) { if (!ReadValue(&ret[i])) {
DLOG(WARNING) << "[ReadList] Couldn't read element " << i; DLOG(WARNING) << "[ReadList] Couldn't read element " << i;
return false; return false;
} }
} }
*data = query::TypedValue(ret); *data = DecodedValue(ret);
DLOG(INFO) << "[ReadList] Success"; DLOG(INFO) << "[ReadList] Success";
return true; return true;
} }
bool ReadMap(const Marker &marker, query::TypedValue *data) { bool ReadMap(const Marker &marker, DecodedValue *data) {
DLOG(INFO) << "[ReadMap] Start"; DLOG(INFO) << "[ReadMap] Start";
auto size = ReadTypeSize(marker, MarkerMap); auto size = ReadTypeSize(marker, MarkerMap);
if (size == -1) { if (size == -1) {
@ -467,25 +317,25 @@ class Decoder {
return false; return false;
} }
query::TypedValue tv; DecodedValue dv;
std::string str; std::string str;
std::map<std::string, query::TypedValue> ret; std::map<std::string, DecodedValue> ret;
for (int64_t i = 0; i < size; ++i) { for (int64_t i = 0; i < size; ++i) {
if (!ReadTypedValue(&tv)) { if (!ReadValue(&dv)) {
DLOG(WARNING) << "[ReadMap] Couldn't read index " << i; DLOG(WARNING) << "[ReadMap] Couldn't read index " << i;
return false; return false;
} }
if (tv.type() != query::TypedValue::Type::String) { if (dv.type() != DecodedValue::Type::String) {
DLOG(WARNING) << "[ReadMap] Index " << i << " isn't a string!"; DLOG(WARNING) << "[ReadMap] Index " << i << " isn't a string!";
return false; return false;
} }
str = tv.Value<std::string>(); str = dv.ValueString();
if (!ReadTypedValue(&tv)) { if (!ReadValue(&dv)) {
DLOG(WARNING) << "[ReadMap] Couldn't read element " << i; DLOG(WARNING) << "[ReadMap] Couldn't read element " << i;
return false; return false;
} }
ret.insert(std::make_pair(str, tv)); ret.insert(std::make_pair(str, dv));
} }
if (ret.size() != size) { if (ret.size() != size) {
DLOG(WARNING) DLOG(WARNING)
@ -493,9 +343,133 @@ class Decoder {
return false; return false;
} }
*data = query::TypedValue(ret); *data = DecodedValue(ret);
DLOG(INFO) << "[ReadMap] Success"; DLOG(INFO) << "[ReadMap] Success";
return true; return true;
} }
bool ReadVertex(const Marker &marker, DecodedValue *data) {
uint8_t value;
DecodedValue dv;
DecodedVertex vertex;
DLOG(INFO) << "[ReadVertex] Start";
if (!buffer_.Read(&value, 1)) {
DLOG(WARNING) << "[ReadVertex] Missing marker and/or signature data!";
return false;
}
// check header
if (marker != Marker::TinyStruct3) {
DLOG(WARNING) << "[ReadVertex] Received invalid marker "
<< (uint64_t)underlying_cast(marker);
return false;
}
if (value != underlying_cast(Signature::Node)) {
DLOG(WARNING) << "[ReadVertex] Received invalid signature " << value;
return false;
}
// read ID
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read ID!";
return false;
}
vertex.id = dv.ValueInt();
// read labels
if (!ReadValue(&dv, DecodedValue::Type::List)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read labels!";
return false;
}
auto &labels = dv.ValueList();
vertex.labels.resize(labels.size());
for (size_t i = 0; i < labels.size(); ++i) {
if (labels[i].type() != DecodedValue::Type::String) {
DLOG(WARNING) << "[ReadVertex] Label has wrong type!";
return false;
}
vertex.labels[i] = labels[i].ValueString();
}
// read properties
if (!ReadValue(&dv, DecodedValue::Type::Map)) {
DLOG(WARNING) << "[ReadVertex] Couldn't read properties!";
return false;
}
vertex.properties = dv.ValueMap();
*data = DecodedValue(vertex);
DLOG(INFO) << "[ReadVertex] Success";
return true;
}
bool ReadEdge(const Marker &marker, DecodedValue *data) {
uint8_t value;
DecodedValue dv;
DecodedEdge edge;
DLOG(INFO) << "[ReadEdge] Start";
if (!buffer_.Read(&value, 1)) {
DLOG(WARNING) << "[ReadEdge] Missing marker and/or signature data!";
return false;
}
// check header
if (marker != Marker::TinyStruct5) {
DLOG(WARNING) << "[ReadEdge] Received invalid marker "
<< (uint64_t)underlying_cast(marker);
return false;
}
if (value != underlying_cast(Signature::Relationship)) {
DLOG(WARNING) << "[ReadEdge] Received invalid signature " << value;
return false;
}
// read ID
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] couldn't read ID!";
return false;
}
edge.id = dv.ValueInt();
// read from
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read from_id!";
return false;
}
edge.from = dv.ValueInt();
// read to
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read to_id!";
return false;
}
edge.to = dv.ValueInt();
// read type
if (!ReadValue(&dv, DecodedValue::Type::String)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read type!";
return false;
}
edge.type = dv.ValueString();
// read properties
if (!ReadValue(&dv, DecodedValue::Type::Map)) {
DLOG(WARNING) << "[ReadEdge] Couldn't read properties!";
return false;
}
edge.properties = dv.ValueMap();
*data = DecodedValue(edge);
DLOG(INFO) << "[ReadEdge] Success";
return true;
}
}; };
} }

View File

@ -0,0 +1,144 @@
#pragma once
#include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/encoder/base_encoder.hpp"
namespace communication::bolt {
/**
* Bolt Client Encoder.
* Has public interfaces for writing Bolt specific request messages.
* Supported messages are: Init, Run, DiscardAll, PullAll, AckFailure, Reset
*
* @tparam Buffer the output buffer that should be used
*/
template <typename Buffer>
class ClientEncoder : private BaseEncoder<Buffer> {
private:
using BaseEncoder<Buffer>::WriteRAW;
using BaseEncoder<Buffer>::WriteList;
using BaseEncoder<Buffer>::WriteMap;
using BaseEncoder<Buffer>::WriteString;
using BaseEncoder<Buffer>::buffer_;
public:
ClientEncoder(Buffer &buffer) : BaseEncoder<Buffer>(buffer) {}
/**
* Writes a Init message.
*
* From the Bolt v1 documentation:
* InitMessage (signature=0x01) {
* String clientName
* Map<String,Value> authToken
* }
*
* @param client_name the name of the connected client
* @param auth_token a map with authentication data
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessageInit(const std::string client_name,
const std::map<std::string, query::TypedValue> &auth_token) {
WriteRAW(underlying_cast(Marker::TinyStruct2));
WriteRAW(underlying_cast(Signature::Init));
WriteString(client_name);
WriteMap(auth_token);
return buffer_.Flush();
}
/**
* Writes a Run message.
*
* From the Bolt v1 documentation:
* RunMessage (signature=0x10) {
* String statement
* Map<String,Value> parameters
* }
*
* @param statement the statement that should be executed
* @param parameters parameters that should be used to execute the statement
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessageRun(const std::string statement,
const std::map<std::string, query::TypedValue> &parameters,
bool flush = true) {
WriteRAW(underlying_cast(Marker::TinyStruct2));
WriteRAW(underlying_cast(Signature::Run));
WriteString(statement);
WriteMap(parameters);
if (flush) {
return buffer_.Flush();
} else {
buffer_.Chunk();
// Chunk always succeeds, so return true
return true;
}
}
/**
* Writes a DiscardAll message.
*
* From the Bolt v1 documentation:
* DiscardAllMessage (signature=0x2F) {
* }
*
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessageDiscardAll() {
WriteRAW(underlying_cast(Marker::TinyStruct));
WriteRAW(underlying_cast(Signature::DiscardAll));
return buffer_.Flush();
}
/**
* Writes a PullAll message.
*
* From the Bolt v1 documentation:
* PullAllMessage (signature=0x3F) {
* }
*
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessagePullAll() {
WriteRAW(underlying_cast(Marker::TinyStruct));
WriteRAW(underlying_cast(Signature::PullAll));
return buffer_.Flush();
}
/**
* Writes a AckFailure message.
*
* From the Bolt v1 documentation:
* AckFailureMessage (signature=0x0E) {
* }
*
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessageAckFailure() {
WriteRAW(underlying_cast(Marker::TinyStruct));
WriteRAW(underlying_cast(Signature::AckFailure));
return buffer_.Flush();
}
/**
* Writes a Reset message.
*
* From the Bolt v1 documentation:
* ResetMessage (signature=0x0F) {
* }
*
* @returns true if the data was successfully sent to the client
* when flushing, false otherwise
*/
bool MessageReset() {
WriteRAW(underlying_cast(Marker::TinyStruct));
WriteRAW(underlying_cast(Signature::Reset));
return buffer_.Flush();
}
};
}

View File

@ -4,6 +4,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include "communication/bolt/v1/codes.hpp" #include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/decoder/decoded_value.hpp"
#include "communication/bolt/v1/state.hpp" #include "communication/bolt/v1/state.hpp"
#include "query/typed_value.hpp" #include "query/typed_value.hpp"
@ -69,9 +70,9 @@ State StateErrorRun(Session &session, State state) {
// we need to clean up all parameters from this command // we need to clean up all parameters from this command
value &= 0x0F; // the length is stored in the lower nibble value &= 0x0F; // the length is stored in the lower nibble
query::TypedValue tv; DecodedValue dv;
for (int i = 0; i < value; ++i) { for (int i = 0; i < value; ++i) {
if (!session.decoder_.ReadTypedValue(&tv)) { if (!session.decoder_.ReadValue(&dv)) {
DLOG(WARNING) << fmt::format("Couldn't clean up parameter {} / {}!", i, DLOG(WARNING) << fmt::format("Couldn't clean up parameter {} / {}!", i,
value); value);
return State::Close; return State::Close;

View File

@ -7,9 +7,6 @@
namespace communication::bolt { namespace communication::bolt {
static constexpr uint8_t preamble[4] = {0x60, 0x60, 0xB0, 0x17};
static constexpr uint8_t protocol[4] = {0x00, 0x00, 0x00, 0x01};
/** /**
* Handshake state run function * Handshake state run function
* This function runs everything to make a Bolt handshake with the client. * This function runs everything to make a Bolt handshake with the client.
@ -17,7 +14,7 @@ static constexpr uint8_t protocol[4] = {0x00, 0x00, 0x00, 0x01};
*/ */
template <typename Session> template <typename Session>
State StateHandshakeRun(Session &session) { State StateHandshakeRun(Session &session) {
auto precmp = memcmp(session.buffer_.data(), preamble, sizeof(preamble)); auto precmp = memcmp(session.buffer_.data(), kPreamble, sizeof(kPreamble));
if (UNLIKELY(precmp != 0)) { if (UNLIKELY(precmp != 0)) {
DLOG(WARNING) << "Received a wrong preamble!"; DLOG(WARNING) << "Received a wrong preamble!";
return State::Close; return State::Close;
@ -27,7 +24,7 @@ State StateHandshakeRun(Session &session) {
// make sense to check which version the client prefers // make sense to check which version the client prefers
// this will change in the future // this will change in the future
if (!session.socket_.Write(protocol, sizeof(protocol))) { if (!session.socket_.Write(kProtocol, sizeof(kProtocol))) {
DLOG(WARNING) << "Couldn't write handshake response!"; DLOG(WARNING) << "Couldn't write handshake response!";
return State::Close; return State::Close;
} }

View File

@ -6,6 +6,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include "communication/bolt/v1/codes.hpp" #include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/decoder/decoded_value.hpp"
#include "communication/bolt/v1/state.hpp" #include "communication/bolt/v1/state.hpp"
#include "query/exceptions.hpp" #include "query/exceptions.hpp"
#include "query/typed_value.hpp" #include "query/typed_value.hpp"
@ -25,20 +26,19 @@ State HandleRun(Session &session, State state, Marker marker) {
return State::Close; return State::Close;
} }
query::TypedValue query, params; DecodedValue query, params;
if (!session.decoder_.ReadTypedValue(&query, if (!session.decoder_.ReadValue(&query, DecodedValue::Type::String)) {
query::TypedValue::Type::String)) {
DLOG(WARNING) << "Couldn't read query string!"; DLOG(WARNING) << "Couldn't read query string!";
return State::Close; return State::Close;
} }
if (!session.decoder_.ReadTypedValue(&params, query::TypedValue::Type::Map)) { if (!session.decoder_.ReadValue(&params, DecodedValue::Type::Map)) {
DLOG(WARNING) << "Couldn't read parameters!"; DLOG(WARNING) << "Couldn't read parameters!";
return State::Close; return State::Close;
} }
if (state == State::WaitForRollback) { if (state == State::WaitForRollback) {
if (query.Value<std::string>() == "ROLLBACK") { if (query.ValueString() == "ROLLBACK") {
session.Abort(); session.Abort();
// One MessageSuccess for RUN command should be flushed. // One MessageSuccess for RUN command should be flushed.
session.encoder_.MessageSuccess(kEmptyFields); session.encoder_.MessageSuccess(kEmptyFields);
@ -65,7 +65,7 @@ State HandleRun(Session &session, State state, Marker marker) {
debug_assert(!session.encoder_buffer_.HasData(), debug_assert(!session.encoder_buffer_.HasData(),
"There should be no data to write in this state"); "There should be no data to write in this state");
DLOG(INFO) << fmt::format("[Run] '{}'", query.Value<std::string>()); DLOG(INFO) << fmt::format("[Run] '{}'", query.ValueString());
bool in_explicit_transaction = false; bool in_explicit_transaction = false;
if (session.db_accessor_) { if (session.db_accessor_) {
// Transaction already exists. // Transaction already exists.
@ -79,7 +79,7 @@ State HandleRun(Session &session, State state, Marker marker) {
// If there was not explicitly started transaction before maybe we are // If there was not explicitly started transaction before maybe we are
// starting one now. // starting one now.
if (!in_explicit_transaction && query.Value<std::string>() == "BEGIN") { if (!in_explicit_transaction && query.ValueString() == "BEGIN") {
// Check if query string is "BEGIN". If it is then we should start // Check if query string is "BEGIN". If it is then we should start
// transaction and wait for in-transaction queries. // transaction and wait for in-transaction queries.
// TODO: "BEGIN" is not defined by bolt protocol or opencypher so we should // TODO: "BEGIN" is not defined by bolt protocol or opencypher so we should
@ -94,14 +94,14 @@ State HandleRun(Session &session, State state, Marker marker) {
} }
if (in_explicit_transaction) { if (in_explicit_transaction) {
if (query.Value<std::string>() == "COMMIT") { if (query.ValueString() == "COMMIT") {
session.Commit(); session.Commit();
// One MessageSuccess for RUN command should be flushed. // One MessageSuccess for RUN command should be flushed.
session.encoder_.MessageSuccess(kEmptyFields); session.encoder_.MessageSuccess(kEmptyFields);
// One for PULL_ALL should be chunked. // One for PULL_ALL should be chunked.
session.encoder_.MessageSuccess({}, false); session.encoder_.MessageSuccess({}, false);
return State::Result; return State::Result;
} else if (query.Value<std::string>() == "ROLLBACK") { } else if (query.ValueString() == "ROLLBACK") {
session.Abort(); session.Abort();
// One MessageSuccess for RUN command should be flushed. // One MessageSuccess for RUN command should be flushed.
session.encoder_.MessageSuccess(kEmptyFields); session.encoder_.MessageSuccess(kEmptyFields);
@ -113,10 +113,12 @@ State HandleRun(Session &session, State state, Marker marker) {
} }
try { try {
auto is_successfully_executed = session.query_engine_.Run( auto &params_map = params.ValueMap();
query.Value<std::string>(), *session.db_accessor_, std::map<std::string, query::TypedValue> params_tv(params_map.begin(),
session.output_stream_, params_map.end());
params.Value<std::map<std::string, query::TypedValue>>()); auto is_successfully_executed =
session.query_engine_.Run(query.ValueString(), *session.db_accessor_,
session.output_stream_, params_tv);
// TODO: once we remove compiler from query_engine we can change return type // TODO: once we remove compiler from query_engine we can change return type
// to void and not do this checks here. // to void and not do this checks here.

View File

@ -4,6 +4,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include "communication/bolt/v1/codes.hpp" #include "communication/bolt/v1/codes.hpp"
#include "communication/bolt/v1/decoder/decoded_value.hpp"
#include "communication/bolt/v1/encoder/result_stream.hpp" #include "communication/bolt/v1/encoder/result_stream.hpp"
#include "communication/bolt/v1/state.hpp" #include "communication/bolt/v1/state.hpp"
#include "utils/likely.hpp" #include "utils/likely.hpp"
@ -45,22 +46,19 @@ State StateInitRun(Session &session) {
// return State::Close; // return State::Close;
} }
query::TypedValue client_name; DecodedValue client_name;
if (!session.decoder_.ReadTypedValue(&client_name, if (!session.decoder_.ReadValue(&client_name, DecodedValue::Type::String)) {
query::TypedValue::Type::String)) {
DLOG(WARNING) << "Couldn't read client name!"; DLOG(WARNING) << "Couldn't read client name!";
return State::Close; return State::Close;
} }
query::TypedValue metadata; DecodedValue metadata;
if (!session.decoder_.ReadTypedValue(&metadata, if (!session.decoder_.ReadValue(&metadata, DecodedValue::Type::Map)) {
query::TypedValue::Type::Map)) {
DLOG(WARNING) << "Couldn't read metadata!"; DLOG(WARNING) << "Couldn't read metadata!";
return State::Close; return State::Close;
} }
LOG(INFO) << fmt::format("Client connected '{}'", LOG(INFO) << fmt::format("Client connected '{}'", client_name.ValueString())
client_name.Value<std::string>())
<< std::endl; << std::endl;
if (!session.encoder_.MessageSuccess()) { if (!session.encoder_.MessageSuccess()) {

View File

@ -1,6 +1,9 @@
#include "durability/recovery.hpp" #include "durability/recovery.hpp"
#include "communication/bolt/v1/decoder/decoder.hpp" #include "communication/bolt/v1/decoder/decoder.hpp"
#include "durability/file_reader_buffer.hpp" #include "durability/file_reader_buffer.hpp"
#include "query/typed_value.hpp"
using communication::bolt::DecodedValue;
bool Recovery::Recover(const fs::path &snapshot_file, bool Recovery::Recover(const fs::path &snapshot_file,
GraphDbAccessor &db_accessor) { GraphDbAccessor &db_accessor) {
@ -25,41 +28,43 @@ bool Recovery::Decode(const fs::path &snapshot_file,
} }
std::unordered_map<uint64_t, VertexAccessor> vertices; std::unordered_map<uint64_t, VertexAccessor> vertices;
query::TypedValue tv; DecodedValue dv;
if (!decoder.ReadTypedValue(&tv, query::TypedValue::Type::List)) { if (!decoder.ReadValue(&dv, DecodedValue::Type::List)) {
buffer.Close(); buffer.Close();
return false; return false;
} }
auto &label_property_vector = tv.Value<std::vector<query::TypedValue>>(); auto &label_property_vector = dv.ValueList();
for (int i = 0; i < label_property_vector.size(); i += 2) { for (int i = 0; i < label_property_vector.size(); i += 2) {
auto label = label_property_vector[i].Value<std::string>(); auto label = label_property_vector[i].ValueString();
auto property = label_property_vector[i + 1].Value<std::string>(); auto property = label_property_vector[i + 1].ValueString();
db_accessor.BuildIndex(db_accessor.Label(label), db_accessor.BuildIndex(db_accessor.Label(label),
db_accessor.Property(property)); db_accessor.Property(property));
} }
for (int64_t i = 0; i < summary.vertex_num_; ++i) { for (int64_t i = 0; i < summary.vertex_num_; ++i) {
communication::bolt::DecodedVertex vertex; DecodedValue vertex_dv;
if (!decoder.ReadVertex(&vertex)) { if (!decoder.ReadValue(&vertex_dv, DecodedValue::Type::Vertex)) {
buffer.Close(); buffer.Close();
return false; return false;
} }
auto &vertex = vertex_dv.ValueVertex();
auto vertex_accessor = db_accessor.InsertVertex(); auto vertex_accessor = db_accessor.InsertVertex();
for (const auto &label : vertex.labels) { for (const auto &label : vertex.labels) {
vertex_accessor.add_label(db_accessor.Label(label)); vertex_accessor.add_label(db_accessor.Label(label));
} }
for (const auto &property_pair : vertex.properties) { for (const auto &property_pair : vertex.properties) {
vertex_accessor.PropsSet(db_accessor.Property(property_pair.first), vertex_accessor.PropsSet(db_accessor.Property(property_pair.first),
property_pair.second); query::TypedValue(property_pair.second));
} }
vertices.insert({vertex.id, vertex_accessor}); vertices.insert({vertex.id, vertex_accessor});
} }
for (int64_t i = 0; i < summary.edge_num_; ++i) { for (int64_t i = 0; i < summary.edge_num_; ++i) {
communication::bolt::DecodedEdge edge; DecodedValue edge_dv;
if (!decoder.ReadEdge(&edge)) { if (!decoder.ReadValue(&edge_dv, DecodedValue::Type::Edge)) {
buffer.Close(); buffer.Close();
return false; return false;
} }
auto &edge = edge_dv.ValueEdge();
auto it_from = vertices.find(edge.from); auto it_from = vertices.find(edge.from);
auto it_to = vertices.find(edge.to); auto it_to = vertices.find(edge.to);
if (it_from == vertices.end() || it_to == vertices.end()) { if (it_from == vertices.end() || it_to == vertices.end()) {
@ -71,7 +76,7 @@ bool Recovery::Decode(const fs::path &snapshot_file,
for (const auto &property_pair : edge.properties) for (const auto &property_pair : edge.properties)
edge_accessor.PropsSet(db_accessor.Property(property_pair.first), edge_accessor.PropsSet(db_accessor.Property(property_pair.first),
property_pair.second); query::TypedValue(property_pair.second));
} }
uint64_t hash = buffer.hash(); uint64_t hash = buffer.hash();

View File

@ -0,0 +1,66 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "communication/bolt/client.hpp"
#include "io/network/network_endpoint.hpp"
#include "io/network/socket.hpp"
using SocketT = io::network::Socket;
using EndpointT = io::network::NetworkEndpoint;
using ClientT = communication::bolt::Client<SocketT>;
DEFINE_string(address, "127.0.0.1", "Server address");
DEFINE_string(port, "7687", "Server port");
DEFINE_string(username, "", "Username for the database");
DEFINE_string(password, "", "Password for the database");
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
// TODO: handle endpoint exception
EndpointT endpoint(FLAGS_address, FLAGS_port);
SocketT socket;
if (!socket.Connect(endpoint)) return 1;
ClientT client(std::move(socket), FLAGS_username, FLAGS_password);
std::cout << "Memgraph bolt client is connected and running." << std::endl;
while (true) {
std::string s;
std::getline(std::cin, s);
if (s == "") {
break;
}
try {
auto ret = client.Execute(s, {});
std::cout << "Fields:" << std::endl;
for (auto &field : ret.fields) {
std::cout << " " << field << std::endl;
}
std::cout << "Records:" << std::endl;
for (int i = 0; i < ret.records.size(); ++i) {
std::cout << " " << i << std::endl;
for (auto &value : ret.records[i]) {
std::cout << " " << value << std::endl;
}
}
std::cout << "Metadata:" << std::endl;
for (auto &data : ret.metadata) {
std::cout << " " << data.first << " : " << data.second << std::endl;
}
}
catch (const communication::bolt::ClientQueryException &e) {
std::cout << "Client received exception: " << e.what() << std::endl;
}
}
client.Close();
return 0;
}

View File

@ -0,0 +1,166 @@
#include <fstream>
#include <vector>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "communication/bolt/client.hpp"
#include "io/network/network_endpoint.hpp"
#include "io/network/socket.hpp"
#include "threading/sync/spinlock.hpp"
#include "utils/algorithm.hpp"
#include "utils/timer.hpp"
using SocketT = io::network::Socket;
using EndpointT = io::network::NetworkEndpoint;
using ClientT = communication::bolt::Client<SocketT>;
using DecodedValueT = communication::bolt::DecodedValue;
DEFINE_string(address, "127.0.0.1", "Server address");
DEFINE_string(port, "7687", "Server port");
DEFINE_uint64(num_workers, 1, "Number of workers");
DEFINE_string(output, "", "Output file");
DEFINE_string(username, "", "Username for the database");
DEFINE_string(password, "", "Password for the database");
const uint64_t MAX_RETRIES = 1000;
void PrintJsonDecodedValue(std::ostream &os, const DecodedValueT &value) {
switch (value.type()) {
case DecodedValueT::Type::Null:
os << "null";
break;
case DecodedValueT::Type::Bool:
os << (value.ValueBool() ? "true" : "false");
break;
case DecodedValueT::Type::Int:
os << value.ValueInt();
break;
case DecodedValueT::Type::Double:
os << value.ValueDouble();
break;
case DecodedValueT::Type::String:
os << "\"" << value.ValueString() << "\"";
break;
case DecodedValueT::Type::List:
os << "[";
PrintIterable(os, value.ValueList(), ", ",
[](auto &stream, const auto &item) {
PrintJsonDecodedValue(stream, item);
});
os << "]";
break;
case DecodedValueT::Type::Map:
os << "{";
PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
PrintJsonDecodedValue(stream, {pair.first});
stream << ": ";
PrintJsonDecodedValue(stream, pair.second);
});
os << "}";
break;
default:
std::terminate();
}
}
void PrintJsonMetadata(
std::ostream &os,
const std::vector<std::map<std::string, DecodedValueT>> &metadata) {
os << "[";
PrintIterable(os, metadata, ", ", [](auto &stream, const auto &item) {
PrintJsonDecodedValue(stream, item);
});
os << "]";
}
void PrintSummary(
std::ostream &os, double duration,
const std::vector<std::map<std::string, DecodedValueT>> &metadata) {
os << "{\"wall_time\": " << duration << ", "
<< "\"metadatas\": ";
PrintJsonMetadata(os, metadata);
os << "}";
}
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
std::string query;
std::vector<std::thread> threads;
SpinLock mutex;
uint64_t last = 0;
std::vector<std::string> queries;
std::vector<std::map<std::string, DecodedValueT>> metadata;
while (std::getline(std::cin, query)) {
queries.push_back(query);
}
metadata.resize(queries.size());
utils::Timer timer;
for (int i = 0; i < FLAGS_num_workers; ++i) {
threads.push_back(std::thread([&]() {
SocketT socket;
EndpointT endpoint;
try {
endpoint = EndpointT(FLAGS_address, FLAGS_port);
} catch (const io::network::NetworkEndpointException &e) {
std::terminate();
}
if (!socket.Connect(endpoint)) {
std::terminate();
}
ClientT client(std::move(socket), FLAGS_username, FLAGS_password);
uint64_t pos, i;
std::string str;
while (true) {
{
std::lock_guard<SpinLock> lock(mutex);
if (last == queries.size()) {
break;
}
pos = last++;
str = queries[pos];
}
for (i = 0; i < MAX_RETRIES; ++i) {
try {
auto ret = client.Execute(str, {});
std::lock_guard<SpinLock> lock(mutex);
metadata[pos] = ret.metadata;
break;
} catch (const communication::bolt::ClientQueryException &e) {
}
}
if (i == MAX_RETRIES) {
std::terminate();
}
}
client.Close();
}));
}
for (int i = 0; i < FLAGS_num_workers; ++i) {
threads[i].join();
}
auto elapsed = timer.Elapsed();
double duration = elapsed.count();
if (FLAGS_output != "") {
std::ofstream ofile;
ofile.open(FLAGS_output);
PrintSummary(ofile, duration, metadata);
} else {
PrintSummary(std::cout, duration, metadata);
}
return 0;
}

View File

@ -6,7 +6,7 @@
#include "communication/bolt/v1/decoder/decoder.hpp" #include "communication/bolt/v1/decoder/decoder.hpp"
#include "query/typed_value.hpp" #include "query/typed_value.hpp"
using query::TypedValue; using communication::bolt::DecodedValue;
constexpr const int SIZE = 131072; constexpr const int SIZE = 131072;
uint8_t data[SIZE]; uint8_t data[SIZE];
@ -44,49 +44,49 @@ using DecoderT = communication::bolt::Decoder<TestDecoderBuffer>;
TEST(BoltDecoder, NullAndBool) { TEST(BoltDecoder, NullAndBool) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
// test null // test null
buffer.Write((const uint8_t *)"\xC0", 1); buffer.Write((const uint8_t *)"\xC0", 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Null); ASSERT_EQ(dv.type(), DecodedValue::Type::Null);
// test true // test true
buffer.Write((const uint8_t *)"\xC3", 1); buffer.Write((const uint8_t *)"\xC3", 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Bool); ASSERT_EQ(dv.type(), DecodedValue::Type::Bool);
ASSERT_EQ(tv.Value<bool>(), true); ASSERT_EQ(dv.ValueBool(), true);
// test false // test false
buffer.Write((const uint8_t *)"\xC2", 1); buffer.Write((const uint8_t *)"\xC2", 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Bool); ASSERT_EQ(dv.type(), DecodedValue::Type::Bool);
ASSERT_EQ(tv.Value<bool>(), false); ASSERT_EQ(dv.ValueBool(), false);
} }
TEST(BoltDecoder, Int) { TEST(BoltDecoder, Int) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
// test invalid marker // test invalid marker
buffer.Clear(); buffer.Clear();
buffer.Write((uint8_t *)"\xCD", 1); // 0xCD is reserved in the protocol buffer.Write((uint8_t *)"\xCD", 1); // 0xCD is reserved in the protocol
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
for (int i = 0; i < 28; ++i) { for (int i = 0; i < 28; ++i) {
// test missing data // test missing data
buffer.Clear(); buffer.Clear();
buffer.Write(int_encoded[i], int_encoded_len[i] - 1); buffer.Write(int_encoded[i], int_encoded_len[i] - 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
buffer.Write(int_encoded[i], int_encoded_len[i]); buffer.Write(int_encoded[i], int_encoded_len[i]);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Int); ASSERT_EQ(dv.type(), DecodedValue::Type::Int);
ASSERT_EQ(tv.Value<int64_t>(), int_decoded[i]); ASSERT_EQ(dv.ValueInt(), int_decoded[i]);
} }
} }
@ -94,20 +94,20 @@ TEST(BoltDecoder, Double) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
// test missing data // test missing data
buffer.Clear(); buffer.Clear();
buffer.Write(double_encoded[i], 8); buffer.Write(double_encoded[i], 8);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
buffer.Write(double_encoded[i], 9); buffer.Write(double_encoded[i], 9);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Double); ASSERT_EQ(dv.type(), DecodedValue::Type::Double);
ASSERT_EQ(tv.Value<double>(), double_decoded[i]); ASSERT_EQ(dv.ValueDouble(), double_decoded[i]);
} }
} }
@ -115,7 +115,7 @@ TEST(BoltDecoder, String) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
uint8_t headers[][6] = {"\x8F", "\xD0\x0F", "\xD1\x00\x0F", uint8_t headers[][6] = {"\x8F", "\xD0\x0F", "\xD1\x00\x0F",
"\xD2\x00\x00\x00\x0F"}; "\xD2\x00\x00\x00\x0F"};
@ -125,21 +125,21 @@ TEST(BoltDecoder, String) {
// test missing data in header // test missing data in header
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i] - 1); buffer.Write(headers[i], headers_len[i] - 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test missing elements // test missing elements
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
buffer.Write(data, 14); buffer.Write(data, 14);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
buffer.Write(data, 15); buffer.Write(data, 15);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::String); ASSERT_EQ(dv.type(), DecodedValue::Type::String);
std::string &str = tv.Value<std::string>(); std::string &str = dv.ValueString();
for (int j = 0; j < 15; ++j) EXPECT_EQ((uint8_t)str[j], data[j]); for (int j = 0; j < 15; ++j) EXPECT_EQ((uint8_t)str[j], data[j]);
} }
} }
@ -148,7 +148,7 @@ TEST(BoltDecoder, List) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
uint8_t headers[][6] = {"\x9F", "\xD4\x0F", "\xD5\x00\x0F", uint8_t headers[][6] = {"\x9F", "\xD4\x0F", "\xD5\x00\x0F",
"\xD6\x00\x00\x00\x0F"}; "\xD6\x00\x00\x00\x0F"};
@ -158,23 +158,23 @@ TEST(BoltDecoder, List) {
// test missing data in header // test missing data in header
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i] - 1); buffer.Write(headers[i], headers_len[i] - 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test missing elements // test missing elements
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
for (uint8_t j = 0; j < 14; ++j) buffer.Write(&j, 1); for (uint8_t j = 0; j < 14; ++j) buffer.Write(&j, 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
for (uint8_t j = 0; j < 15; ++j) buffer.Write(&j, 1); for (uint8_t j = 0; j < 15; ++j) buffer.Write(&j, 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::List); ASSERT_EQ(dv.type(), DecodedValue::Type::List);
std::vector<TypedValue> &val = tv.Value<std::vector<TypedValue>>(); std::vector<DecodedValue> &val = dv.ValueList();
ASSERT_EQ(val.size(), 15); ASSERT_EQ(val.size(), 15);
for (int j = 0; j < 15; ++j) EXPECT_EQ(val[j].Value<int64_t>(), j); for (int j = 0; j < 15; ++j) EXPECT_EQ(val[j].ValueInt(), j);
} }
} }
@ -182,7 +182,7 @@ TEST(BoltDecoder, Map) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
TypedValue tv; DecodedValue dv;
uint8_t headers[][6] = {"\xAF", "\xD8\x0F", "\xD9\x00\x0F", uint8_t headers[][6] = {"\xAF", "\xD8\x0F", "\xD9\x00\x0F",
"\xDA\x00\x00\x00\x0F"}; "\xDA\x00\x00\x00\x0F"};
@ -195,20 +195,20 @@ TEST(BoltDecoder, Map) {
// test missing data in header // test missing data in header
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i] - 1); buffer.Write(headers[i], headers_len[i] - 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test wrong index type // test wrong index type
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
buffer.Write(&wrong_index, 1); buffer.Write(&wrong_index, 1);
buffer.Write(&wrong_index, 1); buffer.Write(&wrong_index, 1);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test missing element data // test missing element data
buffer.Clear(); buffer.Clear();
buffer.Write(headers[i], headers_len[i]); buffer.Write(headers[i], headers_len[i]);
buffer.Write(index, 2); buffer.Write(index, 2);
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test missing elements // test missing elements
buffer.Clear(); buffer.Clear();
@ -217,7 +217,7 @@ TEST(BoltDecoder, Map) {
buffer.Write(index, 2); buffer.Write(index, 2);
buffer.Write(&j, 1); buffer.Write(&j, 1);
} }
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test elements with same index // test elements with same index
buffer.Clear(); buffer.Clear();
@ -226,7 +226,7 @@ TEST(BoltDecoder, Map) {
buffer.Write(index, 2); buffer.Write(index, 2);
buffer.Write(&j, 1); buffer.Write(&j, 1);
} }
ASSERT_EQ(decoder.ReadTypedValue(&tv), false); ASSERT_EQ(decoder.ReadValue(&dv), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
@ -237,16 +237,15 @@ TEST(BoltDecoder, Map) {
buffer.Write(&tmp, 1); buffer.Write(&tmp, 1);
buffer.Write(&j, 1); buffer.Write(&j, 1);
} }
ASSERT_EQ(decoder.ReadTypedValue(&tv), true); ASSERT_EQ(decoder.ReadValue(&dv), true);
ASSERT_EQ(tv.type(), TypedValue::Type::Map); ASSERT_EQ(dv.type(), DecodedValue::Type::Map);
std::map<std::string, TypedValue> &val = std::map<std::string, DecodedValue> &val = dv.ValueMap();
tv.Value<std::map<std::string, TypedValue>>();
ASSERT_EQ(val.size(), 15); ASSERT_EQ(val.size(), 15);
for (int j = 0; j < 15; ++j) { for (int j = 0; j < 15; ++j) {
char tmp_chr = 'a' + j; char tmp_chr = 'a' + j;
TypedValue tmp_tv = val[std::string(1, tmp_chr)]; DecodedValue tmp_dv = val[std::string(1, tmp_chr)];
EXPECT_EQ(tmp_tv.type(), TypedValue::Type::Int); EXPECT_EQ(tmp_dv.type(), DecodedValue::Type::Int);
EXPECT_EQ(tmp_tv.Value<int64_t>(), j); EXPECT_EQ(tmp_dv.ValueInt(), j);
} }
} }
} }
@ -255,7 +254,7 @@ TEST(BoltDecoder, Vertex) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
communication::bolt::DecodedVertex dv; DecodedValue dv;
uint8_t header[] = "\xB3\x4E"; uint8_t header[] = "\xB3\x4E";
uint8_t wrong_header[] = "\x00\x00"; uint8_t wrong_header[] = "\x00\x00";
@ -267,31 +266,31 @@ TEST(BoltDecoder, Vertex) {
// test missing signature // test missing signature
buffer.Clear(); buffer.Clear();
buffer.Write(wrong_header, 1); buffer.Write(wrong_header, 1);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test wrong marker // test wrong marker
buffer.Clear(); buffer.Clear();
buffer.Write(wrong_header, 2); buffer.Write(wrong_header, 2);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test wrong signature // test wrong signature
buffer.Clear(); buffer.Clear();
buffer.Write(header, 1); buffer.Write(header, 1);
buffer.Write(wrong_header, 1); buffer.Write(wrong_header, 1);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test ID wrong type // test ID wrong type
buffer.Clear(); buffer.Clear();
buffer.Write(header, 2); buffer.Write(header, 2);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test labels wrong outer type // test labels wrong outer type
buffer.Clear(); buffer.Clear();
buffer.Write(header, 2); buffer.Write(header, 2);
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test labels wrong inner type // test labels wrong inner type
buffer.Clear(); buffer.Clear();
@ -299,7 +298,7 @@ TEST(BoltDecoder, Vertex) {
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
buffer.Write(test_list, 1); buffer.Write(test_list, 1);
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test properties wrong outer type // test properties wrong outer type
buffer.Clear(); buffer.Clear();
@ -307,7 +306,7 @@ TEST(BoltDecoder, Vertex) {
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
buffer.Write(test_list, 1); buffer.Write(test_list, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
ASSERT_EQ(decoder.ReadVertex(&dv), false); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
@ -318,17 +317,18 @@ TEST(BoltDecoder, Vertex) {
buffer.Write(test_map, 1); buffer.Write(test_map, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
buffer.Write(test_int, 1); buffer.Write(test_int, 1);
ASSERT_EQ(decoder.ReadVertex(&dv), true); ASSERT_EQ(decoder.ReadValue(&dv, DecodedValue::Type::Vertex), true);
ASSERT_EQ(dv.id, 1); auto &vertex = dv.ValueVertex();
ASSERT_EQ(dv.labels[0], std::string("a")); ASSERT_EQ(vertex.id, 1);
ASSERT_EQ(dv.properties[std::string("a")].Value<int64_t>(), 1); ASSERT_EQ(vertex.labels[0], std::string("a"));
ASSERT_EQ(vertex.properties[std::string("a")].ValueInt(), 1);
} }
TEST(BoltDecoder, Edge) { TEST(BoltDecoder, Edge) {
TestDecoderBuffer buffer; TestDecoderBuffer buffer;
DecoderT decoder(buffer); DecoderT decoder(buffer);
communication::bolt::DecodedEdge de; DecodedValue de;
uint8_t header[] = "\xB5\x52"; uint8_t header[] = "\xB5\x52";
uint8_t wrong_header[] = "\x00\x00"; uint8_t wrong_header[] = "\x00\x00";
@ -341,31 +341,31 @@ TEST(BoltDecoder, Edge) {
// test missing signature // test missing signature
buffer.Clear(); buffer.Clear();
buffer.Write(wrong_header, 1); buffer.Write(wrong_header, 1);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test wrong marker // test wrong marker
buffer.Clear(); buffer.Clear();
buffer.Write(wrong_header, 2); buffer.Write(wrong_header, 2);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test wrong signature // test wrong signature
buffer.Clear(); buffer.Clear();
buffer.Write(header, 1); buffer.Write(header, 1);
buffer.Write(wrong_header, 1); buffer.Write(wrong_header, 1);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test ID wrong type // test ID wrong type
buffer.Clear(); buffer.Clear();
buffer.Write(header, 2); buffer.Write(header, 2);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test from_id wrong type // test from_id wrong type
buffer.Clear(); buffer.Clear();
buffer.Write(header, 2); buffer.Write(header, 2);
buffer.Write(test_int1, 1); buffer.Write(test_int1, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test to_id wrong type // test to_id wrong type
buffer.Clear(); buffer.Clear();
@ -373,7 +373,7 @@ TEST(BoltDecoder, Edge) {
buffer.Write(test_int1, 1); buffer.Write(test_int1, 1);
buffer.Write(test_int2, 1); buffer.Write(test_int2, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test type wrong type // test type wrong type
buffer.Clear(); buffer.Clear();
@ -382,7 +382,7 @@ TEST(BoltDecoder, Edge) {
buffer.Write(test_int2, 1); buffer.Write(test_int2, 1);
buffer.Write(test_int3, 1); buffer.Write(test_int3, 1);
buffer.Write(test_int1, 1); buffer.Write(test_int1, 1);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test properties wrong outer type // test properties wrong outer type
buffer.Clear(); buffer.Clear();
@ -392,7 +392,7 @@ TEST(BoltDecoder, Edge) {
buffer.Write(test_int3, 1); buffer.Write(test_int3, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
buffer.Write(test_int1, 1); buffer.Write(test_int1, 1);
ASSERT_EQ(decoder.ReadEdge(&de), false); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), false);
// test all ok // test all ok
buffer.Clear(); buffer.Clear();
@ -404,12 +404,13 @@ TEST(BoltDecoder, Edge) {
buffer.Write(test_map, 1); buffer.Write(test_map, 1);
buffer.Write(test_str, 2); buffer.Write(test_str, 2);
buffer.Write(test_int1, 1); buffer.Write(test_int1, 1);
ASSERT_EQ(decoder.ReadEdge(&de), true); ASSERT_EQ(decoder.ReadValue(&de, DecodedValue::Type::Edge), true);
ASSERT_EQ(de.id, 1); auto &edge = de.ValueEdge();
ASSERT_EQ(de.from, 2); ASSERT_EQ(edge.id, 1);
ASSERT_EQ(de.to, 3); ASSERT_EQ(edge.from, 2);
ASSERT_EQ(de.type, std::string("a")); ASSERT_EQ(edge.to, 3);
ASSERT_EQ(de.properties[std::string("a")].Value<int64_t>(), 1); ASSERT_EQ(edge.type, std::string("a"));
ASSERT_EQ(edge.properties[std::string("a")].ValueInt(), 1);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

View File

@ -109,20 +109,22 @@ TEST_F(RecoveryTest, TestEncoding) {
snapshot::Summary summary; snapshot::Summary summary;
buffer.Open(snapshot, summary); buffer.Open(snapshot, summary);
query::TypedValue tv; communication::bolt::DecodedValue dv;
decoder.ReadTypedValue(&tv); decoder.ReadValue(&dv);
std::vector<int64_t> ids; std::vector<int64_t> ids;
std::vector<std::string> edge_types; std::vector<std::string> edge_types;
for (int i = 0; i < summary.vertex_num_; ++i) { for (int i = 0; i < summary.vertex_num_; ++i) {
communication::bolt::DecodedVertex vertex; communication::bolt::DecodedValue vertex_dv;
decoder.ReadVertex(&vertex); decoder.ReadValue(&vertex_dv);
auto &vertex = vertex_dv.ValueVertex();
ids.push_back(vertex.id); ids.push_back(vertex.id);
} }
std::vector<int> from, to; std::vector<int> from, to;
for (int i = 0; i < summary.edge_num_; ++i) { for (int i = 0; i < summary.edge_num_; ++i) {
communication::bolt::DecodedEdge edge; communication::bolt::DecodedValue edge_dv;
decoder.ReadEdge(&edge); decoder.ReadValue(&edge_dv);
auto &edge = edge_dv.ValueEdge();
from.push_back(edge.from); from.push_back(edge.from);
to.push_back(edge.to); to.push_back(edge.to);
edge_types.push_back(edge.type); edge_types.push_back(edge.type);