-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmgclient.hpp
More file actions
301 lines (247 loc) · 8.8 KB
/
Copy pathmgclient.hpp
File metadata and controls
301 lines (247 loc) · 8.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
// Copyright (c) 2016-2020 Memgraph Ltd. [https://memgraph.com]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include "mgclient-value.hpp"
#include "mgclient.h"
namespace mg {
class MgException : public std::exception {
public:
explicit MgException(const std::string_view message) : msg_(message) {}
const char *what() const noexcept override { return msg_.c_str(); }
protected:
std::string msg_;
};
class ClientException : public MgException {
public:
explicit ClientException(const std::string_view message)
: MgException(message) {}
};
class TransientException : public MgException {
public:
explicit TransientException(const std::string_view message)
: MgException(message) {}
};
class DatabaseException : public MgException {
public:
explicit DatabaseException(const std::string_view message)
: MgException(message) {}
};
/// An interface for a Memgraph client that can execute queries and fetch
/// results.
class Client {
public:
struct Params {
std::string host = "127.0.0.1";
uint16_t port = 7687;
std::string scheme = "none";
std::string username = "";
std::string password = "";
bool use_ssl = false;
std::string user_agent = "mgclient++/" + std::string(mg_client_version());
};
Client(const Client &) = delete;
Client(Client &&) = default;
Client &operator=(const Client &) = delete;
Client &operator=(Client &&) = delete;
~Client();
/// \brief Client software version.
/// \return client version in the major.minor.patch format.
static const char *Version();
/// Initializes the client (the whole process).
/// Should be called at the beginning of each process using the client.
///
/// \return Zero if initialization was successful.
static int Init();
/// Finalizes the client (the whole process).
/// Should be called at the end of each process using the client.
static void Finalize();
/// \brief Executes the given Cypher `statement`.
/// \return true when the statement is successfully executed, false otherwise.
/// \note
/// After executing the statement, the method is blocked until all incoming
/// data (execution results) are handled, i.e. until `FetchOne` method returns
/// `std::nullopt`. Even if the result set is empty, the fetching has to be
/// done/finished to be able to execute another statement.
bool Execute(const std::string &statement);
/// \brief Executes the given Cypher `statement`, supplied with additional
/// `params`.
/// \return true when the statement is successfully executed, false
/// otherwise.
/// \note
/// After executing the statement, the method is blocked
/// until all incoming data (execution results) are handled, i.e. until
/// `FetchOne` method returns `std::nullopt`.
bool Execute(const std::string &statement, const ConstMap ¶ms);
/// \brief Fetches the next result from the input stream.
/// \return next result from the input stream.
/// If there is nothing to fetch, `std::nullopt` is returned.
std::optional<std::vector<Value>> FetchOne();
/// \brief Fetches all results and discards them.
void DiscardAll();
/// \brief Fetches all results.
std::optional<std::vector<std::vector<Value>>> FetchAll();
const std::vector<std::string> &GetColumns() const;
/// \brief Start a transaction.
/// \return true when the transaction was successfully started, false
/// otherwise.
bool BeginTransaction();
/// \brief Commit current transaction.
/// \return true when the transaction was successfully committed, false
/// otherwise.
bool CommitTransaction();
/// \brief Rollback current transaction.
/// \return true when the transaction was successfully rollbacked, false
/// otherwise.
bool RollbackTransaction();
/// \brief Static method that creates a Memgraph client instance.
/// \return pointer to the created client instance.
/// If the connection couldn't be established given the `params`, it returns
/// a `nullptr`.
static std::unique_ptr<Client> Connect(const Params ¶ms);
private:
explicit Client(mg_session *session);
mg_session *session_;
std::vector<std::string> columns_;
};
inline std::unique_ptr<Client> Client::Connect(const Client::Params ¶ms) {
mg_session_params *mg_params = mg_session_params_make();
if (!mg_params) {
return nullptr;
}
if (!params.host.empty()) {
mg_session_params_set_host(mg_params, params.host.c_str());
}
if (params.port != 0) {
mg_session_params_set_port(mg_params, params.port);
}
if (!params.scheme.empty()) {
mg_session_params_set_scheme(mg_params, params.scheme.c_str());
}
if (!params.username.empty()) {
mg_session_params_set_username(mg_params, params.username.c_str());
}
if (!params.password.empty()) {
mg_session_params_set_password(mg_params, params.password.c_str());
}
if (!params.user_agent.empty()) {
mg_session_params_set_user_agent(mg_params, params.user_agent.c_str());
}
mg_session_params_set_sslmode(
mg_params, params.use_ssl ? MG_SSLMODE_REQUIRE : MG_SSLMODE_DISABLE);
mg_session *session = nullptr;
int status = mg_connect(mg_params, &session);
mg_session_params_destroy(mg_params);
if (status < 0) {
return nullptr;
}
// Using `new` to access private constructor.
return std::unique_ptr<Client>(new Client(session));
}
inline Client::Client(mg_session *session) : session_(session) {}
inline Client::~Client() { mg_session_destroy(session_); }
inline const char *Client::Version() { return mg_client_version(); }
inline int Client::Init() { return mg_init(); }
inline void Client::Finalize() { mg_finalize(); }
inline bool Client::Execute(const std::string &statement) {
const mg_list *columns;
int status = mg_session_run(session_, statement.c_str(), nullptr, nullptr,
&columns, nullptr);
if (status < 0) {
return false;
}
status = mg_session_pull(session_, nullptr);
if (status < 0) {
return false;
}
const size_t list_length = mg_list_size(columns);
columns_.clear();
for (size_t i = 0; i < list_length; i++) {
columns_.push_back(
std::string(Value(mg_list_at(columns, i)).ValueString()));
}
return true;
}
inline bool Client::Execute(const std::string &statement,
const ConstMap ¶ms) {
const mg_list *columns;
int status = mg_session_run(session_, statement.c_str(), params.ptr(),
nullptr, &columns, nullptr);
if (status < 0) {
return false;
}
status = mg_session_pull(session_, nullptr);
if (status < 0) {
return false;
}
const size_t list_length = mg_list_size(columns);
columns_.clear();
for (size_t i = 0; i < list_length; i++) {
columns_.push_back(
std::string(Value(mg_list_at(columns, i)).ValueString()));
}
return true;
}
inline std::optional<std::vector<Value>> Client::FetchOne() {
mg_result *result;
int status = mg_session_fetch(session_, &result);
if (status == MG_ERROR_CLIENT_ERROR) {
throw ClientException(mg_session_error(session_));
}
if (status == MG_ERROR_TRANSIENT_ERROR) {
throw TransientException(mg_session_error(session_));
}
if (status == MG_ERROR_DATABASE_ERROR) {
throw DatabaseException(mg_session_error(session_));
}
if (status != 1) {
return std::nullopt;
}
std::vector<Value> values;
const mg_list *list = mg_result_row(result);
const size_t list_length = mg_list_size(list);
values.reserve(list_length);
for (size_t i = 0; i < list_length; ++i) {
values.emplace_back(Value(mg_list_at(list, i)));
}
return values;
}
inline void Client::DiscardAll() {
while (FetchOne())
;
}
inline std::optional<std::vector<std::vector<Value>>> Client::FetchAll() {
std::vector<std::vector<Value>> data;
while (auto maybe_result = FetchOne()) {
data.emplace_back(std::move(*maybe_result));
}
return data;
}
inline const std::vector<std::string> &Client::GetColumns() const {
return columns_;
}
inline bool Client::BeginTransaction() {
return mg_session_begin_transaction(session_, nullptr) == 0;
}
inline bool Client::CommitTransaction() {
mg_result *result;
return mg_session_commit_transaction(session_, &result) == 0;
}
inline bool Client::RollbackTransaction() {
mg_result *result;
return mg_session_rollback_transaction(session_, &result) == 0;
}
} // namespace mg