Skip to content

Commit ad0db4a

Browse files
Shanyue Wanfacebook-github-bot
authored andcommitted
refactor: Add command registration pattern to AxiomSql (facebookincubator#767)
Summary: Introduces an extensible command registrating/handling system to SqlQueryRunner, replacing hardcoded if-else branches in Console with a flexible registration pattern. **Key Changes:** - Added `CommandResult` with `outcomes` vector to support multiple statement results - Added `StatementOutcome` struct with message, data, and timing fields - Move `Timing` struct and `time()` template to SqlQueryRunner - Implemented prefix-based command matching with longest-match-first semantics - Commands (exit, quit, help, savehistory, clearhistory, session) now register via `registerCommands()` - `handleCommand()` dispatches to registered handlers or the SQL executor - Console now uses `printOutcomes()` helper to reduce code duplication This enables KoskiSqlRunner and other subclasses to customize command behavior while reusing the console infrastructure. Differential Revision: D90933335
1 parent a402d14 commit ad0db4a

File tree

4 files changed

+285
-145
lines changed

4 files changed

+285
-145
lines changed

axiom/cli/Console.cpp

Lines changed: 59 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
#include "axiom/cli/Console.h"
18-
#include <sys/resource.h>
1918
#include <iostream>
2019
#include "axiom/cli/linenoise/linenoise.h"
2120

@@ -47,64 +46,7 @@ using namespace facebook::velox;
4746

4847
namespace axiom::sql {
4948

50-
void Console::initialize() {
51-
gflags::SetUsageMessage(
52-
"Axiom local SQL command line. "
53-
"Run 'axiom_sql --help' for available options.\n");
54-
55-
// Disable logging to stderr if not in debug mode.
56-
FLAGS_logtostderr = FLAGS_debug;
57-
}
58-
59-
void Console::run() {
60-
if (!FLAGS_query.empty()) {
61-
runNoThrow(FLAGS_query, false);
62-
} else {
63-
std::cout << "Axiom SQL. Type statement and end with ;.\n"
64-
"flag name = value; sets a gflag.\n"
65-
"help; prints help text."
66-
<< std::endl;
67-
readCommands("SQL> ");
68-
}
69-
}
70-
71-
namespace {
72-
struct Timing {
73-
uint64_t micros{0};
74-
uint64_t userNanos{0};
75-
uint64_t systemNanos{0};
76-
77-
std::string toString() const {
78-
double pct = 0;
79-
if (micros > 0) {
80-
pct = 100 * (userNanos + systemNanos) / (micros * 1000);
81-
}
82-
83-
std::stringstream out;
84-
out << succinctNanos(micros * 1000) << " / " << succinctNanos(userNanos)
85-
<< " user / " << succinctNanos(systemNanos) << " system (" << pct
86-
<< "%)";
87-
return out.str();
88-
}
89-
};
90-
91-
template <typename T>
92-
T time(const std::function<T()>& func, Timing& timing) {
93-
struct rusage start{};
94-
getrusage(RUSAGE_SELF, &start);
95-
SCOPE_EXIT {
96-
struct rusage end{};
97-
getrusage(RUSAGE_SELF, &end);
98-
auto tvNanos = [](struct timeval tv) {
99-
return tv.tv_sec * 1'000'000'000 + tv.tv_usec * 1'000;
100-
};
101-
timing.userNanos = tvNanos(end.ru_utime) - tvNanos(start.ru_utime);
102-
timing.systemNanos = tvNanos(end.ru_stime) - tvNanos(start.ru_stime);
103-
};
104-
105-
MicrosecondTimer timer(&timing.micros);
106-
return func();
107-
}
49+
namespace util::print {
10850

10951
int64_t countResults(const std::vector<RowVectorPtr>& results) {
11052
int64_t numRows = 0;
@@ -213,52 +155,24 @@ int32_t printResults(const std::vector<RowVectorPtr>& results) {
213155

214156
return numRows;
215157
}
216-
} // namespace
217-
218-
void Console::runNoThrow(std::string_view sql, bool isInteractive) {
219-
const SqlQueryRunner::RunOptions options{
220-
.numWorkers = FLAGS_num_workers,
221-
.numDrivers = FLAGS_num_drivers,
222-
.splitTargetBytes = FLAGS_split_target_bytes,
223-
.optimizerTraceFlags = FLAGS_optimizer_trace,
224-
.debugMode = FLAGS_debug,
225-
};
226158

227-
decltype(runner_.parseMultiple(sql, options)) statements;
228-
try {
229-
// Parse all statements upfront.
230-
statements = runner_.parseMultiple(sql, options);
231-
} catch (std::exception& e) {
232-
std::cerr << "Parse failed: " << e.what() << std::endl;
233-
return;
234-
}
235-
236-
// Execute each statement with timing.
237-
for (const auto& statement : statements) {
238-
try {
239-
Timing statementTiming;
240-
auto result = time<SqlQueryRunner::SqlResult>(
241-
[&]() { return runner_.run(*statement, options); }, statementTiming);
242-
243-
if (result.message.has_value()) {
244-
std::cout << result.message.value() << std::endl;
245-
} else {
246-
printResults(result.results);
247-
}
248-
249-
if (isInteractive) {
250-
// In interactive mode, show per-statement timing.
251-
std::cout << statementTiming.toString() << std::endl;
252-
}
253-
} catch (std::exception& e) {
254-
std::cerr << "Query failed: " << e.what() << std::endl;
255-
// Stop executing remaining statements in this block.
256-
return;
159+
void printOutcomes(const std::vector<StatementOutcome>& outcomes) {
160+
for (const auto& outcome : outcomes) {
161+
if (outcome.message.has_value()) {
162+
std::cout << outcome.message.value() << std::endl;
163+
}
164+
if (!outcome.data.empty()) {
165+
printResults(outcome.data);
166+
}
167+
if (outcome.timing.has_value()) {
168+
std::cout << outcome.timing->toString() << std::endl;
257169
}
258170
}
259171
}
260172

261-
namespace {
173+
} // namespace util::print
174+
175+
namespace util::read {
262176

263177
// Reads multi-line command from 'in' until encounters ';' followed by
264178
// zero or
@@ -315,7 +229,42 @@ std::string readCommand(const std::string& prompt, bool& atEnd) {
315229
atEnd = true;
316230
return "";
317231
}
318-
} // namespace
232+
} // namespace util::read
233+
234+
void Console::initialize() {
235+
gflags::SetUsageMessage(
236+
"Axiom local SQL command line. "
237+
"Run 'axiom_sql --help' for available options.\n");
238+
239+
// Disable logging to stderr if not in debug mode.
240+
FLAGS_logtostderr = FLAGS_debug;
241+
242+
const SqlQueryRunner::RunOptions options{
243+
.numWorkers = FLAGS_num_workers,
244+
.numDrivers = FLAGS_num_drivers,
245+
.splitTargetBytes = FLAGS_split_target_bytes,
246+
.optimizerTraceFlags = FLAGS_optimizer_trace,
247+
.debugMode = FLAGS_debug,
248+
// Interactive mode prints time elapsed for each statement.
249+
.measureTiming = FLAGS_query.empty(),
250+
.historyPath = FLAGS_data_path + "/.history",
251+
};
252+
253+
runner_.registerCommands(options);
254+
}
255+
256+
void Console::run() {
257+
if (!FLAGS_query.empty()) {
258+
auto result = runner_.handleCommand(FLAGS_query);
259+
util::print::printOutcomes(result.outcomes);
260+
} else {
261+
std::cout << "Axiom SQL. Type statement and end with ;.\n"
262+
"flag name = value; sets a gflag.\n"
263+
"help; prints help text."
264+
<< std::endl;
265+
readCommands("SQL> ");
266+
}
267+
}
319268

320269
void Console::readCommands(const std::string& prompt) {
321270
linenoiseSetMultiLine(1);
@@ -325,7 +274,7 @@ void Console::readCommands(const std::string& prompt) {
325274

326275
for (;;) {
327276
bool atEnd;
328-
std::string command = readCommand(prompt, atEnd);
277+
std::string command = util::read::readCommand(prompt, atEnd);
329278
if (atEnd) {
330279
break;
331280
}
@@ -334,21 +283,14 @@ void Console::readCommands(const std::string& prompt) {
334283
continue;
335284
}
336285

337-
if (command.starts_with("exit") || command.starts_with("quit")) {
338-
break;
339-
}
340-
341-
if (command.starts_with("help")) {
342-
static const char* helpText =
343-
"Axiom Interactive SQL\n\n"
344-
"Type SQL and end with ';'.\n"
345-
"To set a flag, type 'flag <gflag_name> = <value>;' Leave a space on either side of '='.\n\n"
346-
"Useful flags:\n\n"
347-
"num_workers - Make a distributed plan for this many workers. Runs it in-process with remote exchanges with serialization and passing data in memory. If num_workers is 1, makes single node plans without remote exchanges.\n\n"
348-
"num_drivers - Specifies the parallelism for workers. This many threads per pipeline per worker.\n\n";
286+
// Try registered commands first, then execute SQL.
287+
auto result = runner_.handleCommand(command);
349288

350-
std::cout << helpText;
351-
continue;
289+
if (result.handled) {
290+
util::print::printOutcomes(result.outcomes);
291+
}
292+
if (result.shouldExit) {
293+
break;
352294
}
353295

354296
char* flag = nullptr;
@@ -398,25 +340,7 @@ void Console::readCommands(const std::string& prompt) {
398340
}
399341
continue;
400342
}
401-
402-
if (sscanf(command.c_str(), "session %ms = %ms", &flag, &value) == 2) {
403-
std::cout << "Session '" << flag << "' set to '" << value << "'"
404-
<< std::endl;
405-
runner_.sessionConfig()[std::string(flag)] = std::string(value);
406-
continue;
407-
}
408-
409-
if (command.starts_with("savehistory")) {
410-
runner_.saveHistory(FLAGS_data_path + "/.history");
411-
continue;
412-
}
413-
414-
if (command.starts_with("clearhistory")) {
415-
runner_.clearHistory();
416-
continue;
417-
}
418-
419-
runNoThrow(command);
420343
}
421344
}
345+
422346
} // namespace axiom::sql

axiom/cli/Console.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ class Console {
3434
void run();
3535

3636
private:
37-
// Executes SQL and prints results, catching any exceptions.
38-
// @param sql The SQL text to execute, which may contain multiple
39-
// semicolon-separated statements.
40-
// @param isInteractive If true, shows timing after each statement for
41-
// multi-statement queries.
42-
void runNoThrow(std::string_view sql, bool isInteractive = true);
43-
4437
// Reads and executes commands from standard input in interactive mode.
4538
void readCommands(const std::string& prompt);
4639

0 commit comments

Comments
 (0)