summaryrefslogtreecommitdiffstats
path: root/src/command_executor.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/command_executor.cc392
1 files changed, 256 insertions, 136 deletions
diff --git a/src/command_executor.cc b/src/command_executor.cc
index 574fefb..ca62818 100644
--- a/src/command_executor.cc
+++ b/src/command_executor.cc
@@ -32,9 +32,11 @@
#include "command_executor.hh"
#include "base/ansi_scrubber.hh"
+#include "base/ansi_vars.hh"
#include "base/fs_util.hh"
#include "base/injector.hh"
#include "base/itertools.hh"
+#include "base/paths.hh"
#include "base/string_util.hh"
#include "bound_tags.hh"
#include "config.h"
@@ -45,24 +47,22 @@
#include "lnav_config.hh"
#include "lnav_util.hh"
#include "log_format_loader.hh"
+#include "prql-modules.h"
#include "readline_highlighters.hh"
#include "service_tags.hh"
#include "shlex.hh"
+#include "sql_help.hh"
#include "sql_util.hh"
#include "vtab_module.hh"
-#include "yajlpp/json_ptr.hh"
+
+#ifdef HAVE_RUST_DEPS
+# include "prqlc.cxx.hh"
+#endif
using namespace lnav::roles::literals;
exec_context INIT_EXEC_CONTEXT;
-static const std::string MSG_FORMAT_STMT = R"(
-SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
- FROM all_logs
- GROUP BY log_msg_format
- ORDER BY total DESC
-)";
-
int
sql_progress(const struct log_cursor& lc)
{
@@ -105,7 +105,7 @@ sql_progress_finished()
static Result<std::string, lnav::console::user_message> execute_from_file(
exec_context& ec,
- const ghc::filesystem::path& path,
+ const std::string& src,
int line_number,
const std::string& cmdline);
@@ -166,14 +166,7 @@ bind_sql_parameters(exec_context& ec, sqlite3_stmt* stmt)
return Err(um);
}
- ov_iter = ec.ec_override.find(name);
- if (ov_iter != ec.ec_override.end()) {
- sqlite3_bind_text(stmt,
- lpc,
- ov_iter->second.c_str(),
- ov_iter->second.length(),
- SQLITE_TRANSIENT);
- } else if (name[0] == '$') {
+ if (name[0] == '$') {
const auto& lvars = ec.ec_local_vars.top();
const auto& gvars = ec.ec_global_vars;
std::map<std::string, scoped_value_t>::const_iterator local_var,
@@ -263,14 +256,60 @@ execute_search(const std::string& search_cmd)
Result<std::string, lnav::console::user_message>
execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
{
- db_label_source& dls = lnav_data.ld_db_row_source;
+ db_label_source& dls = *(ec.ec_label_source_stack.back());
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
struct timeval start_tv, end_tv;
std::string stmt_str = trim(sql);
std::string retval;
int retcode = SQLITE_OK;
- log_info("Executing SQL: %s", sql.c_str());
+ if (lnav::sql::is_prql(stmt_str)) {
+ log_info("compiling PRQL: %s", stmt_str.c_str());
+
+#if HAVE_RUST_DEPS
+ auto opts = prqlc::Options{true, "sql.sqlite", true};
+
+ auto tree = sqlite_extension_prql;
+ for (const auto& mod : lnav_prql_modules) {
+ tree.emplace_back(prqlc::SourceTreeElement{
+ mod.get_name(),
+ mod.to_string_fragment().to_string(),
+ });
+ }
+ tree.emplace_back(prqlc::SourceTreeElement{"", stmt_str});
+ auto cr = prqlc::compile_tree(tree, opts);
+
+ for (const auto& msg : cr.messages) {
+ if (msg.kind != prqlc::MessageKind::Error) {
+ continue;
+ }
+
+ auto stmt_al = attr_line_t(stmt_str);
+ readline_sqlite_highlighter(stmt_al, 0);
+ auto um
+ = lnav::console::user_message::error(
+ attr_line_t("unable to compile PRQL: ").append(stmt_al))
+ .with_reason(
+ attr_line_t::from_ansi_str((std::string) msg.reason));
+ if (!msg.display.empty()) {
+ um.with_note(
+ attr_line_t::from_ansi_str((std::string) msg.display));
+ }
+ for (const auto& hint : msg.hints) {
+ um.with_help(attr_line_t::from_ansi_str((std::string) hint));
+ break;
+ }
+ return Err(um);
+ }
+ stmt_str = (std::string) cr.output;
+#else
+ auto um = lnav::console::user_message::error(
+ attr_line_t("PRQL is not supported in this build"));
+ return Err(um);
+#endif
+ }
+
+ log_info("Executing SQL: %s", stmt_str.c_str());
auto old_mode = lnav_data.ld_mode;
lnav_data.ld_mode = ln_mode_t::BUSY;
@@ -281,8 +320,9 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
std::vector<std::string> args;
split_ws(stmt_str, args);
- auto* sql_cmd_map = injector::get<readline_context::command_map_t*,
- sql_cmd_map_tag>();
+ const auto* sql_cmd_map
+ = injector::get<readline_context::command_map_t*,
+ sql_cmd_map_tag>();
auto cmd_iter = sql_cmd_map->find(args[0]);
if (cmd_iter != sql_cmd_map->end()) {
@@ -294,10 +334,6 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
}
}
- if (stmt_str == ".msgformats") {
- stmt_str = MSG_FORMAT_STMT;
- }
-
ec.ec_accumulator->clear();
const auto& source = ec.ec_source.back();
@@ -358,27 +394,19 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
bool done = false;
auto bound_values = TRY(bind_sql_parameters(ec, stmt.in()));
- if (lnav_data.ld_rl_view != nullptr) {
- if (lnav_data.ld_rl_view) {
- lnav_data.ld_rl_view->set_attr_value(
- lnav::console::user_message::info(
- attr_line_t("executing SQL statement, press ")
- .append("CTRL+]"_hotkey)
- .append(" to cancel"))
- .to_attr_line());
- lnav_data.ld_rl_view->do_update();
- }
- }
-
ec.ec_sql_callback(ec, stmt.in());
while (!done) {
retcode = sqlite3_step(stmt.in());
switch (retcode) {
case SQLITE_OK:
- case SQLITE_DONE:
+ case SQLITE_DONE: {
+ auto changes = sqlite3_changes(lnav_data.ld_db.in());
+
+ log_info("sqlite3_changes() -> %d", changes);
done = true;
break;
+ }
case SQLITE_ROW:
ec.ec_sql_callback(ec, stmt.in());
@@ -414,7 +442,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
log_error("sqlite3_step error code: %d", retcode);
auto um = sqlite3_error_to_user_message(lnav_data.ld_db)
- .with_snippets(ec.ec_source)
+ .with_context_snippets(ec.ec_source)
.with_note(bound_note);
return Err(um);
@@ -437,14 +465,14 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
}
lnav_data.ld_filter_view.reload_data();
lnav_data.ld_files_view.reload_data();
- lnav_data.ld_views[LNV_DB].reload_data();
- lnav_data.ld_views[LNV_DB].set_left(0);
lnav_data.ld_active_files.fc_files
| lnav::itertools::for_each(&logfile::dump_stats);
if (ec.ec_sql_callback != sql_callback) {
retval = ec.ec_accumulator->get_string();
} else if (!dls.dls_rows.empty()) {
+ lnav_data.ld_views[LNV_DB].reload_data();
+ lnav_data.ld_views[LNV_DB].set_left(0);
if (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]);
@@ -501,6 +529,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
if (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) {
+ lnav_data.ld_views[LNV_DB].reload_data();
ensure_view(&lnav_data.ld_views[LNV_DB]);
}
}
@@ -511,13 +540,73 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
return Ok(retval);
}
+Result<void, lnav::console::user_message>
+multiline_executor::push_back(string_fragment line)
+{
+ this->me_line_number += 1;
+
+ if (line.trim().empty()) {
+ if (this->me_cmdline) {
+ this->me_cmdline = this->me_cmdline.value() + "\n";
+ }
+ return Ok();
+ }
+ if (line[0] == '#') {
+ return Ok();
+ }
+
+ switch (line[0]) {
+ case ':':
+ case '/':
+ case ';':
+ case '|':
+ if (this->me_cmdline) {
+ this->me_last_result
+ = TRY(execute_from_file(this->me_exec_context,
+ this->me_source,
+ this->me_starting_line_number,
+ trim(this->me_cmdline.value())));
+ }
+
+ this->me_starting_line_number = this->me_line_number;
+ this->me_cmdline = line.to_string();
+ break;
+ default:
+ if (this->me_cmdline) {
+ this->me_cmdline = fmt::format(
+ FMT_STRING("{}{}"), this->me_cmdline.value(), line);
+ } else {
+ this->me_last_result = TRY(
+ execute_from_file(this->me_exec_context,
+ this->me_source,
+ this->me_line_number,
+ fmt::format(FMT_STRING(":{}"), line)));
+ }
+ break;
+ }
+
+ return Ok();
+}
+
+Result<std::string, lnav::console::user_message>
+multiline_executor::final()
+{
+ if (this->me_cmdline) {
+ this->me_last_result
+ = TRY(execute_from_file(this->me_exec_context,
+ this->me_source,
+ this->me_starting_line_number,
+ trim(this->me_cmdline.value())));
+ }
+
+ return Ok(this->me_last_result);
+}
+
static Result<std::string, lnav::console::user_message>
-execute_file_contents(exec_context& ec,
- const ghc::filesystem::path& path,
- bool multiline)
+execute_file_contents(exec_context& ec, const ghc::filesystem::path& path)
{
- static ghc::filesystem::path stdin_path("-");
- static ghc::filesystem::path dev_stdin_path("/dev/stdin");
+ static const ghc::filesystem::path stdin_path("-");
+ static const ghc::filesystem::path dev_stdin_path("/dev/stdin");
std::string retval;
FILE* file;
@@ -527,59 +616,22 @@ execute_file_contents(exec_context& ec,
return ec.make_error("stdin has already been consumed");
}
file = stdin;
- } else if ((file = fopen(path.c_str(), "r")) == nullptr) {
+ } else if ((file = fopen(path.c_str(), "re")) == nullptr) {
return ec.make_error("unable to open file");
}
- int line_number = 0, starting_line_number = 0;
auto_mem<char> line;
size_t line_max_size;
ssize_t line_size;
- nonstd::optional<std::string> cmdline;
+ multiline_executor me(ec, path.string());
ec.ec_path_stack.emplace_back(path.parent_path());
exec_context::output_guard og(ec);
while ((line_size = getline(line.out(), &line_max_size, file)) != -1) {
- line_number += 1;
-
- if (trim(line.in()).empty()) {
- if (multiline && cmdline) {
- cmdline = cmdline.value() + "\n";
- }
- continue;
- }
- if (line[0] == '#') {
- continue;
- }
-
- switch (line[0]) {
- case ':':
- case '/':
- case ';':
- case '|':
- if (cmdline) {
- retval = TRY(execute_from_file(
- ec, path, starting_line_number, trim(cmdline.value())));
- }
-
- starting_line_number = line_number;
- cmdline = std::string(line);
- break;
- default:
- if (multiline) {
- cmdline = fmt::format("{}{}", cmdline.value(), line.in());
- } else {
- retval = TRY(execute_from_file(
- ec, path, line_number, fmt::format(":{}", line.in())));
- }
- break;
- }
+ TRY(me.push_back(string_fragment::from_bytes(line.in(), line_size)));
}
- if (cmdline) {
- retval = TRY(execute_from_file(
- ec, path, starting_line_number, trim(cmdline.value())));
- }
+ retval = TRY(me.final());
if (file == stdin) {
if (isatty(STDOUT_FILENO)) {
@@ -594,25 +646,35 @@ execute_file_contents(exec_context& ec,
}
Result<std::string, lnav::console::user_message>
-execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
+execute_file(exec_context& ec, const std::string& path_and_args)
{
+ static const intern_string_t SRC = intern_string::lookup("cmdline");
+
available_scripts scripts;
- std::vector<std::string> split_args;
std::string retval, msg;
shlex lexer(path_and_args);
log_info("Executing file: %s", path_and_args.c_str());
- if (!lexer.split(split_args, ec.ec_local_vars.top())) {
- return ec.make_error("unable to parse path");
+ auto split_args_res = lexer.split(scoped_resolver{&ec.ec_local_vars.top()});
+ if (split_args_res.isErr()) {
+ auto split_err = split_args_res.unwrapErr();
+ auto um = lnav::console::user_message::error(
+ "unable to parse script command-line")
+ .with_reason(split_err.te_msg)
+ .with_snippet(lnav::console::snippet::from(
+ SRC, lexer.to_attr_line(split_err)));
+
+ return Err(um);
}
+ auto split_args = split_args_res.unwrap();
if (split_args.empty()) {
return ec.make_error("no script specified");
}
ec.ec_local_vars.push({});
- auto script_name = split_args[0];
+ auto script_name = split_args[0].se_value;
auto& vars = ec.ec_local_vars.top();
char env_arg_name[32];
std::string star, open_error = "file not found";
@@ -625,13 +687,13 @@ execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
vars["#"] = env_arg_name;
for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
snprintf(env_arg_name, sizeof(env_arg_name), "%lu", lpc);
- vars[env_arg_name] = split_args[lpc];
+ vars[env_arg_name] = split_args[lpc].se_value;
}
for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
if (lpc > 1) {
star.append(" ");
}
- star.append(split_args[lpc]);
+ star.append(split_args[lpc].se_value);
}
vars["__all__"] = star;
@@ -672,8 +734,7 @@ execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
if (!paths_to_exec.empty()) {
for (auto& path_iter : paths_to_exec) {
- retval
- = TRY(execute_file_contents(ec, path_iter.sm_path, multiline));
+ retval = TRY(execute_file_contents(ec, path_iter.sm_path));
}
}
ec.ec_local_vars.pop();
@@ -688,13 +749,13 @@ execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
Result<std::string, lnav::console::user_message>
execute_from_file(exec_context& ec,
- const ghc::filesystem::path& path,
+ const std::string& src,
int line_number,
const std::string& cmdline)
{
std::string retval, alt_msg;
- auto _sg = ec.enter_source(
- intern_string::lookup(path.string()), line_number, cmdline);
+ auto _sg
+ = ec.enter_source(intern_string::lookup(src), line_number, cmdline);
switch (cmdline[0]) {
case ':':
@@ -715,10 +776,8 @@ execute_from_file(exec_context& ec,
break;
}
- log_info("%s:%d:execute result -- %s",
- path.c_str(),
- line_number,
- retval.c_str());
+ log_info(
+ "%s:%d:execute result -- %s", src.c_str(), line_number, retval.c_str());
return Ok(retval);
}
@@ -726,6 +785,17 @@ execute_from_file(exec_context& ec,
Result<std::string, lnav::console::user_message>
execute_any(exec_context& ec, const std::string& cmdline_with_mode)
{
+ if (cmdline_with_mode.empty()) {
+ auto um = lnav::console::user_message::error("empty command")
+ .with_help(
+ "a command should start with ':', ';', '/', '|' and "
+ "followed by the operation to perform");
+ if (!ec.ec_source.empty()) {
+ um.with_snippet(ec.ec_source.back());
+ }
+ return Err(um);
+ }
+
std::string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
auto _cleanup = finally([&ec] {
if (ec.is_read_write() &&
@@ -734,6 +804,7 @@ execute_any(exec_context& ec, const std::string& cmdline_with_mode)
(lnav_data.ld_flags & LNF_HEADLESS || ec.ec_path_stack.size() > 1))
{
rescan_files();
+ wait_for_pipers(nonstd::nullopt);
rebuild_indexes_repeatedly();
}
});
@@ -785,11 +856,13 @@ execute_init_commands(
"");
return;
}
+ fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
fd_copy = auto_fd::dup_of(fileno(tmpout));
+ fd_copy.close_on_exec();
ec_out = std::make_pair(tmpout.release(), fclose);
}
- auto& dls = lnav_data.ld_db_row_source;
+ auto& dls = *(ec.ec_label_source_stack.back());
int option_index = 1;
{
@@ -804,6 +877,8 @@ execute_init_commands(
wait_for_children();
+ lnav_data.ld_view_stack.top() |
+ [&ec](auto* tc) { ec.ec_top_line = tc->get_selection(); };
log_debug("init cmd: %s", cmd.c_str());
{
auto _sg
@@ -828,9 +903,16 @@ execute_init_commands(
}
rescan_files();
+ auto deadline = current_timeval()
+ + ((lnav_data.ld_flags & LNF_HEADLESS)
+ ? timeval{5, 0}
+ : timeval{0, 500000});
+ wait_for_pipers(deadline);
rebuild_indexes_repeatedly();
}
- if (dls.dls_rows.size() > 1) {
+ if (dls.dls_rows.size() > 1 && lnav_data.ld_view_stack.size() == 1)
+ {
+ lnav_data.ld_views[LNV_DB].reload_data();
ensure_view(LNV_DB);
}
}
@@ -842,15 +924,20 @@ execute_init_commands(
if (ec_out && fstat(fd_copy, &st) != -1 && st.st_size > 0) {
static const auto OUTPUT_NAME = std::string("Initial command output");
- lnav_data.ld_active_files.fc_file_names[OUTPUT_NAME]
- .with_fd(std::move(fd_copy))
- .with_include_in_session(false)
- .with_detect_format(false);
- lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl);
-
- if (lnav_data.ld_rl_view != nullptr) {
- lnav_data.ld_rl_view->set_alt_value(
- HELP_MSG_1(X, "to close the file"));
+ auto create_piper_res = lnav::piper::create_looper(
+ OUTPUT_NAME, std::move(fd_copy), auto_fd{});
+ if (create_piper_res.isOk()) {
+ lnav_data.ld_active_files.fc_file_names[OUTPUT_NAME]
+ .with_piper(create_piper_res.unwrap())
+ .with_include_in_session(false)
+ .with_detect_format(false)
+ .with_init_location(0_vl);
+ lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl);
+
+ if (lnav_data.ld_rl_view != nullptr) {
+ lnav_data.ld_rl_view->set_alt_value(
+ HELP_MSG_1(X, "to close the file"));
+ }
}
}
@@ -860,7 +947,7 @@ execute_init_commands(
int
sql_callback(exec_context& ec, sqlite3_stmt* stmt)
{
- auto& dls = lnav_data.ld_db_row_source;
+ auto& dls = *(ec.ec_label_source_stack.back());
if (!sqlite3_stmt_busy(stmt)) {
dls.clear();
@@ -868,7 +955,6 @@ sql_callback(exec_context& ec, sqlite3_stmt* stmt)
return 0;
}
- auto& chart = dls.dls_chart;
auto& vc = view_colors::singleton();
int ncols = sqlite3_column_count(stmt);
int row_number;
@@ -890,16 +976,17 @@ sql_callback(exec_context& ec, sqlite3_stmt* stmt)
dls.push_header(colname, type, graphable);
if (graphable) {
+ auto& hm = dls.dls_headers.back();
auto name_for_ident_attrs = colname;
auto attrs = vc.attrs_for_ident(name_for_ident_attrs);
for (size_t attempt = 0;
- chart.attrs_in_use(attrs) && attempt < 3;
+ hm.hm_chart.attrs_in_use(attrs) && attempt < 3;
attempt++)
{
name_for_ident_attrs += " ";
attrs = vc.attrs_for_ident(name_for_ident_attrs);
}
- chart.with_attrs_for_ident(colname, attrs);
+ hm.hm_chart.with_attrs_for_ident(colname, attrs);
dls.dls_headers.back().hm_title_attrs = attrs;
}
}
@@ -963,7 +1050,7 @@ pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd)
if (out) {
FILE* file = *out;
- return std::async(std::launch::async, [&fd, file]() {
+ return std::async(std::launch::async, [fd = std::move(fd), file]() {
char buffer[1024];
ssize_t rc;
@@ -978,26 +1065,37 @@ pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd)
return std::string();
});
}
- auto tmp_fd
- = lnav::filesystem::open_temp_file(
- ghc::filesystem::temp_directory_path() / "lnav.out.XXXXXX")
- .map([](auto pair) {
- ghc::filesystem::remove(pair.first);
-
- return std::move(pair.second);
- })
- .expect("Cannot create temporary file for callback");
- auto pp
- = std::make_shared<piper_proc>(std::move(fd), false, std::move(tmp_fd));
- static int exec_count = 0;
+ std::error_code errc;
+ ghc::filesystem::create_directories(lnav::paths::workdir(), errc);
+ auto open_temp_res = lnav::filesystem::open_temp_file(lnav::paths::workdir()
+ / "exec.XXXXXX");
+ if (open_temp_res.isErr()) {
+ return lnav::futures::make_ready_future(
+ fmt::format(FMT_STRING("error: cannot open temp file -- {}"),
+ open_temp_res.unwrapErr()));
+ }
+
+ auto tmp_pair = open_temp_res.unwrap();
- lnav_data.ld_pipers.push_back(pp);
+ auto reader = std::thread(
+ [in_fd = std::move(fd), out_fd = std::move(tmp_pair.second)]() {
+ char buffer[1024];
+ ssize_t rc;
+
+ while ((rc = read(in_fd, buffer, sizeof(buffer))) > 0) {
+ write(out_fd, buffer, rc);
+ }
+ });
+ reader.detach();
+
+ static int exec_count = 0;
auto desc
= fmt::format(FMT_STRING("[{}] Output of {}"), exec_count++, cmdline);
- lnav_data.ld_active_files.fc_file_names[desc]
- .with_fd(pp->get_fd())
+ lnav_data.ld_active_files.fc_file_names[tmp_pair.first]
+ .with_filename(desc)
.with_include_in_session(false)
- .with_detect_format(false);
+ .with_detect_format(false)
+ .with_init_location(0_vl);
lnav_data.ld_files_to_front.emplace_back(desc, 0_vl);
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(X, "to close the file"));
@@ -1013,7 +1111,7 @@ add_global_vars(exec_context& ec)
shlex subber(iter.second);
std::string str;
- if (!subber.eval(str, ec.ec_global_vars)) {
+ if (!subber.eval(str, scoped_resolver{&ec.ec_global_vars})) {
log_error("Unable to evaluate global variable value: %s",
iter.second.c_str());
continue;
@@ -1071,6 +1169,28 @@ exec_context::exec_context(logline_value_vector* line_values,
void
exec_context::execute(const std::string& cmdline)
{
+ if (this->get_provenance<mouse_input>()) {
+ require(!lnav_data.ld_rl_view->is_active());
+
+ int context = 0;
+ switch (cmdline[0]) {
+ case '/':
+ context = lnav::enums::to_underlying(ln_mode_t::SEARCH);
+ break;
+ case ':':
+ context = lnav::enums::to_underlying(ln_mode_t::COMMAND);
+ break;
+ case ';':
+ context = lnav::enums::to_underlying(ln_mode_t::SQL);
+ break;
+ case '|':
+ context = lnav::enums::to_underlying(ln_mode_t::EXEC);
+ break;
+ }
+
+ lnav_data.ld_rl_view->append_to_history(context, cmdline.substr(1));
+ }
+
auto exec_res = execute_any(*this, cmdline);
if (exec_res.isErr()) {
this->ec_error_callback_stack.back()(exec_res.unwrapErr());