mirror of https://github.com/tstack/lnav.git
[session] add time_offset of files to session exports
This commit is contained in:
parent
a04dc2a327
commit
41754a8ec1
8
NEWS
8
NEWS
|
@ -64,6 +64,9 @@ lnav v0.11.0:
|
|||
current session state to a file as a list of commands/SQL
|
||||
statements. This script file can be executed to restore the
|
||||
majority of the current state.
|
||||
* Added the "echoln()" SQL function that behaves similarly to the
|
||||
":echo" command, writing its first argument to the current
|
||||
output.
|
||||
|
||||
Breaking Changes:
|
||||
* Added a 'language' column to the lnav_view_filters table that
|
||||
|
@ -92,11 +95,16 @@ lnav v0.11.0:
|
|||
(view_name, type, language, pattern) as a UNIQUE index and
|
||||
will raise a conflict error on an INSERT. Use "REPLACE INTO"
|
||||
instead of "INSERT INTO" to ignore conflict error.
|
||||
* The types of SQL values stored as local variables in scripts
|
||||
is now preserved when used as bound variables at a later point
|
||||
in the script.
|
||||
|
||||
Fixes:
|
||||
* Toggling enabled/disabled filters when there is a SQL expression
|
||||
no longer causes a crash.
|
||||
* Fix a crash related to long lines that are word wrapped.
|
||||
* Multiple SQL statements in a SQL block of a script are now
|
||||
executed instead of just the first one.
|
||||
|
||||
lnav v0.10.1:
|
||||
Features:
|
||||
|
|
|
@ -178,7 +178,7 @@ scrub_ansi_string(std::string& str, string_attrs_t& sa)
|
|||
}
|
||||
|
||||
void
|
||||
add_ansi_vars(std::map<std::string, std::string>& vars)
|
||||
add_ansi_vars(std::map<std::string, scoped_value_t>& vars)
|
||||
{
|
||||
vars["ansi_csi"] = ANSI_CSI;
|
||||
vars["ansi_norm"] = ANSI_NORM;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "attr_line.hh"
|
||||
#include "shlex.resolver.hh"
|
||||
|
||||
#define ANSI_CSI "\x1b["
|
||||
#define ANSI_CHAR_ATTR "m"
|
||||
|
@ -67,6 +68,6 @@ void scrub_ansi_string(std::string& str, string_attrs_t& sa);
|
|||
* Populate a variable map with strings that contain escape sequences that
|
||||
* might be useful to script writers.
|
||||
*/
|
||||
void add_ansi_vars(std::map<std::string, std::string>& vars);
|
||||
void add_ansi_vars(std::map<std::string, scoped_value_t>& vars);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,39 @@ SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
|
|||
ORDER BY total DESC
|
||||
)";
|
||||
|
||||
struct bind_visitor {
|
||||
bind_visitor(sqlite3_stmt* stmt, int index) : bv_stmt(stmt), bv_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const std::string& str) const
|
||||
{
|
||||
sqlite3_bind_text(this->bv_stmt,
|
||||
this->bv_index,
|
||||
str.c_str(),
|
||||
str.size(),
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
void operator()(null_value_t) const
|
||||
{
|
||||
sqlite3_bind_null(this->bv_stmt, this->bv_index);
|
||||
}
|
||||
|
||||
void operator()(int64_t value) const
|
||||
{
|
||||
sqlite3_bind_int64(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
void operator()(double value) const
|
||||
{
|
||||
sqlite3_bind_double(this->bv_stmt, this->bv_index, value);
|
||||
}
|
||||
|
||||
sqlite3_stmt* bv_stmt;
|
||||
int bv_index;
|
||||
};
|
||||
|
||||
int
|
||||
sql_progress(const struct log_cursor& lc)
|
||||
{
|
||||
|
@ -145,6 +178,112 @@ execute_command(exec_context& ec, const std::string& cmdline)
|
|||
return ec.make_error("no command to execute");
|
||||
}
|
||||
|
||||
static Result<std::map<std::string, scoped_value_t>,
|
||||
lnav::console::user_message>
|
||||
bind_sql_parameters(exec_context& ec, sqlite3_stmt* stmt)
|
||||
{
|
||||
std::map<std::string, scoped_value_t> retval;
|
||||
auto param_count = sqlite3_bind_parameter_count(stmt);
|
||||
for (int lpc = 0; lpc < param_count; lpc++) {
|
||||
std::map<std::string, std::string>::iterator ov_iter;
|
||||
const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
|
||||
if (name == nullptr) {
|
||||
auto um
|
||||
= lnav::console::user_message::error("invalid SQL statement")
|
||||
.with_reason(
|
||||
"using a question-mark (?) for bound variables "
|
||||
"is not supported, only named bound parameters "
|
||||
"are supported")
|
||||
.with_help(
|
||||
"named parameters start with a dollar-sign "
|
||||
"($) or colon (:) followed by the variable name");
|
||||
ec.add_error_context(um);
|
||||
|
||||
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] == '$') {
|
||||
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,
|
||||
global_var;
|
||||
const char* env_value;
|
||||
|
||||
if (lnav_data.ld_window) {
|
||||
char buf[32];
|
||||
int lines, cols;
|
||||
|
||||
getmaxyx(lnav_data.ld_window, lines, cols);
|
||||
if (strcmp(name, "$LINES") == 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", lines);
|
||||
sqlite3_bind_text(stmt, lpc + 1, buf, -1, SQLITE_TRANSIENT);
|
||||
} else if (strcmp(name, "$COLS") == 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", cols);
|
||||
sqlite3_bind_text(stmt, lpc + 1, buf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
if ((local_var = lvars.find(&name[1])) != lvars.end()) {
|
||||
mapbox::util::apply_visitor(bind_visitor(stmt, lpc + 1),
|
||||
local_var->second);
|
||||
retval[name] = local_var->second;
|
||||
} else if ((global_var = gvars.find(&name[1])) != gvars.end()) {
|
||||
mapbox::util::apply_visitor(bind_visitor(stmt, lpc + 1),
|
||||
global_var->second);
|
||||
retval[name] = global_var->second;
|
||||
} else if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
retval[name] = env_value;
|
||||
}
|
||||
} else if (name[0] == ':' && ec.ec_line_values != nullptr) {
|
||||
for (auto& lv : *ec.ec_line_values) {
|
||||
if (lv.lv_meta.lvm_name != &name[1]) {
|
||||
continue;
|
||||
}
|
||||
switch (lv.lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_BOOLEAN:
|
||||
sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i);
|
||||
retval[name] = fmt::to_string(lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
sqlite3_bind_double(stmt, lpc + 1, lv.lv_value.d);
|
||||
retval[name] = fmt::to_string(lv.lv_value.d);
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
sqlite3_bind_int64(stmt, lpc + 1, lv.lv_value.i);
|
||||
retval[name] = fmt::to_string(lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_NULL:
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
retval[name] = db_label_source::NULL_STR;
|
||||
break;
|
||||
default:
|
||||
sqlite3_bind_text(stmt,
|
||||
lpc + 1,
|
||||
lv.text_value(),
|
||||
lv.text_length(),
|
||||
SQLITE_TRANSIENT);
|
||||
retval[name] = lv.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sqlite3_bind_null(stmt, lpc + 1);
|
||||
log_warning("Could not bind variable: %s", name);
|
||||
retval[name] = db_label_source::NULL_STR;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(retval);
|
||||
}
|
||||
|
||||
Result<std::string, lnav::console::user_message>
|
||||
execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
||||
{
|
||||
|
@ -153,7 +292,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||
struct timeval start_tv, end_tv;
|
||||
std::string stmt_str = trim(sql);
|
||||
std::string retval;
|
||||
int retcode;
|
||||
int retcode = SQLITE_OK;
|
||||
|
||||
log_info("Executing SQL: %s", sql.c_str());
|
||||
|
||||
|
@ -163,8 +302,8 @@ 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>();
|
||||
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()) {
|
||||
|
@ -188,138 +327,58 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||
source.s_location,
|
||||
source.s_content);
|
||||
gettimeofday(&start_tv, nullptr);
|
||||
retcode = sqlite3_prepare_v2(
|
||||
lnav_data.ld_db.in(), stmt_str.c_str(), -1, stmt.out(), nullptr);
|
||||
if (retcode != SQLITE_OK) {
|
||||
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
||||
|
||||
alt_msg = "";
|
||||
return ec.make_error("{}", errmsg);
|
||||
}
|
||||
if (stmt == nullptr) {
|
||||
alt_msg = "";
|
||||
return ec.make_error("No statement given");
|
||||
}
|
||||
std::map<std::string, std::string> bound_values;
|
||||
#ifdef HAVE_SQLITE3_STMT_READONLY
|
||||
if (ec.is_read_only() && !sqlite3_stmt_readonly(stmt.in())) {
|
||||
return ec.make_error(
|
||||
"modifying statements are not allowed in this context: {}", sql);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
bool done = false;
|
||||
auto param_count = sqlite3_bind_parameter_count(stmt.in());
|
||||
for (int lpc = 0; lpc < param_count; lpc++) {
|
||||
std::map<std::string, std::string>::iterator ov_iter;
|
||||
const auto* name = sqlite3_bind_parameter_name(stmt.in(), lpc + 1);
|
||||
if (name == nullptr) {
|
||||
auto um
|
||||
= lnav::console::user_message::error(
|
||||
"invalid SQL statement")
|
||||
.with_reason(
|
||||
"using a question-mark (?) for bound variables "
|
||||
"is not supported, only named bound parameters "
|
||||
"are supported")
|
||||
.with_help(
|
||||
"named parameters start with a dollar-sign "
|
||||
"($) or colon (:) followed by the variable name");
|
||||
ec.add_error_context(um);
|
||||
|
||||
return Err(um);
|
||||
}
|
||||
|
||||
ov_iter = ec.ec_override.find(name);
|
||||
if (ov_iter != ec.ec_override.end()) {
|
||||
sqlite3_bind_text(stmt.in(),
|
||||
lpc,
|
||||
ov_iter->second.c_str(),
|
||||
ov_iter->second.length(),
|
||||
SQLITE_TRANSIENT);
|
||||
} else if (name[0] == '$') {
|
||||
const auto& lvars = ec.ec_local_vars.top();
|
||||
const auto& gvars = ec.ec_global_vars;
|
||||
std::map<std::string, std::string>::const_iterator local_var,
|
||||
global_var;
|
||||
const char* env_value;
|
||||
|
||||
if (lnav_data.ld_window) {
|
||||
char buf[32];
|
||||
int lines, cols;
|
||||
|
||||
getmaxyx(lnav_data.ld_window, lines, cols);
|
||||
if (strcmp(name, "$LINES") == 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", lines);
|
||||
sqlite3_bind_text(
|
||||
stmt.in(), lpc + 1, buf, -1, SQLITE_TRANSIENT);
|
||||
} else if (strcmp(name, "$COLS") == 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", cols);
|
||||
sqlite3_bind_text(
|
||||
stmt.in(), lpc + 1, buf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
if ((local_var = lvars.find(&name[1])) != lvars.end()) {
|
||||
sqlite3_bind_text(stmt.in(),
|
||||
lpc + 1,
|
||||
local_var->second.c_str(),
|
||||
local_var->second.length(),
|
||||
SQLITE_TRANSIENT);
|
||||
bound_values[name] = local_var->second;
|
||||
} else if ((global_var = gvars.find(&name[1])) != gvars.end()) {
|
||||
sqlite3_bind_text(stmt.in(),
|
||||
lpc + 1,
|
||||
global_var->second.c_str(),
|
||||
global_var->second.length(),
|
||||
SQLITE_TRANSIENT);
|
||||
bound_values[name] = global_var->second;
|
||||
} else if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(
|
||||
stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
bound_values[name] = env_value;
|
||||
}
|
||||
} else if (name[0] == ':' && ec.ec_line_values != nullptr) {
|
||||
for (auto& lv : *ec.ec_line_values) {
|
||||
if (lv.lv_meta.lvm_name != &name[1]) {
|
||||
continue;
|
||||
}
|
||||
switch (lv.lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_BOOLEAN:
|
||||
sqlite3_bind_int64(
|
||||
stmt.in(), lpc + 1, lv.lv_value.i);
|
||||
bound_values[name] = fmt::to_string(lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
sqlite3_bind_double(
|
||||
stmt.in(), lpc + 1, lv.lv_value.d);
|
||||
bound_values[name] = fmt::to_string(lv.lv_value.d);
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
sqlite3_bind_int64(
|
||||
stmt.in(), lpc + 1, lv.lv_value.i);
|
||||
bound_values[name] = fmt::to_string(lv.lv_value.i);
|
||||
break;
|
||||
case value_kind_t::VALUE_NULL:
|
||||
sqlite3_bind_null(stmt.in(), lpc + 1);
|
||||
bound_values[name] = db_label_source::NULL_STR;
|
||||
break;
|
||||
default:
|
||||
sqlite3_bind_text(stmt.in(),
|
||||
lpc + 1,
|
||||
lv.text_value(),
|
||||
lv.text_length(),
|
||||
SQLITE_TRANSIENT);
|
||||
bound_values[name] = lv.to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sqlite3_bind_null(stmt.in(), lpc + 1);
|
||||
log_warning("Could not bind variable: %s", name);
|
||||
bound_values[name] = db_label_source::NULL_STR;
|
||||
}
|
||||
const auto* curr_stmt = stmt_str.c_str();
|
||||
auto last_is_readonly = false;
|
||||
while (curr_stmt != nullptr) {
|
||||
const char* tail = nullptr;
|
||||
while (isspace(*curr_stmt)) {
|
||||
curr_stmt += 1;
|
||||
}
|
||||
retcode = sqlite3_prepare_v2(
|
||||
lnav_data.ld_db.in(), curr_stmt, -1, stmt.out(), &tail);
|
||||
if (retcode != SQLITE_OK) {
|
||||
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
||||
|
||||
alt_msg = "";
|
||||
|
||||
auto um = lnav::console::user_message::error(
|
||||
"failed to compile SQL statement")
|
||||
.with_reason(errmsg)
|
||||
.with_snippets(ec.ec_source);
|
||||
|
||||
auto annotated_sql = annotate_sql_with_error(
|
||||
lnav_data.ld_db.in(), curr_stmt, tail);
|
||||
auto loc = um.um_snippets.back().s_location;
|
||||
if (curr_stmt == stmt_str.c_str()) {
|
||||
um.um_snippets.pop_back();
|
||||
} else {
|
||||
auto tail_iter = stmt_str.begin();
|
||||
|
||||
std::advance(tail_iter, (curr_stmt - stmt_str.c_str()));
|
||||
loc.sl_line_number
|
||||
+= std::count(stmt_str.begin(), tail_iter, '\n');
|
||||
}
|
||||
|
||||
um.with_snippet(lnav::console::snippet::from(loc, annotated_sql));
|
||||
|
||||
return Err(um);
|
||||
}
|
||||
if (stmt == nullptr) {
|
||||
retcode = SQLITE_DONE;
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_SQLITE3_STMT_READONLY
|
||||
last_is_readonly = sqlite3_stmt_readonly(stmt.in());
|
||||
if (ec.is_read_only() && !last_is_readonly) {
|
||||
return ec.make_error(
|
||||
"modifying statements are not allowed in this context: {}",
|
||||
sql);
|
||||
}
|
||||
#endif
|
||||
bool done = false;
|
||||
|
||||
auto bound_values = TRY(bind_sql_parameters(ec, stmt.in()));
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_value("Executing query: " + sql + " ...");
|
||||
}
|
||||
|
@ -345,11 +404,18 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||
bound_note.append(
|
||||
"the bound parameters are set as follows:\n");
|
||||
for (const auto& bval : bound_values) {
|
||||
auto scrubbed_val = scrub_ws(bval.second.c_str());
|
||||
|
||||
auto val_as_str = fmt::to_string(bval.second);
|
||||
auto sql_type = bval.second.match(
|
||||
[](const std::string&) { return SQLITE_TEXT; },
|
||||
[](int64_t) { return SQLITE_INTEGER; },
|
||||
[](null_value_t) { return SQLITE_NULL; },
|
||||
[](double) { return SQLITE_FLOAT; });
|
||||
auto scrubbed_val = scrub_ws(val_as_str.c_str());
|
||||
truncate_to(scrubbed_val, 40);
|
||||
bound_note.append(" ")
|
||||
.append(lnav::roles::variable(bval.first))
|
||||
.append(":")
|
||||
.append(sqlite3_type_to_string(sql_type))
|
||||
.append(" = ")
|
||||
.append_quoted(scrubbed_val)
|
||||
.append("\n");
|
||||
|
@ -366,29 +432,11 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||
}
|
||||
}
|
||||
|
||||
if (!dls.dls_rows.empty() && !ec.ec_local_vars.empty()
|
||||
&& !ec.ec_dry_run) {
|
||||
auto& vars = ec.ec_local_vars.top();
|
||||
curr_stmt = tail;
|
||||
}
|
||||
|
||||
for (unsigned int lpc = 0; lpc < dls.dls_headers.size(); lpc++) {
|
||||
const auto& column_name = dls.dls_headers[lpc].hm_name;
|
||||
|
||||
if (sql_ident_needs_quote(column_name.c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* value = dls.dls_rows[0][lpc];
|
||||
if (value == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vars[column_name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->clear_value();
|
||||
}
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->clear_value();
|
||||
}
|
||||
|
||||
gettimeofday(&end_tv, nullptr);
|
||||
|
@ -451,7 +499,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||
}
|
||||
}
|
||||
#ifdef HAVE_SQLITE3_STMT_READONLY
|
||||
else if (sqlite3_stmt_readonly(stmt.in()))
|
||||
else if (last_is_readonly)
|
||||
{
|
||||
retval = "info: No rows matched";
|
||||
alt_msg = "";
|
||||
|
@ -802,6 +850,7 @@ sql_callback(exec_context& ec, sqlite3_stmt* stmt)
|
|||
int ncols = sqlite3_column_count(stmt);
|
||||
int row_number;
|
||||
int lpc, retval = 0;
|
||||
auto set_vars = false;
|
||||
|
||||
row_number = dls.dls_rows.size();
|
||||
dls.dls_rows.resize(row_number + 1);
|
||||
|
@ -822,10 +871,11 @@ sql_callback(exec_context& ec, sqlite3_stmt* stmt)
|
|||
chart.with_attrs_for_ident(colname, attrs);
|
||||
}
|
||||
}
|
||||
set_vars = true;
|
||||
}
|
||||
for (lpc = 0; lpc < ncols; lpc++) {
|
||||
const char* value = (const char*) sqlite3_column_text(stmt, lpc);
|
||||
db_label_source::header_meta& hm = dls.dls_headers[lpc];
|
||||
auto& hm = dls.dls_headers[lpc];
|
||||
|
||||
dls.push_column(value);
|
||||
if ((hm.hm_column_type == SQLITE_TEXT
|
||||
|
@ -841,6 +891,28 @@ sql_callback(exec_context& ec, sqlite3_stmt* stmt)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (set_vars && !ec.ec_local_vars.empty() && !ec.ec_dry_run) {
|
||||
if (sql_ident_needs_quote(hm.hm_name.c_str())) {
|
||||
continue;
|
||||
}
|
||||
auto& vars = ec.ec_local_vars.top();
|
||||
|
||||
switch (hm.hm_column_type) {
|
||||
case SQLITE_INTEGER:
|
||||
vars[hm.hm_name]
|
||||
= (int64_t) sqlite3_column_int64(stmt, lpc);
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
vars[hm.hm_name] = sqlite3_column_double(stmt, lpc);
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
vars[hm.hm_name] = null_value_t{};
|
||||
break;
|
||||
default:
|
||||
vars[hm.hm_name] = std::string(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -950,7 +1022,7 @@ exec_context::exec_context(std::vector<logline_value>* line_values,
|
|||
{
|
||||
static const auto COMMAND_SRC = intern_string::lookup("command");
|
||||
|
||||
this->ec_local_vars.push(std::map<std::string, std::string>());
|
||||
this->ec_local_vars.push(std::map<std::string, scoped_value_t>());
|
||||
this->ec_path_stack.emplace_back(".");
|
||||
this->ec_source.emplace_back(
|
||||
lnav::console::snippet::from(COMMAND_SRC, "").with_line(1));
|
||||
|
|
|
@ -219,8 +219,8 @@ struct exec_context {
|
|||
|
||||
std::map<std::string, std::string> ec_override;
|
||||
std::vector<logline_value>* ec_line_values;
|
||||
std::stack<std::map<std::string, std::string>> ec_local_vars;
|
||||
std::map<std::string, std::string> ec_global_vars;
|
||||
std::stack<std::map<std::string, scoped_value_t>> ec_local_vars;
|
||||
std::map<std::string, scoped_value_t> ec_global_vars;
|
||||
std::vector<ghc::filesystem::path> ec_path_stack;
|
||||
std::vector<lnav::console::snippet> ec_source;
|
||||
help_text* ec_current_help{nullptr};
|
||||
|
|
|
@ -52,6 +52,7 @@ CREATE TABLE lnav_file (
|
|||
inode integer, -- The inode for the file on the device.
|
||||
filepath text, -- The path to the file.
|
||||
mimetype text, -- The MIME type for the file.
|
||||
content_id text, -- The hash of some unique content in the file.
|
||||
format text, -- The log file format for the file.
|
||||
lines integer, -- The number of lines in the file.
|
||||
time_offset integer, -- The millisecond offset for timestamps.
|
||||
|
@ -95,19 +96,24 @@ CREATE TABLE lnav_file (
|
|||
to_sqlite(ctx, fmt::to_string(lf->get_text_format()));
|
||||
break;
|
||||
case 4:
|
||||
to_sqlite(ctx, format_name);
|
||||
to_sqlite(
|
||||
ctx,
|
||||
fmt::format(FMT_STRING("v1:{}"), lf->get_content_id()));
|
||||
break;
|
||||
case 5:
|
||||
to_sqlite(ctx, format_name);
|
||||
break;
|
||||
case 6:
|
||||
to_sqlite(ctx, (int64_t) lf->size());
|
||||
break;
|
||||
case 6: {
|
||||
case 7: {
|
||||
auto tv = lf->get_time_offset();
|
||||
int64_t ms = (tv.tv_sec * 1000LL) + tv.tv_usec / 1000LL;
|
||||
|
||||
to_sqlite(ctx, ms);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
case 8: {
|
||||
auto& cfg = injector::get<const file_vtab::config&>();
|
||||
auto lf_stat = lf->get_stat();
|
||||
|
||||
|
@ -179,6 +185,7 @@ CREATE TABLE lnav_file (
|
|||
int64_t inode,
|
||||
std::string path,
|
||||
const char* text_format,
|
||||
const char* content_id,
|
||||
const char* format,
|
||||
int64_t lines,
|
||||
int64_t time_offset,
|
||||
|
|
|
@ -150,7 +150,7 @@ handle_keyseq(const char* keyseq)
|
|||
auto& var_stack = ec.ec_local_vars;
|
||||
|
||||
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
|
||||
var_stack.push(std::map<std::string, std::string>());
|
||||
var_stack.push(std::map<std::string, scoped_value_t>());
|
||||
auto& vars = var_stack.top();
|
||||
vars["keyseq"] = keyseq;
|
||||
const auto& kc = iter->second;
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
:append-to /tmp/interesting-lines.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`echo`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`echo`, :ref:`echoln`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -397,12 +397,13 @@
|
|||
|
||||
.. _echo:
|
||||
|
||||
:echo *msg*
|
||||
^^^^^^^^^^^
|
||||
:echo *\[-n\]* *msg*
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Echo the given message to the screen or, if :redirect-to has been called, to output file specified in the redirect. Variable substitution is performed on the message. Use a backslash to escape any special characters, like '$'
|
||||
|
||||
**Parameters**
|
||||
* **-n** --- Do not print a line-feed at the end of the output
|
||||
* **msg\*** --- The message to display
|
||||
|
||||
**Examples**
|
||||
|
@ -413,7 +414,7 @@
|
|||
:echo Hello, World!
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -488,7 +489,7 @@
|
|||
* **path\*** --- The path to the file to write
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -900,7 +901,7 @@
|
|||
:pipe-line-to sed -e 's/foo/bar/g'
|
||||
|
||||
**See Also**
|
||||
:ref:`append_to`, :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`append_to`, :ref:`echo`, :ref:`echoln`, :ref:`export_session_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -923,7 +924,7 @@
|
|||
:pipe-to sed -e s/foo/bar/g
|
||||
|
||||
**See Also**
|
||||
:ref:`append_to`, :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`append_to`, :ref:`echo`, :ref:`echoln`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1045,7 +1046,7 @@
|
|||
:redirect-to /tmp/script-output.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1433,7 +1434,7 @@
|
|||
:write-table-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1456,7 +1457,7 @@
|
|||
:write-csv-to /tmp/table.csv
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1479,7 +1480,7 @@
|
|||
:write-json-to /tmp/table.json
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1502,7 +1503,7 @@
|
|||
:write-jsonlines-to /tmp/table.json
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1526,7 +1527,7 @@
|
|||
:write-raw-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1549,7 +1550,7 @@
|
|||
:write-screen-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1572,7 +1573,7 @@
|
|||
:write-to /tmp/interesting-lines.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -1595,7 +1596,7 @@
|
|||
:write-view-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`
|
||||
|
||||
----
|
||||
|
||||
|
|
|
@ -1042,6 +1042,22 @@ dirname(*path*)
|
|||
----
|
||||
|
||||
|
||||
.. _echoln:
|
||||
|
||||
echoln(*value*)
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Echo the argument to the current output file and return it
|
||||
|
||||
**Parameters**
|
||||
* **value\*** --- The value to write to the current output file
|
||||
|
||||
**See Also**
|
||||
:ref:`append_to`, :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
||||
.. _endswith:
|
||||
|
||||
endswith(*str*, *suffix*)
|
||||
|
|
|
@ -5629,6 +5629,11 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||
"been called, to output file specified in the redirect. "
|
||||
"Variable substitution is performed on the message. Use a "
|
||||
"backslash to escape any special characters, like '$'")
|
||||
.with_parameter(
|
||||
help_text(
|
||||
"-n",
|
||||
"Do not print a line-feed at the end of the output")
|
||||
.optional())
|
||||
.with_parameter(help_text("msg", "The message to display"))
|
||||
.with_tags({"io", "scripting"})
|
||||
.with_example({"To output 'Hello, World!'", "Hello, World!"})},
|
||||
|
|
|
@ -91,25 +91,6 @@ enum class log_footer_columns : uint32_t {
|
|||
line_hash,
|
||||
};
|
||||
|
||||
static const char*
|
||||
type_to_string(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case SQLITE_FLOAT:
|
||||
return "FLOAT";
|
||||
|
||||
case SQLITE_INTEGER:
|
||||
return "INTEGER";
|
||||
|
||||
case SQLITE_TEXT:
|
||||
return "TEXT";
|
||||
}
|
||||
|
||||
ensure("Invalid sqlite type");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string
|
||||
log_vtab_impl::get_table_statement()
|
||||
{
|
||||
|
@ -140,7 +121,7 @@ log_vtab_impl::get_table_statement()
|
|||
" %-*s %-7s %s COLLATE %-15Q,%s\n",
|
||||
max_name_len,
|
||||
colname.in(),
|
||||
type_to_string(iter->vc_type),
|
||||
sqlite3_type_to_string(iter->vc_type),
|
||||
iter->vc_hidden ? "hidden" : "",
|
||||
iter->vc_collator.empty() ? "BINARY" : iter->vc_collator.c_str(),
|
||||
comment.c_str());
|
||||
|
@ -930,7 +911,10 @@ vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
|
|||
hasher line_hasher;
|
||||
|
||||
auto outbuf
|
||||
= auto_buffer::alloc(hasher::STRING_SIZE);
|
||||
= auto_buffer::alloc(3 + hasher::STRING_SIZE);
|
||||
outbuf.push_back('v');
|
||||
outbuf.push_back('1');
|
||||
outbuf.push_back(':');
|
||||
line_hasher.update(sbr.get_data(), sbr.length())
|
||||
.update(cl)
|
||||
.to_string(outbuf);
|
||||
|
|
|
@ -229,7 +229,7 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
|
|||
std::string rewritten_line;
|
||||
|
||||
ec.with_perms(exec_context::perm_t::READ_ONLY);
|
||||
ec.ec_local_vars.push(std::map<std::string, std::string>());
|
||||
ec.ec_local_vars.push(std::map<std::string, scoped_value_t>());
|
||||
ec.ec_top_line = vis_line_t(row);
|
||||
add_ansi_vars(ec.ec_global_vars);
|
||||
add_global_vars(ec);
|
||||
|
|
|
@ -86,6 +86,26 @@ struct from_sqlite<log_filter_session_state> {
|
|||
}
|
||||
};
|
||||
|
||||
struct log_file_session_state {
|
||||
std::string lfss_content_id;
|
||||
std::string lfss_format;
|
||||
int64_t lfss_time_offset;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct from_sqlite<log_file_session_state> {
|
||||
inline log_file_session_state operator()(int argc,
|
||||
sqlite3_value** argv,
|
||||
int argi)
|
||||
{
|
||||
return {
|
||||
from_sqlite<std::string>()(argc, argv, argi + 0),
|
||||
from_sqlite<std::string>()(argc, argv, argi + 1),
|
||||
from_sqlite<int64_t>()(argc, argv, argi + 2),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
namespace lnav {
|
||||
namespace session {
|
||||
|
||||
|
@ -106,6 +126,11 @@ SELECT log_time_msecs, log_format, log_mark, log_comment, log_tags, log_line_has
|
|||
SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
||||
)";
|
||||
|
||||
static const char* FILE_QUERY = R"(
|
||||
SELECT content_id, format, time_offset FROM lnav_file
|
||||
WHERE format IS NOT NULL AND time_offset != 0
|
||||
)";
|
||||
|
||||
static const char* HEADER = R"(
|
||||
# This file is an export of an lnav session. You can type
|
||||
# '|/path/to/this/file' in lnav to execute this file and
|
||||
|
@ -114,11 +139,19 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
# The files loaded into the session were:
|
||||
)";
|
||||
|
||||
static const char* MARK_HEADER = R"(
|
||||
static constexpr const char MARK_HEADER[] = R"(
|
||||
|
||||
# The following SQL statements will restore the bookmarks,
|
||||
# comments, and tags that were added in the session.
|
||||
|
||||
;SELECT total_changes() AS before_mark_changes
|
||||
)";
|
||||
|
||||
static constexpr const char MARK_FOOTER[] = R"(
|
||||
;SELECT {} - (total_changes() - $before_mark_changes) AS failed_mark_changes
|
||||
;SELECT echoln(printf('%sERROR%s: failed to restore %d bookmarks',
|
||||
$ansi_red, $ansi_norm, $failed_mark_changes))
|
||||
WHERE $failed_mark_changes != 0
|
||||
)";
|
||||
|
||||
static const char* FILTER_HEADER = R"(
|
||||
|
@ -128,13 +161,32 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
|
||||
)";
|
||||
|
||||
console::user_message errmsg;
|
||||
static const char* FILE_HEADER = R"(
|
||||
|
||||
auto prep_res = prepare_stmt(lnav_db.in(), BOOKMARK_QUERY);
|
||||
if (prep_res.isErr()) {
|
||||
# The following SQL statements will restore the state of the
|
||||
# files in the session.
|
||||
|
||||
;SELECT total_changes() AS before_file_changes
|
||||
)";
|
||||
|
||||
static constexpr const char FILE_FOOTER[] = R"(
|
||||
;SELECT {} - (total_changes() - $before_file_changes) AS failed_file_changes
|
||||
;SELECT echoln(printf('%sERROR%s: failed to restore the state of %d files',
|
||||
$ansi_red, $ansi_norm, $failed_file_changes))
|
||||
WHERE $failed_file_changes != 0
|
||||
)";
|
||||
|
||||
static constexpr const char VIEW_HEADER[] = R"(
|
||||
|
||||
# The following commands will restore the state of the {} view.
|
||||
|
||||
)";
|
||||
|
||||
auto prep_mark_res = prepare_stmt(lnav_db.in(), BOOKMARK_QUERY);
|
||||
if (prep_mark_res.isErr()) {
|
||||
return Err(
|
||||
console::user_message::error("unable to export log bookmarks")
|
||||
.with_reason(prep_res.unwrapErr()));
|
||||
.with_reason(prep_mark_res.unwrapErr()));
|
||||
}
|
||||
|
||||
fmt::print(file, FMT_STRING("{}"), HEADER);
|
||||
|
@ -142,16 +194,14 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
fmt::print(file, FMT_STRING("# {}"), lf->get_filename());
|
||||
}
|
||||
|
||||
auto added_mark_header = false;
|
||||
auto bookmark_stmt = prep_res.unwrap();
|
||||
auto done = false;
|
||||
while (!done) {
|
||||
done = bookmark_stmt.fetch_row<log_message_session_state>().match(
|
||||
[file, &added_mark_header](const log_message_session_state& lmss) {
|
||||
if (!added_mark_header) {
|
||||
fmt::print(file, FMT_STRING("{}"), MARK_HEADER);
|
||||
added_mark_header = true;
|
||||
auto mark_count = 0;
|
||||
auto each_mark_res
|
||||
= prep_mark_res.unwrap().for_each_row<log_message_session_state>(
|
||||
[file, &mark_count](const log_message_session_state& lmss) {
|
||||
if (mark_count == 0) {
|
||||
fmt::print(file, FMT_STRING(MARK_HEADER));
|
||||
}
|
||||
mark_count += 1;
|
||||
fmt::print(file,
|
||||
FMT_STRING(";UPDATE all_logs "
|
||||
"SET log_mark = {}, "
|
||||
|
@ -167,19 +217,16 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
sqlitepp::quote(lmss.lmss_format),
|
||||
sqlitepp::quote(lmss.lmss_hash));
|
||||
return false;
|
||||
},
|
||||
[](prepared_stmt::end_of_rows) { return true; },
|
||||
[&errmsg](const prepared_stmt::fetch_error& fe) {
|
||||
errmsg
|
||||
= console::user_message::error(
|
||||
"failed to fetch bookmark metadata for log message")
|
||||
.with_reason(fe.fe_msg);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (each_mark_res.isErr()) {
|
||||
return Err(console::user_message::error(
|
||||
"failed to fetch bookmark metadata for log message")
|
||||
.with_reason(each_mark_res.unwrapErr().fe_msg));
|
||||
}
|
||||
|
||||
if (!errmsg.um_message.empty()) {
|
||||
return Err(errmsg);
|
||||
if (mark_count > 0) {
|
||||
fmt::print(file, FMT_STRING(MARK_FOOTER), mark_count);
|
||||
}
|
||||
|
||||
auto prep_filter_res = prepare_stmt(lnav_db.in(), FILTER_QUERY);
|
||||
|
@ -189,10 +236,8 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
}
|
||||
|
||||
auto added_filter_header = false;
|
||||
auto filter_stmt = prep_filter_res.unwrap();
|
||||
done = false;
|
||||
while (!done) {
|
||||
done = filter_stmt.fetch_row<log_filter_session_state>().match(
|
||||
auto each_filter_res
|
||||
= prep_filter_res.unwrap().for_each_row<log_filter_session_state>(
|
||||
[file, &added_filter_header](const log_filter_session_state& lfss) {
|
||||
if (!added_filter_header) {
|
||||
fmt::print(file, FMT_STRING("{}"), FILTER_HEADER);
|
||||
|
@ -209,18 +254,45 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
sqlitepp::quote(lfss.lfss_language),
|
||||
sqlitepp::quote(lfss.lfss_pattern));
|
||||
return false;
|
||||
},
|
||||
[](prepared_stmt::end_of_rows) { return true; },
|
||||
[&errmsg](const prepared_stmt::fetch_error& fe) {
|
||||
errmsg = console::user_message::error(
|
||||
"failed to fetch filter state for views")
|
||||
.with_reason(fe.fe_msg);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (each_filter_res.isErr()) {
|
||||
return Err(console::user_message::error(
|
||||
"failed to fetch filter state for views")
|
||||
.with_reason(each_filter_res.unwrapErr().fe_msg));
|
||||
}
|
||||
|
||||
if (!errmsg.um_message.empty()) {
|
||||
return Err(errmsg);
|
||||
auto prep_file_res = prepare_stmt(lnav_db.in(), FILE_QUERY);
|
||||
if (prep_file_res.isErr()) {
|
||||
return Err(console::user_message::error("unable to export file state")
|
||||
.with_reason(prep_file_res.unwrapErr()));
|
||||
}
|
||||
|
||||
auto file_count = 0;
|
||||
auto file_stmt = prep_file_res.unwrap();
|
||||
auto each_file_res = file_stmt.for_each_row<log_file_session_state>(
|
||||
[file, &file_count](const log_file_session_state& lfss) {
|
||||
if (file_count == 0) {
|
||||
fmt::print(file, FMT_STRING("{}"), FILE_HEADER);
|
||||
}
|
||||
file_count += 1;
|
||||
fmt::print(file,
|
||||
FMT_STRING(";UPDATE lnav_file "
|
||||
"SET time_offset = {} "
|
||||
"WHERE content_id = {} AND format = {}\n"),
|
||||
lfss.lfss_time_offset,
|
||||
sqlitepp::quote(lfss.lfss_content_id),
|
||||
sqlitepp::quote(lfss.lfss_format));
|
||||
return false;
|
||||
});
|
||||
|
||||
if (each_file_res.isErr()) {
|
||||
return Err(console::user_message::error("failed to fetch file state")
|
||||
.with_reason(each_file_res.unwrapErr().fe_msg));
|
||||
}
|
||||
|
||||
if (file_count > 0) {
|
||||
fmt::print(file, FMT_STRING(FILE_FOOTER), file_count);
|
||||
}
|
||||
|
||||
for (auto view_index : {LNV_LOG, LNV_TEXT}) {
|
||||
|
@ -229,10 +301,7 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
continue;
|
||||
}
|
||||
|
||||
fmt::print(file,
|
||||
FMT_STRING("\n# The following commands will restore the"
|
||||
"\n# state of the {} view.\n\n"),
|
||||
lnav_view_titles[view_index]);
|
||||
fmt::print(file, FMT_STRING(VIEW_HEADER), lnav_view_titles[view_index]);
|
||||
fmt::print(file,
|
||||
FMT_STRING(":switch-to-view {}\n"),
|
||||
lnav_view_strings[view_index]);
|
||||
|
@ -244,7 +313,7 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
|
||||
if (min_level != LEVEL_UNKNOWN) {
|
||||
fmt::print(file,
|
||||
FMT_STRING(":set-min-log-level {}"),
|
||||
FMT_STRING(":set-min-log-level {}\n"),
|
||||
level_names[min_level]);
|
||||
}
|
||||
|
||||
|
@ -252,11 +321,11 @@ SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters
|
|||
char tsbuf[128];
|
||||
if (lss->get_min_log_time(min_time)) {
|
||||
sql_strftime(tsbuf, sizeof(tsbuf), min_time, 'T');
|
||||
fmt::print(file, FMT_STRING(":hide-lines-before {}"), tsbuf);
|
||||
fmt::print(file, FMT_STRING(":hide-lines-before {}\n"), tsbuf);
|
||||
}
|
||||
if (lss->get_max_log_time(max_time)) {
|
||||
sql_strftime(tsbuf, sizeof(tsbuf), max_time, 'T');
|
||||
fmt::print(file, FMT_STRING(":hide-lines-after {}"), tsbuf);
|
||||
fmt::print(file, FMT_STRING(":hide-lines-after {}\n"), tsbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
src/shlex.hh
14
src/shlex.hh
|
@ -100,12 +100,11 @@ public:
|
|||
int extra = token == shlex_token_t::ST_VARIABLE_REF ? 0 : 1;
|
||||
std::string var_name(&this->s_str[cap.c_begin + 1 + extra],
|
||||
cap.length() - 1 - extra * 2);
|
||||
std::map<std::string, std::string>::const_iterator
|
||||
local_var;
|
||||
auto local_var = vars.find(var_name);
|
||||
const char* var_value = getenv(var_name.c_str());
|
||||
|
||||
if ((local_var = vars.find(var_name)) != vars.end()) {
|
||||
result.append(local_var->second);
|
||||
if (local_var != vars.end()) {
|
||||
result.append(fmt::to_string(local_var->second));
|
||||
} else if (var_value != nullptr) {
|
||||
result.append(var_value);
|
||||
}
|
||||
|
@ -167,12 +166,11 @@ public:
|
|||
int extra = token == shlex_token_t::ST_VARIABLE_REF ? 0 : 1;
|
||||
std::string var_name(&this->s_str[cap.c_begin + 1 + extra],
|
||||
cap.length() - 1 - extra * 2);
|
||||
std::map<std::string, std::string>::const_iterator
|
||||
local_var;
|
||||
auto local_var = vars.find(var_name);
|
||||
const char* var_value = getenv(var_name.c_str());
|
||||
|
||||
if ((local_var = vars.find(var_name)) != vars.end()) {
|
||||
result.back().append(local_var->second);
|
||||
if (local_var != vars.end()) {
|
||||
result.back().append(fmt::to_string(local_var->second));
|
||||
} else if (var_value != nullptr) {
|
||||
result.back().append(var_value);
|
||||
}
|
||||
|
|
|
@ -36,35 +36,60 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "mapbox/variant.hpp"
|
||||
|
||||
struct null_value_t {};
|
||||
using scoped_value_t
|
||||
= mapbox::util::variant<std::string, int64_t, double, null_value_t>;
|
||||
|
||||
namespace fmt {
|
||||
template<>
|
||||
struct formatter<scoped_value_t> : formatter<std::string> {
|
||||
template<typename FormatContext>
|
||||
auto format(const scoped_value_t& sv, FormatContext& ctx) const
|
||||
{
|
||||
auto retval
|
||||
= sv.match([](std::string str) { return str; },
|
||||
[](null_value_t) { return std::string("<NULL>"); },
|
||||
[](int64_t value) { return fmt::to_string(value); },
|
||||
[](double value) { return fmt::to_string(value); });
|
||||
|
||||
return fmt::formatter<std::string>::format(retval, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
class scoped_resolver {
|
||||
public:
|
||||
scoped_resolver(
|
||||
std::initializer_list<std::map<std::string, std::string>*> l)
|
||||
std::initializer_list<std::map<std::string, scoped_value_t>*> l)
|
||||
{
|
||||
this->sr_stack.insert(this->sr_stack.end(), l.begin(), l.end());
|
||||
};
|
||||
}
|
||||
|
||||
typedef std::map<std::string, std::string>::const_iterator const_iterator;
|
||||
using const_iterator
|
||||
= std::map<std::string, scoped_value_t>::const_iterator;
|
||||
|
||||
const_iterator find(const std::string& str) const
|
||||
{
|
||||
const_iterator retval;
|
||||
|
||||
for (auto scope : this->sr_stack) {
|
||||
for (const auto* scope : this->sr_stack) {
|
||||
if ((retval = scope->find(str)) != scope->end()) {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return this->end();
|
||||
};
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return this->sr_stack.back()->end();
|
||||
}
|
||||
|
||||
std::vector<const std::map<std::string, std::string>*> sr_stack;
|
||||
std::vector<const std::map<std::string, scoped_value_t>*> sr_stack;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
117
src/sql_util.cc
117
src/sql_util.cc
|
@ -676,6 +676,55 @@ sql_safe_ident(const string_fragment& ident)
|
|||
return retval;
|
||||
}
|
||||
|
||||
attr_line_t
|
||||
annotate_sql_with_error(sqlite3* db, const char* sql, const char* tail)
|
||||
{
|
||||
const auto* errmsg = sqlite3_errmsg(db);
|
||||
attr_line_t retval;
|
||||
int erroff = -1;
|
||||
|
||||
#if defined(HAVE_SQLITE3_ERROR_OFFSET)
|
||||
erroff = sqlite3_error_offset(db);
|
||||
#endif
|
||||
if (tail != nullptr) {
|
||||
const auto* tail_lf = strchr(tail, '\n');
|
||||
if (tail_lf == nullptr) {
|
||||
tail = tail + strlen(tail);
|
||||
} else {
|
||||
tail = tail_lf;
|
||||
}
|
||||
retval.append(string_fragment{sql, 0, (int) (tail - sql)});
|
||||
} else {
|
||||
retval.append(sql);
|
||||
}
|
||||
if (erroff >= retval.length()) {
|
||||
erroff -= 1;
|
||||
}
|
||||
if (erroff != -1 && !endswith(retval.get_string(), "\n")) {
|
||||
retval.append("\n");
|
||||
}
|
||||
retval.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
|
||||
readline_sqlite_highlighter(retval, retval.length());
|
||||
|
||||
if (erroff != -1) {
|
||||
auto line_with_error
|
||||
= string_fragment(retval.get_string())
|
||||
.find_boundaries_around(erroff, string_fragment::tag1{'\n'});
|
||||
auto erroff_in_line = erroff - line_with_error.sf_begin;
|
||||
|
||||
attr_line_t pointer;
|
||||
|
||||
pointer.append(erroff_in_line, ' ')
|
||||
.append("^ "_snippet_border)
|
||||
.append(lnav::roles::error(errmsg))
|
||||
.append("\n");
|
||||
|
||||
retval.insert(line_with_error.sf_end + 1, pointer).rtrim();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
sql_compile_script(sqlite3* db,
|
||||
const char* src_name,
|
||||
|
@ -703,51 +752,8 @@ sql_compile_script(sqlite3* db,
|
|||
retcode = sqlite3_prepare_v2(db, script, -1, stmt.out(), &tail);
|
||||
log_debug("retcode %d %p %p", retcode, script, tail);
|
||||
if (retcode != SQLITE_OK) {
|
||||
const char* errmsg = sqlite3_errmsg(db);
|
||||
int erroff = -1;
|
||||
attr_line_t sql_content;
|
||||
|
||||
#if defined(HAVE_SQLITE3_ERROR_OFFSET)
|
||||
erroff = sqlite3_error_offset(db);
|
||||
#endif
|
||||
if (tail != nullptr) {
|
||||
const auto* tail_lf = strchr(tail, '\n');
|
||||
if (tail_lf == nullptr) {
|
||||
tail = tail + strlen(tail);
|
||||
} else {
|
||||
tail = tail_lf;
|
||||
}
|
||||
sql_content.append(
|
||||
string_fragment{script, 0, (int) (tail - script)});
|
||||
} else {
|
||||
sql_content.append(script);
|
||||
}
|
||||
if (erroff >= sql_content.length()) {
|
||||
erroff -= 1;
|
||||
}
|
||||
if (erroff != -1 && !endswith(sql_content.get_string(), "\n")) {
|
||||
sql_content.append("\n");
|
||||
}
|
||||
sql_content.with_attr_for_all(
|
||||
VC_ROLE.value(role_t::VCR_QUOTED_CODE));
|
||||
readline_sqlite_highlighter(sql_content, sql_content.length());
|
||||
|
||||
if (erroff != -1) {
|
||||
auto line_with_error
|
||||
= string_fragment(sql_content.get_string())
|
||||
.find_boundaries_around(erroff,
|
||||
string_fragment::tag1{'\n'});
|
||||
auto erroff_in_line = erroff - line_with_error.sf_begin;
|
||||
|
||||
attr_line_t pointer;
|
||||
|
||||
pointer.append(erroff_in_line, ' ')
|
||||
.append("^ "_snippet_border)
|
||||
.append(lnav::roles::error(errmsg))
|
||||
.append("\n");
|
||||
|
||||
sql_content.insert(line_with_error.sf_end + 1, pointer).rtrim();
|
||||
}
|
||||
const auto* errmsg = sqlite3_errmsg(db);
|
||||
auto sql_content = annotate_sql_with_error(db, script, tail);
|
||||
|
||||
errors.emplace_back(
|
||||
lnav::console::user_message::error(
|
||||
|
@ -923,6 +929,27 @@ guess_type_from_pcre(const std::string& pattern, std::string& collator)
|
|||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
sqlite3_type_to_string(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case SQLITE_FLOAT:
|
||||
return "FLOAT";
|
||||
case SQLITE_INTEGER:
|
||||
return "INTEGER";
|
||||
case SQLITE_TEXT:
|
||||
return "TEXT";
|
||||
case SQLITE_NULL:
|
||||
return "NULL";
|
||||
case SQLITE_BLOB:
|
||||
return "BLOB";
|
||||
}
|
||||
|
||||
ensure("Invalid sqlite type");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* XXX figure out how to do this with the template */
|
||||
void
|
||||
sqlite_close_wrapper(void* mem)
|
||||
|
|
|
@ -118,6 +118,12 @@ void sql_execute_script(sqlite3* db,
|
|||
|
||||
int guess_type_from_pcre(const std::string& pattern, std::string& collator);
|
||||
|
||||
const char* sqlite3_type_to_string(int type);
|
||||
|
||||
attr_line_t annotate_sql_with_error(sqlite3* db,
|
||||
const char* sql,
|
||||
const char* tail);
|
||||
|
||||
int sqlite_authorizer(void* pUserData,
|
||||
int action_code,
|
||||
const char* detail1,
|
||||
|
|
|
@ -157,6 +157,29 @@ struct prepared_stmt {
|
|||
};
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
Result<void, fetch_error> for_each_row(F func)
|
||||
{
|
||||
nonstd::optional<fetch_error> err;
|
||||
auto done = false;
|
||||
|
||||
while (!done) {
|
||||
done = this->template fetch_row<T>().match(
|
||||
func,
|
||||
[](end_of_rows) { return true; },
|
||||
[&err](const fetch_error& fe) {
|
||||
err = fe;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return Err(err.value());
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
auto_mem<sqlite3_stmt> ps_stmt;
|
||||
};
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ sql_lnav_top_file()
|
|||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
auto top_view = top_view_opt.value();
|
||||
auto* top_view = top_view_opt.value();
|
||||
return top_view->map_top_row([](const auto& al) {
|
||||
return get_string_attr(al.get_attrs(), logline::L_FILE) |
|
||||
[](const auto wrapper) {
|
||||
|
@ -104,6 +104,24 @@ sql_error(const char* str)
|
|||
throw sqlite_func_error("{}", str);
|
||||
}
|
||||
|
||||
static nonstd::optional<std::string>
|
||||
sql_echoln(nonstd::optional<std::string> arg)
|
||||
{
|
||||
if (arg) {
|
||||
auto& ec = lnav_data.ld_exec_context;
|
||||
auto outfile = ec.get_output();
|
||||
|
||||
if (outfile) {
|
||||
fmt::print(outfile.value(), FMT_STRING("{}\n"), arg.value());
|
||||
if (outfile.value() == stdout) {
|
||||
lnav_data.ld_stdout_used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
int
|
||||
state_extension_functions(struct FuncDef** basic_funcs,
|
||||
struct FuncDefAgg** agg_funcs)
|
||||
|
@ -140,6 +158,16 @@ state_extension_functions(struct FuncDef** basic_funcs,
|
|||
.with_parameter({"msg", "The error message"}))
|
||||
.with_flags(SQLITE_UTF8),
|
||||
|
||||
sqlite_func_adapter<decltype(&sql_echoln), sql_echoln>::builder(
|
||||
help_text("echoln",
|
||||
"Echo the argument to the current output file and return "
|
||||
"it")
|
||||
.sql_function()
|
||||
.with_parameter(
|
||||
{"value", "The value to write to the current output file"})
|
||||
.with_tags({"io"}))
|
||||
.with_flags(SQLITE_UTF8),
|
||||
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
|
|
|
@ -316,10 +316,7 @@ to_sqlite(sqlite3_context* ctx, const nonstd::optional<T>& val)
|
|||
}
|
||||
|
||||
struct ToSqliteVisitor {
|
||||
ToSqliteVisitor(sqlite3_context* vctx)
|
||||
: tsv_context(vctx){
|
||||
|
||||
};
|
||||
ToSqliteVisitor(sqlite3_context* vctx) : tsv_context(vctx) {}
|
||||
|
||||
template<typename T>
|
||||
void operator()(T&& t) const
|
||||
|
|
|
@ -520,6 +520,8 @@ EXPECTED_FILES = \
|
|||
$(srcdir)/%reldir%/test_sql.sh_c7e1dbf4605914720b55787785abfafdf2c4178a.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_cc77a633a66d1778705a34e3657737547b3fb08d.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_cc77a633a66d1778705a34e3657737547b3fb08d.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_dd540973a0dc86320d84706845a15608196ae5be.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_dd540973a0dc86320d84706845a15608196ae5be.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_e70dc7d2b686c7f91c2b41b10f3920c50f3ea405.out \
|
||||
$(srcdir)/%reldir%/test_sql_anno.sh_028d5d5af2f3519b59d349d41cb7ecf385253b51.err \
|
||||
|
|
|
@ -726,7 +726,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[4mSee Also[0m
|
||||
[1m:echo[0m, [1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:redirect-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To append marked lines to the file /tmp/interesting-lines.txt:
|
||||
[37m[40m:[0m[1m[36m[40mappend-to[0m[37m[40m /tmp/interesting-lines.txt [0m
|
||||
|
@ -929,13 +929,14 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[4mSee Also[0m
|
||||
[1m:enable-word-wrap[0m, [1m:hide-fields[0m, [1m:highlight[0m
|
||||
|
||||
[4m:[0m[1m[4mecho[0m[4m [0m[4mmsg[0m
|
||||
[4m:[0m[1m[4mecho[0m[4m [[0m[4m-n[0m[4m] [0m[4mmsg[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Echo the given message to the screen or, if :redirect-to has been
|
||||
called, to output file specified in the redirect. Variable
|
||||
substitution is performed on the message. Use a backslash to escape
|
||||
any special characters, like '$'
|
||||
[4mParameter[0m
|
||||
[4mParameters[0m
|
||||
[4m-n[0m Do not print a line-feed at the end of the output
|
||||
[4mmsg[0m The message to display
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
|
@ -943,7 +944,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To output 'Hello, World!':
|
||||
[37m[40m:[0m[1m[36m[40mecho[0m[37m[40m Hello, World! [0m
|
||||
|
@ -998,7 +999,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
|
||||
[4m:[0m[1m[4mfilter-expr[0m[4m [0m[4mexpr[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
|
@ -1250,7 +1251,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[4mSee Also[0m
|
||||
[1m:append-to[0m, [1m:echo[0m, [1m:export-session-to[0m, [1m:pipe-to[0m, [1m:redirect-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write the top line to 'sed' for processing:
|
||||
[37m[40m:[0m[1m[36m[40mpipe-line-to[0m[37m[40m sed -e 's/foo/bar/g' [0m
|
||||
|
@ -1265,7 +1266,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[4mSee Also[0m
|
||||
[1m:append-to[0m, [1m:echo[0m, [1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:redirect-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write marked lines to 'sed' for processing:
|
||||
[37m[40m:[0m[1m[36m[40mpipe-to[0m[37m[40m sed -e s/foo/bar/g [0m
|
||||
|
@ -1343,7 +1344,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write the output of lnav commands to the file /tmp/script-output.txt:
|
||||
[37m[40m:[0m[1m[36m[40mredirect-to[0m[37m[40m /tmp/script-output.txt [0m
|
||||
|
@ -1573,7 +1574,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write SQL results as text to /tmp/table.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-table-to[0m[37m[40m /tmp/table.txt [0m
|
||||
|
@ -1593,7 +1594,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write SQL results as CSV to /tmp/table.csv:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-csv-to[0m[37m[40m /tmp/table.csv [0m
|
||||
|
@ -1613,7 +1614,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m,
|
||||
[1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write SQL results as JSON to /tmp/table.json:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-json-to[0m[37m[40m /tmp/table.json [0m
|
||||
|
@ -1633,7 +1634,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m,
|
||||
[1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write SQL results as JSON Lines to /tmp/table.json:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-jsonlines-to[0m[37m[40m /tmp/table.json [0m
|
||||
|
@ -1657,7 +1658,8 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m,
|
||||
[1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write the marked lines in the log view to /tmp/table.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-raw-to[0m[37m[40m /tmp/table.txt [0m
|
||||
|
@ -1678,7 +1680,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write only the displayed text to /tmp/table.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-screen-to[0m[37m[40m /tmp/table.txt [0m
|
||||
|
@ -1698,7 +1700,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write SQL results as text to /tmp/table.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-table-to[0m[37m[40m /tmp/table.txt [0m
|
||||
|
@ -1716,7 +1718,8 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:redirect-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m,
|
||||
[1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write marked lines to the file /tmp/interesting-lines.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-to[0m[37m[40m /tmp/interesting-lines.txt [0m
|
||||
|
@ -1737,7 +1740,7 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-to[0m
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write the top view to /tmp/table.txt:
|
||||
[37m[40m:[0m[1m[36m[40mwrite-view-to[0m[37m[40m /tmp/table.txt [0m
|
||||
|
@ -2189,6 +2192,17 @@ lnav@googlegroups.com[1] support@lnav.org[2]
|
|||
|
||||
|
||||
|
||||
[1m[4mecholn[0m[4m([0m[4mvalue[0m[4m)[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Echo the argument to the current output file and return it
|
||||
[4mParameter[0m
|
||||
[4mvalue[0m The value to write to the current output file
|
||||
[4mSee Also[0m
|
||||
[1m:append-to[0m, [1m:echo[0m, [1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m,
|
||||
[1m:redirect-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-view-to[0m
|
||||
|
||||
[1m[4mendswith[0m[4m([0m[4mstr[0m[4m, [0m[4msuffix[0m[4m)[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Test if a string ends with the given suffix
|
||||
|
|
|
@ -9,10 +9,16 @@
|
|||
# The following SQL statements will restore the bookmarks,
|
||||
# comments, and tags that were added in the session.
|
||||
|
||||
;UPDATE all_logs SET log_mark = 1, log_comment = NULL, log_tags = NULL WHERE log_time_msecs = 1248130769000 AND log_format = 'access_log' AND log_line_hash = 'b05c1bdfe75cde41e151c89087e31951'
|
||||
;SELECT total_changes() AS before_mark_changes
|
||||
;UPDATE all_logs SET log_mark = 1, log_comment = NULL, log_tags = NULL WHERE log_time_msecs = 1248130769000 AND log_format = 'access_log' AND log_line_hash = 'v1:b05c1bdfe75cde41e151c89087e31951'
|
||||
|
||||
# The following commands will restore the
|
||||
# state of the LOG view.
|
||||
;SELECT 1 - (total_changes() - $before_mark_changes) AS failed_mark_changes
|
||||
;SELECT echoln(printf('%sERROR%s: failed to restore %d bookmarks',
|
||||
$ansi_red, $ansi_norm, $failed_mark_changes))
|
||||
WHERE $failed_mark_changes != 0
|
||||
|
||||
|
||||
# The following commands will restore the state of the LOG view.
|
||||
|
||||
:switch-to-view log
|
||||
:goto 1
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: no such table: nonexistent_table
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: no such table: nonexistent_table
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mselect[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mfrom[0m[37m[40m [0m[37m[40mnonexistent_table[0m[37m[40m [0m
|
||||
[36m | [0m[1m[36m[40mselect[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mfrom[0m[37m[40m nonexistent_table [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: the stop parameter is required
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: the stop parameter is required
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mFROM[0m[37m[40m [0m[1m[37m[40mgenerate_series[0m[37m[40m([0m[1m[37m[40m1[0m[37m[40m) [0m
|
||||
[36m | [0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mFROM[0m[37m[40m [0m[1m[37m[40mgenerate_series[0m[37m[40m([0m[1m[37m[40m1[0m[37m[40m) [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: not authorized
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: not authorized
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'/tmp/memdb'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
[36m | [0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'/tmp/memdb'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: not authorized
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: not authorized
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'simple-db.db'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
[36m | [0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'simple-db.db'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: not authorized
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: not authorized
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m':memdb:'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
[36m | [0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m':memdb:'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m[37m[40m [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: the start parameter is required
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: the start parameter is required
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mFROM[0m[37m[40m [0m[1m[37m[40mgenerate_series[0m[37m[40m() [0m
|
||||
[36m | [0m[1m[36m[40mSELECT[0m[37m[40m [0m[1m[37m[40m*[0m[37m[40m [0m[1m[36m[40mFROM[0m[37m[40m [0m[1m[37m[40mgenerate_series[0m[37m[40m() [0m
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[1m[31m✘ error[0m: not authorized
|
||||
[1m[31m✘ error[0m: failed to compile SQL statement
|
||||
[1m[31mreason[0m: not authorized
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'file:memdb?cache=shared'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m
|
||||
[36m | [0m[1m[36m[40mattach[0m[37m[40m [0m[1m[36m[40mdatabase[0m[37m[40m [0m[35m[40m'file:memdb?cache=shared'[0m[37m[40m [0m[1m[36m[40mas[0m[37m[40m [0m[35m[40m'db'[0m
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[1m[31m✘ error[0m: call to [1m[4mraise_error[0m[4m([0m[4mmsg[0m[4m)[0m failed
|
||||
[1m[31mreason[0m: oops!
|
||||
[36m --> [0m[1mcommand-option[0m:2
|
||||
[36m | [0m[37m[40m;[0m[1m[36m[40mSELECT[0m[37m[40m [0m[37m[40m$inum[0m[37m[40m, [0m[37m[40m$nul[0m[37m[40m, [0m[37m[40m$fnum[0m[37m[40m, [0m[37m[40m$str[0m[37m[40m, [0m[1m[37m[40mraise_error[0m[37m[40m([0m[35m[40m'oops!'[0m[37m[40m)[0m
|
||||
[36m =[0m [36mnote[0m: the bound parameters are set as follows:
|
||||
[4m$fnum[0m:FLOAT = “2”
|
||||
[4m$inum[0m:INTEGER = “1”
|
||||
[4m$nul[0m:NULL = “<NULL>”
|
||||
[4m$str[0m:TEXT = “abc”
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
[1m$id [0m[1m$parent [0m[1m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m2 [0m[1m [0m[1m0 [0m[1m [0m[1m0 [0m[1m [0m[1mSCAN all_logs VIRTUAL TABLE INDEX 1:SEARCH all_logs USING log_level < ? [0m
|
||||
[1m[7m$id [0m[1m[7m$parent [0m[1m[7m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m[7m 2[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7mSCAN all_logs VIRTUAL TABLE INDEX 1:SEARCH all_logs USING log_level <[0m[1m ? [0m
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[1m$id [0m[1m$parent [0m[1m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m2 [0m[1m [0m[1m0 [0m[1m [0m[1m0 [0m[1m [0m[1mSCAN all_logs VIRTUAL TABLE INDEX 1:SEARCH all_logs USING log_format = ? [0m
|
||||
[1m[7m$id [0m[1m[7m$parent [0m[1m[7m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m[7m 2[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7mSCAN all_logs VIRTUAL TABLE INDEX 1:SEARCH all_logs USING log_format =[0m[1m ? [0m
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[1m$id [0m[1m$parent [0m[1m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m2 [0m[1m [0m[1m0 [0m[1m [0m[1m0 [0m[1m [0m[1mSCAN access_log VIRTUAL TABLE INDEX 1:SEARCH access_log USING log_path GLOB ? [0m
|
||||
[1m[7m$id [0m[1m[7m$parent [0m[1m[7m$notused [0m[1m replace($detail, 'SCAN TABLE', 'SCAN') [0m
|
||||
[1m[7m 2[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7m 0[0m[1m[7m [0m[1m[7mSCAN access_log VIRTUAL TABLE INDEX 1:SEARCH access_log USING log_path GLOB[0m[1m ? [0m
|
||||
|
|
|
@ -149,6 +149,11 @@ run_cap_test ${lnav_test} -n \
|
|||
-c ";SELECT * FROM generate_series(1)" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ";SELECT 1 AS inum, NULL AS nul, 2.0 AS fnum, 'abc' AS str" \
|
||||
-c ";SELECT \$inum, \$nul, \$fnum, \$str, raise_error('oops!')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ";SELECT raise_error('oops!')" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
|
Loading…
Reference in New Issue