Skip to content

Commit 545345a

Browse files
committed
wip
1 parent 9727ed2 commit 545345a

4 files changed

Lines changed: 183 additions & 15 deletions

File tree

src/interactive.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ int Run(utils::bolt::Config &bolt_config, const std::string &history, bool no_hi
114114

115115
try {
116116
auto ret = query::ExecuteQuery(session.get(), query->query);
117-
if (ret.records.size() > 0) {
118-
Output(ret.header, ret.records, output_opts, csv_opts);
117+
if (!ret.records_as_strings.empty()) {
118+
format::Output(ret.header, ret.records_as_strings, output_opts, csv_opts);
119119
}
120120
std::string summary;
121-
if (ret.records.size() == 0) {
121+
const size_t row_count = ret.records_as_strings.size();
122+
if (row_count == 0) {
122123
summary = "Empty set";
123-
} else if (ret.records.size() == 1) {
124-
summary = std::to_string(ret.records.size()) + " row in set";
124+
} else if (row_count == 1) {
125+
summary = "1 row in set";
125126
} else {
126-
summary = std::to_string(ret.records.size()) + " rows in set";
127+
summary = std::to_string(row_count) + " rows in set";
127128
}
128129
std::printf("%s (round trip in %.3lf sec)\n", summary.c_str(), ret.wall_time.count());
129130
auto history_ret = save_history();

src/serial_import.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ int Run(const utils::bolt::Config &bolt_config, const format::CsvOptions &csv_op
3737

3838
try {
3939
auto ret = query::ExecuteQuery(session.get(), query->query);
40-
if (ret.records.size() > 0) {
41-
Output(ret.header, ret.records, output_opts, csv_opts);
40+
if (!ret.records_as_strings.empty()) {
41+
format::Output(ret.header, ret.records_as_strings, output_opts, csv_opts);
4242
}
4343
} catch (const utils::ClientQueryException &e) {
4444
console::EchoFailure("Failed query", query->query);

src/utils/utils.cpp

Lines changed: 153 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,7 @@ QueryResult ExecuteQuery(mg_session *session, const std::string &query) {
975975
QueryResult ret;
976976
mg_result *result;
977977
while ((status = mg_session_fetch(session, &result)) == 1) {
978-
ret.records.push_back(mg_memory::MakeCustomUnique<mg_list>(mg_list_copy(mg_result_row(result))));
979-
if (!ret.records.back()) {
980-
std::cerr << "out of memory";
981-
std::abort();
982-
}
978+
ret.records_as_strings.push_back(format::FormatRowToStrings(mg_result_row(result)));
983979
}
984980
if (status != 0) {
985981
if (mg_session_status(session) == MG_SESSION_BAD) {
@@ -1156,6 +1152,29 @@ void PrintRowTabular(const mg_memory::MgListPtr &data, int total_width, int colu
11561152
std::cout << data_output << std::endl;
11571153
}
11581154

1155+
void PrintRowTabular(const std::vector<std::string> &data, int total_width, int column_width, int num_columns,
1156+
bool all_columns_fit, int margin) {
1157+
if (!all_columns_fit) num_columns -= 1;
1158+
std::string data_output = std::string(total_width, ' ');
1159+
for (auto i = 0; i < total_width; i += column_width) {
1160+
data_output[i] = '|';
1161+
int idx = i / column_width;
1162+
if (idx < num_columns && idx < (int)data.size()) {
1163+
std::string field_str(data[idx]);
1164+
if ((int)field_str.size() > column_width - 2 * margin - 1) {
1165+
field_str.erase(column_width - 2 * margin - 1, std::string::npos);
1166+
field_str.replace(field_str.size() - 3, 3, "...");
1167+
}
1168+
data_output.replace(i + 1 + margin, field_str.size(), field_str);
1169+
}
1170+
}
1171+
if (!all_columns_fit) {
1172+
data_output.replace(total_width - column_width, 3, "...");
1173+
}
1174+
data_output[total_width - 1] = '|';
1175+
std::cout << data_output << std::endl;
1176+
}
1177+
11591178
void PrintTabular(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
11601179
const bool fit_to_screen) {
11611180
// lifted from replxx io.cxx
@@ -1228,6 +1247,80 @@ void PrintTabular(const std::vector<std::string> &header, const std::vector<mg_m
12281247
std::cout << line_fill << std::endl;
12291248
}
12301249

1250+
void PrintTabular(const std::vector<std::string> &header,
1251+
const std::vector<std::vector<std::string>> &records_as_strings, const bool fit_to_screen) {
1252+
auto get_screen_columns = []() {
1253+
int cols(0);
1254+
#ifdef _WIN32
1255+
HANDLE console_out = GetStdHandle(STD_OUTPUT_HANDLE);
1256+
CONSOLE_SCREEN_BUFFER_INFO inf;
1257+
GetConsoleScreenBufferInfo(console_out, &inf);
1258+
cols = inf.dwSize.X;
1259+
#else
1260+
struct winsize ws;
1261+
cols = (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) ? 80 : ws.ws_col;
1262+
#endif
1263+
return (cols > 0) ? cols : 80;
1264+
};
1265+
1266+
auto window_columns = get_screen_columns();
1267+
bool all_columns_fit = true;
1268+
auto num_columns = header.size();
1269+
auto column_width = GetMaxColumnWidth(header);
1270+
for (size_t i = 0; i < records_as_strings.size(); ++i) {
1271+
column_width = std::max(column_width, GetMaxColumnWidth(records_as_strings[i]));
1272+
}
1273+
column_width = std::max(static_cast<uint64_t>(5), column_width);
1274+
auto total_width = column_width * num_columns + 1;
1275+
1276+
if (fit_to_screen && total_width > window_columns) {
1277+
uint64_t lo = 5;
1278+
uint64_t hi = column_width;
1279+
uint64_t last = 5;
1280+
while (lo < hi) {
1281+
uint64_t mid = lo + (hi - lo) / 2;
1282+
uint64_t width = mid * num_columns + 1;
1283+
if (width <= window_columns) {
1284+
last = mid;
1285+
lo = mid + 1;
1286+
} else {
1287+
hi = mid - 1;
1288+
}
1289+
}
1290+
column_width = last;
1291+
total_width = column_width * num_columns + 1;
1292+
while (total_width > window_columns && num_columns > 1) {
1293+
num_columns -= 1;
1294+
total_width = column_width * num_columns + 1;
1295+
all_columns_fit = false;
1296+
}
1297+
}
1298+
1299+
auto line_fill = std::string(total_width, '-');
1300+
for (auto i = 0u; i < total_width; i += column_width) {
1301+
line_fill[i] = '+';
1302+
}
1303+
line_fill[total_width - 1] = '+';
1304+
std::cout << line_fill << std::endl;
1305+
PrintHeaderTabular(header, total_width, column_width, num_columns, all_columns_fit);
1306+
std::cout << line_fill << std::endl;
1307+
for (size_t i = 0; i < records_as_strings.size(); ++i) {
1308+
PrintRowTabular(records_as_strings[i], total_width, column_width, num_columns, all_columns_fit, 1);
1309+
}
1310+
std::cout << line_fill << std::endl;
1311+
}
1312+
1313+
std::vector<std::string> FormatRowToStrings(const mg_list *row) {
1314+
std::vector<std::string> out;
1315+
out.reserve(mg_list_size(row));
1316+
for (uint32_t i = 0; i < mg_list_size(row); ++i) {
1317+
std::stringstream ss;
1318+
utils::PrintValue(ss, mg_list_at(row, i));
1319+
out.push_back(ss.str());
1320+
}
1321+
return out;
1322+
}
1323+
12311324
std::vector<std::string> FormatCsvFields(const mg_memory::MgListPtr &fields, const CsvOptions &csv_opts) {
12321325
std::vector<std::string> formatted;
12331326
formatted.reserve(mg_list_size(fields.get()));
@@ -1277,6 +1370,36 @@ void PrintCsv(const std::vector<std::string> &header, const std::vector<mg_memor
12771370
}
12781371
}
12791372

1373+
static std::vector<std::string> QuoteCsvFields(const std::vector<std::string> &fields,
1374+
const CsvOptions &csv_opts) {
1375+
std::vector<std::string> out;
1376+
out.reserve(fields.size());
1377+
for (const auto &s : fields) {
1378+
std::string f(s);
1379+
if (csv_opts.doublequote) {
1380+
f = utils::Replace(f, "\"", "\"\"");
1381+
} else {
1382+
f = utils::Replace(f, "\"", csv_opts.escapechar + "\"");
1383+
}
1384+
f.insert(0, 1, '"');
1385+
f.append(1, '"');
1386+
out.push_back(std::move(f));
1387+
}
1388+
return out;
1389+
}
1390+
1391+
void PrintCsv(const std::vector<std::string> &header,
1392+
const std::vector<std::vector<std::string>> &records_as_strings, const CsvOptions &csv_opts) {
1393+
auto formatted_header = FormatCsvHeader(header, csv_opts);
1394+
utils::PrintIterable(std::cout, formatted_header, csv_opts.delimiter);
1395+
std::cout << std::endl;
1396+
for (size_t i = 0; i < records_as_strings.size(); ++i) {
1397+
auto formatted_row = QuoteCsvFields(records_as_strings[i], csv_opts);
1398+
utils::PrintIterable(std::cout, formatted_row, csv_opts.delimiter);
1399+
std::cout << std::endl;
1400+
}
1401+
}
1402+
12801403
void PrintCypherl(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records) {
12811404
if (header.size() != 1) {
12821405
std::cerr << "ERROR: cypherl output format requires exactly 1 output column" << std::endl;
@@ -1297,6 +1420,19 @@ void PrintCypherl(const std::vector<std::string> &header, const std::vector<mg_m
12971420
}
12981421
}
12991422

1423+
void PrintCypherl(const std::vector<std::string> &header,
1424+
const std::vector<std::vector<std::string>> &records_as_strings) {
1425+
if (header.size() != 1) {
1426+
std::cerr << "ERROR: cypherl output format requires exactly 1 output column" << std::endl;
1427+
std::exit(1);
1428+
}
1429+
for (const auto &row : records_as_strings) {
1430+
for (const auto &cell : row) {
1431+
std::cout << cell << std::endl;
1432+
}
1433+
}
1434+
}
1435+
13001436
void Output(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
13011437
const OutputOptions &out_opts, const CsvOptions &csv_opts) {
13021438
if (out_opts.output_format == constants::kTabularFormat) {
@@ -1308,6 +1444,18 @@ void Output(const std::vector<std::string> &header, const std::vector<mg_memory:
13081444
}
13091445
}
13101446

1447+
void Output(const std::vector<std::string> &header,
1448+
const std::vector<std::vector<std::string>> &records_as_strings, const OutputOptions &out_opts,
1449+
const CsvOptions &csv_opts) {
1450+
if (out_opts.output_format == constants::kTabularFormat) {
1451+
PrintTabular(header, records_as_strings, out_opts.fit_to_screen);
1452+
} else if (out_opts.output_format == constants::kCsvFormat) {
1453+
PrintCsv(header, records_as_strings, csv_opts);
1454+
} else if (out_opts.output_format == constants::kCypherlFormat) {
1455+
PrintCypherl(header, records_as_strings);
1456+
}
1457+
}
1458+
13111459
} // namespace format
13121460

13131461
DECLARE_bool(term_colors);

src/utils/utils.hpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ void PrintBatchesInfo(const std::vector<Batch> &);
305305

306306
struct QueryResult {
307307
std::vector<std::string> header;
308-
std::vector<mg_memory::MgListPtr> records;
308+
/// Row data as formatted strings (avoids deep copy of mg_list/mg_value).
309+
std::vector<std::vector<std::string>> records_as_strings;
309310
std::chrono::duration<double> wall_time;
310311
std::optional<std::map<std::string, std::string>> notification;
311312
std::optional<std::map<std::string, std::int64_t>> stats;
@@ -365,20 +366,38 @@ uint64_t GetMaxColumnWidth(const mg_memory::MgListPtr &data, int margin);
365366
uint64_t GetMaxColumnWidth(const std::vector<std::string> &data, int margin);
366367

367368
void PrintRowTabular(const mg_memory::MgListPtr &data, int total_width, int column_width, int num_columns,
368-
bool all_columns_fit, int margin);
369+
bool all_columns_fit, int margin);
370+
371+
void PrintRowTabular(const std::vector<std::string> &data, int total_width, int column_width, int num_columns,
372+
bool all_columns_fit, int margin);
369373

370374
void PrintTabular(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
371375
const bool fit_to_screen);
372376

377+
void PrintTabular(const std::vector<std::string> &header,
378+
const std::vector<std::vector<std::string>> &records_as_strings, const bool fit_to_screen);
379+
380+
std::vector<std::string> FormatRowToStrings(const mg_list *row);
381+
373382
std::vector<std::string> FormatCsvFields(const mg_memory::MgListPtr &fields, const CsvOptions &csv_opts);
374383

375384
std::vector<std::string> FormatCsvHeader(const std::vector<std::string> &fields, const CsvOptions &csv_opts);
376385

377386
void PrintCsv(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
378387
const CsvOptions &csv_opts);
379388

389+
void PrintCsv(const std::vector<std::string> &header,
390+
const std::vector<std::vector<std::string>> &records_as_strings, const CsvOptions &csv_opts);
391+
392+
void PrintCypherl(const std::vector<std::string> &header,
393+
const std::vector<std::vector<std::string>> &records_as_strings);
394+
380395
void Output(const std::vector<std::string> &header, const std::vector<mg_memory::MgListPtr> &records,
381396
const OutputOptions &out_opts, const CsvOptions &csv_opts);
397+
398+
void Output(const std::vector<std::string> &header,
399+
const std::vector<std::vector<std::string>> &records_as_strings, const OutputOptions &out_opts,
400+
const CsvOptions &csv_opts);
382401
} // namespace format
383402

384403
Replxx *InitAndSetupReplxx();

0 commit comments

Comments
 (0)