summaryrefslogtreecommitdiffstats
path: root/src/sql_commands.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/sql_commands.cc545
1 files changed, 540 insertions, 5 deletions
diff --git a/src/sql_commands.cc b/src/sql_commands.cc
index e145331..34f0603 100644
--- a/src/sql_commands.cc
+++ b/src/sql_commands.cc
@@ -30,12 +30,15 @@
#include "base/auto_mem.hh"
#include "base/fs_util.hh"
#include "base/injector.bind.hh"
+#include "base/itertools.hh"
#include "base/lnav_log.hh"
#include "bound_tags.hh"
#include "command_executor.hh"
#include "config.h"
+#include "lnav.hh"
#include "readline_context.hh"
#include "shlex.hh"
+#include "sql_help.hh"
#include "sqlite-extension-func.hh"
#include "sqlitepp.hh"
#include "view_helpers.hh"
@@ -88,6 +91,7 @@ sql_cmd_read(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
+ static const intern_string_t SRC = intern_string::lookup("cmdline");
static auto& lnav_db = injector::get<auto_sqlite3&>();
static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
@@ -102,13 +106,23 @@ sql_cmd_read(exec_context& ec,
return ec.make_error("{} -- unavailable in secure mode", args[0]);
}
- std::vector<std::string> split_args;
shlex lexer(cmdline);
- if (!lexer.split(split_args, ec.create_resolver())) {
- return ec.make_error("unable to parse arguments");
+ auto split_args_res = lexer.split(ec.create_resolver());
+ if (split_args_res.isErr()) {
+ auto split_err = split_args_res.unwrapErr();
+ auto um
+ = lnav::console::user_message::error("unable to parse file name")
+ .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()
+ | lnav::itertools::map([](const auto& elem) { return elem.se_value; });
+
for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
auto read_res = lnav::filesystem::read_file(split_args[lpc]);
@@ -166,6 +180,36 @@ sql_cmd_schema(exec_context& ec,
}
static Result<std::string, lnav::console::user_message>
+sql_cmd_msgformats(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ static const std::string MSG_FORMAT_STMT = R"(
+SELECT count(*) AS total,
+ min(log_line) AS log_line,
+ min(log_time) AS log_time,
+ humanize_duration(timediff(max(log_time), min(log_time))) AS duration,
+ group_concat(DISTINCT log_format) AS log_formats,
+ log_msg_format
+ FROM all_logs
+ WHERE log_msg_format != ''
+ GROUP BY log_msg_format
+ HAVING total > 1
+ ORDER BY total DESC, log_line ASC
+)";
+
+ std::string retval;
+
+ if (args.empty()) {
+ return Ok(retval);
+ }
+
+ std::string alt;
+
+ return execute_sql(ec, MSG_FORMAT_STMT, alt);
+}
+
+static Result<std::string, lnav::console::user_message>
sql_cmd_generic(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@@ -180,6 +224,184 @@ sql_cmd_generic(exec_context& ec,
return Ok(retval);
}
+static Result<std::string, lnav::console::user_message>
+prql_cmd_from(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-table");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static readline_context::prompt_result_t
+prql_cmd_from_prompt(exec_context& ec, const std::string& cmdline)
+{
+ if (!endswith(cmdline, "from ")) {
+ return {};
+ }
+
+ auto* tc = *lnav_data.ld_view_stack.top();
+ auto* lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
+
+ if (lss == nullptr || lss->text_line_count() == 0) {
+ return {};
+ }
+
+ auto line_pair = lss->find_line_with_file(lss->at(tc->get_selection()));
+ if (!line_pair) {
+ return {};
+ }
+
+ auto format_name
+ = line_pair->first->get_format_ptr()->get_name().to_string();
+ return {
+ "",
+ lnav::prql::quote_ident(format_name) + " ",
+ };
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_aggregate(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_append(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-table");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_derive(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_filter(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_group(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ args.emplace_back("prql-source");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_join(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-table");
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_select(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_sort(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ args.emplace_back("prql-expr");
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
+static Result<std::string, lnav::console::user_message>
+prql_cmd_take(exec_context& ec,
+ std::string cmdline,
+ std::vector<std::string>& args)
+{
+ std::string retval;
+
+ if (args.empty()) {
+ return Ok(retval);
+ }
+
+ return Ok(retval);
+}
+
static readline_context::command_t sql_commands[] = {
{
".dump",
@@ -193,8 +415,11 @@ static readline_context::command_t sql_commands[] = {
},
{
".msgformats",
- sql_cmd_schema,
- help_text(".msgformats", "df").sql_command(),
+ sql_cmd_msgformats,
+ help_text(".msgformats",
+ "Executes a query that will summarize the different message "
+ "formats found in the logs")
+ .sql_command(),
},
{
".read",
@@ -250,6 +475,313 @@ static readline_context::command_t sql_commands[] = {
"WITH",
sql_cmd_generic,
},
+ {
+ "from",
+ prql_cmd_from,
+ help_text("from")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL command to specify a data source")
+ .with_parameter({"table", "The table to use as a source"})
+ .with_example({
+ "To pull data from the 'http_status_codes' database table",
+ "from http_status_codes | take 3",
+ help_example::language::prql,
+ })
+ .with_example({
+ "To use an array literal as a source",
+ "from [{ col1=1, col2='abc' }, { col1=2, col2='def' }]",
+ help_example::language::prql,
+ }),
+ prql_cmd_from_prompt,
+ "prql-source",
+ },
+ {
+ "aggregate",
+ prql_cmd_aggregate,
+ help_text("aggregate")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to summarize many rows into one")
+ .with_parameter(
+ help_text{"expr", "The aggregate expression(s)"}.with_grouping(
+ "{", "}"))
+ .with_example({
+ "To group values into a JSON array",
+ "from [{a=1}, {a=2}] | aggregate { arr = json.group_array a }",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "append",
+ prql_cmd_append,
+ help_text("append")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to concatenate tables together")
+ .with_parameter({"table", "The table to use as a source"}),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "derive",
+ prql_cmd_derive,
+ help_text("derive")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to derive one or more columns")
+ .with_parameter(
+ help_text{"column", "The new column"}.with_grouping("{", "}"))
+ .with_example({
+ "To add a column that is a multiplication of another",
+ "from [{a=1}, {a=2}] | derive b = a * 2",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "filter",
+ prql_cmd_filter,
+ help_text("filter")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to pick rows based on their values")
+ .with_parameter(
+ {"expr", "The expression to evaluate over each row"})
+ .with_example({
+ "To pick rows where 'a' is greater than one",
+ "from [{a=1}, {a=2}] | filter a > 1",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "group",
+ prql_cmd_group,
+ help_text("group")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to partition rows into groups")
+ .with_parameter(
+ help_text{"key_columns", "The columns that define the group"}
+ .with_grouping("{", "}"))
+ .with_parameter(
+ help_text{"pipeline", "The pipeline to execute over a group"}
+ .with_grouping("(", ")"))
+ .with_example({
+ "To group by log_level and count the rows in each partition",
+ "from lnav_example_log | group { log_level } (aggregate { "
+ "count this })",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "join",
+ prql_cmd_join,
+ help_text("join")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to add columns from another table")
+ .with_parameter(
+ help_text{"side", "Specifies which rows to include"}
+ .with_enum_values({"inner", "left", "right", "full"})
+ .with_default_value("inner")
+ .optional())
+ .with_parameter(
+ {"table", "The other table to join with the current rows"})
+ .with_parameter(
+ help_text{"condition", "The condition used to join rows"}
+ .with_grouping("(", ")")),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "select",
+ prql_cmd_select,
+ help_text("select")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to pick and compute columns")
+ .with_parameter(
+ help_text{"expr", "The columns to include in the result set"}
+ .with_grouping("{", "}"))
+ .with_example({
+ "To pick the 'b' column from the rows",
+ "from [{a=1, b='abc'}, {a=2, b='def'}] | select b",
+ help_example::language::prql,
+ })
+ .with_example({
+ "To compute a new column from an input",
+ "from [{a=1}, {a=2}] | select b = a * 2",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "stats.average_of",
+ prql_cmd_sort,
+ help_text("stats.average_of", "Compute the average of col")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"col", "The column to average"})
+ .with_example({
+ "To get the average of a",
+ "from [{a=1}, {a=1}, {a=2}] | stats.average_of a",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "stats.count_by",
+ prql_cmd_sort,
+ help_text(
+ "stats.count_by",
+ "Partition rows and count the number of rows in each partition")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"column", "The columns to group by"}
+ .one_or_more()
+ .with_grouping("{", "}"))
+ .with_example({
+ "To count rows for a particular value of column 'a'",
+ "from [{a=1}, {a=1}, {a=2}] | stats.count_by a",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "stats.hist",
+ prql_cmd_sort,
+ help_text("stats.hist", "Count the top values per bucket of time")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"col", "The column to count"})
+ .with_parameter(help_text{"slice", "The time slice"}
+ .optional()
+ .with_default_value("'1h'"))
+ .with_parameter(
+ help_text{"top", "The limit on the number of values to report"}
+ .optional()
+ .with_default_value("10"))
+ .with_example({
+ "To chart the values of ex_procname over time",
+ "from lnav_example_log | stats.hist ex_procname",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "stats.sum_of",
+ prql_cmd_sort,
+ help_text("stats.sum_of", "Compute the sum of col")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"col", "The column to sum"})
+ .with_example({
+ "To get the sum of a",
+ "from [{a=1}, {a=1}, {a=2}] | stats.sum_of a",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "stats.by",
+ prql_cmd_sort,
+ help_text("stats.by", "A shorthand for grouping and aggregating")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"col", "The column to sum"})
+ .with_parameter(help_text{"values", "The aggregations to perform"})
+ .with_example({
+ "To partition by a and get the sum of b",
+ "from [{a=1, b=1}, {a=1, b=1}, {a=2, b=1}] | stats.by a "
+ "{sum b}",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "sort",
+ prql_cmd_sort,
+ help_text("sort")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL transform to sort rows")
+ .with_parameter(help_text{
+ "expr", "The values to use when ordering the result set"}
+ .with_grouping("{", "}"))
+ .with_example({
+ "To sort the rows in descending order",
+ "from [{a=1}, {a=2}] | sort {-a}",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "take",
+ prql_cmd_take,
+ help_text("take")
+ .prql_transform()
+ .with_tags({"prql"})
+ .with_summary("PRQL command to pick rows based on their position")
+ .with_parameter({"n_or_range", "The number of rows or range"})
+ .with_example({
+ "To pick the first row",
+ "from [{a=1}, {a=2}, {a=3}] | take 1",
+ help_example::language::prql,
+ })
+ .with_example({
+ "To pick the second and third rows",
+ "from [{a=1}, {a=2}, {a=3}] | take 2..3",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
+ {
+ "utils.distinct",
+ prql_cmd_sort,
+ help_text("utils.distinct",
+ "A shorthand for getting distinct values of col")
+ .prql_function()
+ .with_tags({"prql"})
+ .with_parameter(help_text{"col", "The column to sum"})
+ .with_example({
+ "To get the distinct values of a",
+ "from [{a=1}, {a=1}, {a=2}] | utils.distinct a",
+ help_example::language::prql,
+ }),
+ nullptr,
+ "prql-source",
+ {"prql-source"},
+ },
};
static readline_context::command_map_t sql_cmd_map;
@@ -259,6 +791,9 @@ static auto bound_sql_cmd_map
sql_cmd_map_tag>::to_instance(+[]() {
for (auto& cmd : sql_commands) {
sql_cmd_map[cmd.c_name] = &cmd;
+ if (cmd.c_help.ht_name) {
+ cmd.c_help.index_tags();
+ }
}
return &sql_cmd_map;