-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathutils.hpp
More file actions
404 lines (309 loc) · 12.5 KB
/
utils.hpp
File metadata and controls
404 lines (309 loc) · 12.5 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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// Copyright (C) 2016-2023 Memgraph Ltd. [https://memgraph.com]
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <filesystem>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#ifdef _WIN32
#define STDIN_FILENO 0
#endif /* _WIN32 */
#include "mgclient.h"
#include <functional>
#include "replxx.h"
#include "query_type.hpp"
namespace fs = std::filesystem;
namespace mg_memory {
/// Unique pointers with custom deleters for automatic memory management of
/// mg_values.
template <class T>
inline void CustomDelete(T *);
template <>
inline void CustomDelete(mg_session *session) {
mg_session_destroy(session);
}
template <>
inline void CustomDelete(mg_session_params *session_params) {
mg_session_params_destroy(session_params);
}
template <>
inline void CustomDelete(mg_list *list) {
mg_list_destroy(list);
}
template <>
inline void CustomDelete(mg_map *map) {
mg_map_destroy(map);
}
template <class T>
using CustomUniquePtr = std::unique_ptr<T, void (*)(T *)>;
template <class T>
CustomUniquePtr<T> MakeCustomUnique(T *ptr) {
return CustomUniquePtr<T>(ptr, CustomDelete<T>);
}
using MgSessionPtr = CustomUniquePtr<mg_session>;
using MgSessionParamsPtr = CustomUniquePtr<mg_session_params>;
using MgListPtr = CustomUniquePtr<mg_list>;
using MgMapPtr = CustomUniquePtr<mg_map>;
} // namespace mg_memory
namespace utils {
class ClientFatalException : public std::exception {
public:
ClientFatalException(std::string what) : what_(std::move(what)) {}
const char *what() const noexcept override { return what_.c_str(); }
private:
std::string what_;
};
class ClientQueryException : public std::exception {
public:
ClientQueryException(std::string what) : what_(std::move(what)) {}
const char *what() const noexcept override { return what_.c_str(); }
private:
std::string what_;
};
bool EnsureDir(const fs::path &dir) noexcept;
fs::path GetUserHomeDir();
/**
* return string with all uppercased characters (locale independent).
*/
std::string ToUpperCase(std::string s);
/**
* removes whitespace characters from the start and from the end of a string.
*
* @param str string that is going to be trimmed
*
* @return trimmed string
*/
std::string Trim(const std::string &s);
/**
* replaces all occurences of <match> in <src> with <replacement>.
*/
// todo: this could be implemented much more efficiently.
std::string Replace(std::string src, const std::string &match, const std::string &replacement);
/// escapes all whitespace and quotation characters to produce a string
/// which can be used as a string literal.
std::string Escape(const std::string &src);
/**
* outputs a collection of items to the given stream, separating them with the
* given delimiter.
*
* @param stream destination stream.
* @param iterable an iterable collection of items.
* @param delim delimiter that is put between items.
* @param streamer function which accepts a tstream and an item and
* streams the item to the stream.
*/
template <typename tstream, typename titerable>
inline void PrintIterable(tstream &stream, const titerable &iterable, const std::string &delim = ", ") {
bool first = true;
for (const auto &item : iterable) {
if (first)
first = false;
else
stream << delim;
stream << item;
}
}
void PrintStringUnescaped(std::ostream &os, const mg_string *str);
void PrintValue(std::ostream &os, const mg_string *str);
void PrintValue(std::ostream &os, const mg_map *map);
void PrintValue(std::ostream &os, const mg_node *node);
void PrintValue(std::ostream &os, const mg_relationship *rel);
void PrintValue(std::ostream &os, const mg_unbound_relationship *rel);
void PrintValue(std::ostream &os, const mg_path *path);
void PrintValue(std::ostream &os, const mg_date *date);
void PrintValue(std::ostream &os, const mg_local_time *local_time);
void PrintValue(std::ostream &os, const mg_local_date_time *local_date_time);
void PrintValue(std::ostream &os, const mg_duration *duration);
void PrintValue(std::ostream &os, const mg_value *value);
} // namespace utils
// Unfinished query text from previous input.
// e.g. Previous input was MATCH(n) RETURN n; MATCH
// then default_text would be set to MATCH for next query.
static std::string mgconsole_global_default_text;
// The following variables are used to track the line number and index (number specifying order) of the processed query.
[[maybe_unused]] static int64_t mgconsole_global_line_number{0};
[[maybe_unused]] static int64_t mgconsole_global_query_index{0};
namespace console {
bool is_a_tty(int fd);
void PrintHelp();
void PrintDocs();
void EchoFailure(const std::string &failure_msg, const std::string &explanation);
void EchoInfo(const std::string &message);
void EchoStats(const std::map<std::string, std::int64_t> &stats);
void EchoNotification(const std::map<std::string, std::string> ¬ification);
void EchoExecutionInfo(const std::map<std::string, double> &execution_info);
/// Helper function that sets default input for 'readline'
int SetDefaultText();
void SetStdinEcho(bool enable);
std::optional<std::string> GetLine();
struct ParseLineInfo {
query::line::CollectedClauses collected_clauses;
};
struct ParseLineResult {
std::string line;
bool is_done;
// In the case when caller is interested in more info.
std::optional<ParseLineInfo> info;
};
/// Because query can span across multiple lines.
inline ParseLineInfo MergeParseLineInfo(const ParseLineInfo &l, const ParseLineInfo &r) {
return ParseLineInfo{
.collected_clauses = query::line::MergeCollectedClauses(l.collected_clauses, r.collected_clauses),
};
}
/// Helper function that parses user line input.
/// @param line user input line.
/// @param quote quote character or '\0'; if set line is inside quotation.
/// @param escaped if set, next character should be escaped.
/// @return ParseLineResult a pair of string and bool. string is parsed line and bool marks
/// if query finished(Query finishes with ';') with this line. + optionally info about what line contains
ParseLineResult ParseLine(const std::string &line, char *quote, bool *escaped, bool collect_info = false);
/// Helper function that reads a line from the
/// standard input using the 'readline' lib.
/// Adds support for history and reverse-search.
/// @param prompt The prompt to display.
/// @return User input line, or nullopt on EOF.
std::optional<std::string> ReadLine(Replxx *replxx_instance, const std::string &prompt);
} // namespace console
namespace query {
// Interesting abstraction because multiple lines can be parsed in parallel.
struct Line {
int64_t line_number;
std::string line;
};
// NOTE: In theory it's possible to merge QueryInfo and CollectedClauses because they are the same, but it's not clear
// what would be the best, leaving as is.
struct QueryInfo {
bool has_create{false};
bool has_match{false};
bool has_merge{false};
bool has_detach_delete{false};
bool has_create_index{false};
bool has_drop_index{false};
bool has_remove{false};
bool has_storage_mode{false};
};
inline std::optional<QueryInfo> QueryInfoFromParseLineInfo(const std::optional<console::ParseLineInfo> &line_info) {
// NOTE: The logic here is correct only if there is a controlled input, change to make batched and parallel import
// non-experimental feature.
if (line_info) {
return QueryInfo{
.has_create = line_info->collected_clauses.has_create,
.has_match = line_info->collected_clauses.has_match,
.has_merge = line_info->collected_clauses.has_merge,
.has_detach_delete = line_info->collected_clauses.has_detach_delete,
.has_create_index = line_info->collected_clauses.has_create_index,
.has_drop_index = line_info->collected_clauses.has_drop_index,
.has_remove = line_info->collected_clauses.has_remove,
.has_storage_mode = line_info->collected_clauses.has_storage_mode,
};
} else {
return std::nullopt;
}
}
struct Query {
int64_t line_number{0};
int64_t index{0};
std::string query{""};
std::optional<QueryInfo> info{std::nullopt};
};
void PrintQueryInfo(const Query &);
struct Batch {
explicit Batch(int64_t capacity, int64_t index) : capacity(capacity), index(index) { queries.reserve(capacity); }
Batch() = delete;
Batch(const Batch &) = delete;
Batch &operator=(const Batch &) = delete;
Batch(Batch &&) = default;
Batch &operator=(Batch &&) = default;
int64_t capacity;
int64_t index;
std::vector<Query> queries;
bool is_executed = false;
int64_t backoff = 1;
int64_t attempts = 0;
};
void PrintBatchesInfo(const std::vector<Batch> &);
struct QueryResult {
std::vector<std::string> header;
std::vector<mg_memory::MgListPtr> records;
std::chrono::duration<double> wall_time;
std::optional<std::map<std::string, std::string>> notification;
std::optional<std::map<std::string, std::int64_t>> stats;
std::optional<std::map<std::string, double>> execution_info;
};
struct BatchResult {
bool is_executed;
std::vector<QueryResult> results;
};
// Depends on the global static string because of ...; MATCH
// The extra part is perserved for the next GetQuery call
std::optional<Query> GetQuery(Replxx *replxx_instance, bool collect_info = false);
//auto build_handler(query::QueryResult &ret, mg_session *session) -> std::function<bool(int, mg_result *)>;
struct QueryProcessor{
virtual void process_header(mg_list const *header) = 0;
virtual void process_row(mg_list const *row) =0;
virtual void process_summary(mg_map const *summary)= 0;
virtual void process_fatal() = 0;
virtual void process_query_error() = 0;
};
void ExecuteQueryEx(mg_session *session, const std::string &query, QueryProcessor & processor);
QueryResult ExecuteQuery(mg_session *session, const std::string &query);
BatchResult ExecuteBatch(mg_session *session, const Batch &batch);
} // namespace query
namespace format {
struct CsvOptions {
CsvOptions(std::string delim, std::string escape, const bool dquote)
: delimiter(std::move(delim)), escapechar(std::move(escape)), doublequote(dquote) {}
bool ValidateDoubleQuote() {
if (!doublequote && escapechar.size() != 1) {
return false;
}
return true;
}
std::string delimiter;
std::string escapechar;
bool doublequote;
};
struct OutputOptions {
OutputOptions(std::string out_format, const bool fit_to_scr)
: output_format(std::move(out_format)), fit_to_screen(fit_to_scr) {}
std::string output_format;
bool fit_to_screen;
};
void PrintHeaderTabular(const std::vector<std::string> &data, int total_width, int column_width, int num_columns,
bool all_columns_fit, int margin);
/// Helper function for determining maximum length of data.
/// @param data List of mg_values representing row.
/// @param margin Column margin width.
/// @return length needed for representing max size element in @p data list.
/// Plus one is added because of column start character '|'.
uint64_t GetMaxColumnWidth(const mg_memory::MgListPtr &data, int margin);
uint64_t GetMaxColumnWidth(const std::vector<std::string> &data, int margin);
void PrintRowTabular(const mg_memory::MgListPtr &data, int total_width, int column_width, int num_columns,
bool all_columns_fit, int margin);
void PrintTabular(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
const bool fit_to_screen);
std::vector<std::string> FormatCsvFields(const mg_memory::MgListPtr &fields, const CsvOptions &csv_opts);
std::vector<std::string> FormatCsvHeader(const std::vector<std::string> &fields, const CsvOptions &csv_opts);
void PrintCsv(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
const CsvOptions &csv_opts);
void Output(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
const OutputOptions &out_opts, const CsvOptions &csv_opts);
} // namespace format
Replxx *InitAndSetupReplxx();