[docs] add report generation to cookbook

This commit is contained in:
Timothy Stack 2021-02-25 15:47:36 -08:00
parent 4d2691fa99
commit d45d7d6c58
60 changed files with 1505 additions and 722 deletions

13
NEWS
View File

@ -33,6 +33,13 @@ lnav v0.9.1:
* Themes can now include definitions for text highlights under:
/ui/theme-defs/<theme_name>/highlights
* Added a "grayscale" theme that isn't so colorful.
* Added the humanize_file_size() SQL function that converts a numeric size
to a human-friendly string.
* Added the sparkline() SQL function that returns a "sparkline" bar made
out of unicode characters. It can be used with a single value or as
an aggregator.
* Added a "log_time_msecs" hidden column to the log tables that returns
the timestamp as the number of milliseconds from the epoch.
Interface Changes:
* When copying log lines, the file name and time offset will be included
@ -42,9 +49,15 @@ lnav v0.9.1:
* The range_start and range_stop values of the regexp_capture() results
now start at 1 instead of zero to match with what the other SQL string
functions expect.
* The ":write-cols-to" command has been renamed to ":write-table-to".
* The DB view will limit the maximum column width to 120 characters.
* The ":echo" command now evaluates its message to do variable
substitution.
Fixes:
* Unicode text can now be entered in prompts.
* The replicate() SQL function would cause a crash if the number of
replications was zero.
lnav v0.9.0:
Features:

View File

@ -41,6 +41,7 @@ class CustSqliteLexer(RegexLexer):
'root': [
(r'\s+', Text),
(r'--.*\n?', Comment.Single),
(r'#.*\n?', Comment.Single),
(r'/\*', Comment.Multiline, 'multiline-comments'),
(words((
'ABORT',

View File

@ -19,6 +19,40 @@ Defining a New Format
TBD
Annotating Logs
---------------
Log messages can be annotated in a couple of different ways in **lnav** to help
you get organized.
Create partitions for Linux boots
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When digging through logs that can be broken up into multiple sections,
**lnav**'s :ref:`partitioning feature<taking_notes>` can be used to keep track
of which section you are in. For example, if a collection of Linux logs
covered multiple boots, the following script could be used to create partitions
for each boot. After the partition name is set for the log messages, the
current name will show up in the top status bar next to the current time.
.. literalinclude:: ../../src/scripts/partition-by-boot.lnav
:language: custsqlite
:caption: partition-by-boot.lnav
:linenos:
Tagging SSH log messages
^^^^^^^^^^^^^^^^^^^^^^^^
Log messages can be tagged interactively with the :ref:`:tag<tag>` command or
programmatically using the :ref:`sql-ext`. This example uses a script to
search for interesting SSH messages and automatically adds an appropriate tag.
.. literalinclude:: ../../example-scripts/tag-ssh-msgs.lnav
:language: custsqlite
:caption: tag-ssh-msgs.lnav
:linenos:
Log Analysis
------------
@ -50,3 +84,21 @@ between 10,000 and 40,000 bytes like so:
.. code-block:: custsqlite
:filter-expr :sc_bytes BETWEEN 10000 AND 40000
Generating a Report
^^^^^^^^^^^^^^^^^^^
Reports can be generated by writing an **lnav** :ref:`script<scripts>` that
uses SQL queries and commands to format a document. A basic script can simply
execute a SQL query that is shown in the DB view. More sophisticated scripts
can use the following commands to generate customized output for a report:
* The :ref:`:echo<echo>` command to write plain text
* :ref:`SQL queries<sql-ext>` followed by a "write" command, like
:ref:`:write-table-to<write_table_to>`.
.. literalinclude:: ../../example-scripts/report-demo.lnav
:language: custsqlite
:caption: report-demo.lnav
:linenos:

View File

@ -333,9 +333,25 @@ can have a mix of SQL and **lnav** commands, as well as include other scripts.
The type of statement to execute is determined by the leading character on a
line: a semi-colon begins a SQL statement; a colon starts an **lnav** command;
and a pipe (|) denotes another script to be executed. Lines beginning with a
hash are treated as comments. Any arguments passed to a script can be
referenced using '$N' where 'N' is the index of the argument. Remember that
you need to use the :ref:`:eval<eval>` command when referencing
hash are treated as comments. The following variables are defined in a script:
.. envvar:: #
The number of arguments passed to the script.
.. envvar:: __all__
A string containing all the arguments joined by a single space.
.. envvar:: 0
The path to the script being executed.
.. envvar:: 1-N
The arguments passed to the script.
Remember that you need to use the :ref:`:eval<eval>` command when referencing
variables in most **lnav** commands. Scripts can provide help text to be
displayed during interactive usage by adding the following tags in a comment
header:
@ -350,6 +366,17 @@ header:
# @description: Say hello to the given names.
.. tip::
The :ref:`:eval<eval>` command can be used to do variable substitution for
commands that do not natively support it. For example, to substitute the
variable, :code:`pattern`, in a :ref:`:filter-out<filter_out>` command:
.. code-block:: lnav
:eval :filter-out ${pattern}
Installing Formats
------------------

View File

@ -84,69 +84,6 @@ the `sqlite.org <http://sqlite.org>`_ web site.
**lnav**'s interface to perform queries. The database will be attached with
a name based on the database file name.
Taking Notes
------------
A few of the columns in the log tables can be updated on a row-by-row basis to
allow you to take notes. The majority of the columns in a log table are
read-only since they are backed by the log files themselves. However, the
following columns can be changed by an :code:`UPDATE` statement:
* **log_part** - The "partition" the log message belongs to. This column can
also be changed by the :ref:`:partition-name<partition_name>` command.
* **log_mark** - Indicates whether the line has been bookmarked.
* **log_comment** - A free-form text field for storing commentary. This
column can also be changed by the :ref:`:comment<comment>` command.
* **log_tags** - A JSON list of tags associated with the log message. This
column can also be changed by the :ref:`:tag<tag>` command.
While these columns can be updated by through other means, using the SQL
interface allows you to make changes automatically and en masse. For example,
to bookmark all lines that have the text "something interesting" in the log
message body, you can execute:
.. code-block:: custsqlite
;UPDATE all_logs SET log_mark = 1 WHERE log_body LIKE '%something interesting%'
As a more advanced example of the power afforded by SQL and **lnav**'s virtual
tables, we will tag log messages where the IP address bound by dhclient has
changed. For example, if dhclient reports "bound to 10.0.0.1" initially and
then reports "bound to 10.0.0.2", we want to tag only the messages where the
IP address was different from the previous message. While this can be done
with a single SQL statement [#]_, we will break things down into a few steps for
this example. First, we will use the :ref:`:create-search-table<create_search_table>`
command to match the dhclient message and extract the IP address:
.. code-block:: lnav
:create-search-table dhclient_ip bound to (?<ip>[^ ]+)
The above command will create a new table named :code:`dhclient_ip` with the
standard log columns and an :code:`ip` column that contains the IP address.
Next, we will create a view over the :code:`dhclient_ip` table that returns
the log message line number, the IP address from the current row and the IP
address from the previous row:
.. code-block:: custsqlite
;CREATE VIEW IF NOT EXISTS dhclient_ip_changes AS SELECT log_line, ip, lag(ip) OVER (ORDER BY log_line) AS prev_ip FROM dhclient_ip
Finally, the following :code:`UPDATE` statement will concatenate the tag
"#ipchanged" onto the :code:`log_tags` column for any rows in the view where
the current IP is different from the previous IP:
.. code-block:: custsqlite
;UPDATE syslog_log SET log_tags = json_concat(log_tags, '#ipchanged') WHERE log_line IN (SELECT log_line FROM dhclient_ip_changes WHERE ip != prev_ip)
Since the above can be a lot to type out interactively, you can put these
commands into a :ref:`script<scripts>` and execute that script with the
:kbd:`\|` hotkey.
.. [#] The expression :code:`regexp_match('bound to ([^ ]+)', log_body) as ip`
can be used to extract the IP address from the log message body.
Commands
--------

View File

@ -132,4 +132,70 @@ To hide messages below a certain log level, you can use the
Search Tables
-------------
TBD
TBD
.. _taking_notes:
Taking Notes
------------
A few of the columns in the log tables can be updated on a row-by-row basis to
allow you to take notes. The majority of the columns in a log table are
read-only since they are backed by the log files themselves. However, the
following columns can be changed by an :code:`UPDATE` statement:
* **log_part** - The "partition" the log message belongs to. This column can
also be changed by the :ref:`:partition-name<partition_name>` command.
* **log_mark** - Indicates whether the line has been bookmarked.
* **log_comment** - A free-form text field for storing commentary. This
column can also be changed by the :ref:`:comment<comment>` command.
* **log_tags** - A JSON list of tags associated with the log message. This
column can also be changed by the :ref:`:tag<tag>` command.
While these columns can be updated by through other means, using the SQL
interface allows you to make changes automatically and en masse. For example,
to bookmark all lines that have the text "something interesting" in the log
message body, you can execute:
.. code-block:: custsqlite
;UPDATE all_logs SET log_mark = 1 WHERE log_body LIKE '%something interesting%'
As a more advanced example of the power afforded by SQL and **lnav**'s virtual
tables, we will tag log messages where the IP address bound by dhclient has
changed. For example, if dhclient reports "bound to 10.0.0.1" initially and
then reports "bound to 10.0.0.2", we want to tag only the messages where the
IP address was different from the previous message. While this can be done
with a single SQL statement [#]_, we will break things down into a few steps for
this example. First, we will use the :ref:`:create-search-table<create_search_table>`
command to match the dhclient message and extract the IP address:
.. code-block:: lnav
:create-search-table dhclient_ip bound to (?<ip>[^ ]+)
The above command will create a new table named :code:`dhclient_ip` with the
standard log columns and an :code:`ip` column that contains the IP address.
Next, we will create a view over the :code:`dhclient_ip` table that returns
the log message line number, the IP address from the current row and the IP
address from the previous row:
.. code-block:: custsqlite
;CREATE VIEW IF NOT EXISTS dhclient_ip_changes AS SELECT log_line, ip, lag(ip) OVER (ORDER BY log_line) AS prev_ip FROM dhclient_ip
Finally, the following :code:`UPDATE` statement will concatenate the tag
"#ipchanged" onto the :code:`log_tags` column for any rows in the view where
the current IP is different from the previous IP:
.. code-block:: custsqlite
;UPDATE syslog_log SET log_tags = json_concat(log_tags, '#ipchanged') WHERE log_line IN (SELECT log_line FROM dhclient_ip_changes WHERE ip != prev_ip)
Since the above can be a lot to type out interactively, you can put these
commands into a :ref:`script<scripts>` and execute that script with the
:kbd:`\|` hotkey.
.. [#] The expression :code:`regexp_match('bound to ([^ ]+)', log_body) as ip`
can be used to extract the IP address from the log message body.

View File

@ -0,0 +1,83 @@
#
# @synopsis: report-demo [<output-path>]
# @description: Generate a report for requests in access_log files
#
# Figure out the file path where the report should be written to, default is
# stdout
;SELECT CASE
WHEN $1 IS NULL THEN '-'
ELSE $1
END AS out_path
# Redirect output from commands to $out_path
:redirect-to $out_path
# Print an introductory message
;SELECT printf('\n%d total requests', count(1)) AS msg FROM access_log
:echo $msg
;WITH top_paths AS (
SELECT
cs_uri_stem,
count(1) AS total_hits,
sum(sc_bytes) as bytes,
count(distinct c_ip) as visitors
FROM access_log
WHERE sc_status BETWEEN 200 AND 300
GROUP BY cs_uri_stem
ORDER BY total_hits DESC
LIMIT 50),
weekly_hits_with_gaps AS (
SELECT timeslice(log_time_msecs, '1w') AS week,
cs_uri_stem,
count(1) AS weekly_hits
FROM access_log
WHERE cs_uri_stem IN (SELECT cs_uri_stem FROM top_paths) AND
sc_status BETWEEN 200 AND 300
GROUP BY week, cs_uri_stem),
all_weeks AS (
SELECT week
FROM weekly_hits_with_gaps
GROUP BY week
ORDER BY week ASC),
weekly_hits AS (
SELECT all_weeks.week,
top_paths.cs_uri_stem,
ifnull(weekly_hits, 0) AS hits
FROM all_weeks
CROSS JOIN top_paths
LEFT JOIN weekly_hits_with_gaps
ON all_weeks.week = weekly_hits_with_gaps.week AND
top_paths.cs_uri_stem = weekly_hits_with_gaps.cs_uri_stem)
SELECT weekly_hits.cs_uri_stem AS Path,
printf('%,9d', total_hits) AS Hits,
printf('%,9d', visitors) AS Visitors,
printf('%9s', humanize_file_size(bytes)) as Amount,
sparkline(hits) AS Weeks
FROM weekly_hits
LEFT JOIN top_paths ON top_paths.cs_uri_stem = weekly_hits.cs_uri_stem
GROUP BY weekly_hits.cs_uri_stem
ORDER BY Hits DESC
LIMIT 10
:write-table-to -
:echo
:echo Failed Requests
:echo
;SELECT printf('%,9d', count(1)) AS Hits,
printf('%,9d', count(distinct c_ip)) AS Visitors,
sc_status AS Status,
cs_method AS Method,
group_concat(distinct cs_version) AS Versions,
cs_uri_stem AS Path,
replicate('|', (cast(count(1) AS REAL) / $total_requests) * 100.0) AS "% of Requests"
FROM access_log
WHERE sc_status >= 400
GROUP BY cs_method, cs_uri_stem
ORDER BY Hits DESC
LIMIT 10
:write-table-to -

View File

@ -0,0 +1,10 @@
#
# @synopsis: tag-ssh-msgs
# @description: Tag interesting SSH log messages
#
;UPDATE all_logs
SET log_tags = json_concat(log_tags, '#ssh.invalid-user')
WHERE log_text LIKE '%Invalid user from%'
;SELECT 'Tagged ' || changes() || ' messages';

View File

@ -89,6 +89,22 @@ public:
auto_fd lh_fd;
};
#if HAVE_ARCHIVE_H
/**
* Enables a subset of the supported archive formats to speed up detection,
* since some formats, like xar are unlikely to be used.
*/
static void enable_desired_archive_formats(archive *arc)
{
archive_read_support_format_7zip(arc);
archive_read_support_format_cpio(arc);
archive_read_support_format_lha(arc);
archive_read_support_format_rar(arc);
archive_read_support_format_tar(arc);
archive_read_support_format_zip(arc);
}
#endif
bool is_archive(const fs::path& filename)
{
#if HAVE_ARCHIVE_H
@ -97,7 +113,7 @@ bool is_archive(const fs::path& filename)
arc = archive_read_new();
archive_read_support_filter_all(arc);
archive_read_support_format_all(arc);
enable_desired_archive_formats(arc);
archive_read_support_format_raw(arc);
log_debug("read open %s", filename.c_str());
auto r = archive_read_open_filename(arc, filename.c_str(), 128 * 1024);
@ -254,7 +270,7 @@ static walk_result_t extract(const std::string &filename, const extract_cb &cb)
auto_mem<archive> ext(archive_free);
arc = archive_read_new();
archive_read_support_format_all(arc);
enable_desired_archive_formats(arc);
archive_read_support_format_raw(arc);
archive_read_support_filter_all(arc);
ext = archive_write_disk_new();

View File

@ -72,6 +72,26 @@ public:
return retval;
};
/**
* dup(2) the given file descriptor and wrap it in an auto_fd.
*
* @param fd The file descriptor to duplicate.
* @return A new auto_fd that contains the duplicated file descriptor.
*/
static auto_fd dup_of(int fd) {
if (fd == -1) {
return auto_fd{};
}
auto new_fd = dup(fd);
if (new_fd == -1) {
throw std::bad_alloc();
}
return auto_fd(new_fd);
};
/**
* Construct an auto_fd to manage the given file descriptor.
*

View File

@ -37,7 +37,7 @@
namespace humanize {
std::string file_size(ssize_t value)
std::string file_size(file_ssize_t value)
{
static const double LN1024 = log(1024.0);
static const std::vector<const char *> UNITS = {
@ -61,4 +61,34 @@ std::string file_size(ssize_t value)
UNITS[exp]);
}
const std::string& sparkline(double value, nonstd::optional<double> upper_opt)
{
static const std::string ZERO = " ";
static const std::string BARS[] = {
"\u2581",
"\u2582",
"\u2583",
"\u2584",
"\u2585",
"\u2586",
"\u2587",
"\u2588",
};
static const double BARS_COUNT = std::distance(begin(BARS), end(BARS));
if (value <= 0.0) {
return ZERO;
}
auto upper = upper_opt.value_or(100.0);
if (value >= upper) {
return BARS[(size_t) BARS_COUNT - 1];
}
size_t index = ceil((value / upper) * BARS_COUNT) - 1;
return BARS[index];
}
}

View File

@ -34,6 +34,8 @@
#include <sys/types.h>
#include "file_range.hh"
namespace humanize {
/**
@ -42,7 +44,9 @@ namespace humanize {
* @param value The value to format.
* @return The formatted string.
*/
std::string file_size(ssize_t value);
std::string file_size(file_ssize_t value);
const std::string& sparkline(double value, nonstd::optional<double> upper);
}

View File

@ -113,6 +113,11 @@ size_t unquote_w3c(char *dst, const char *str, size_t len)
void truncate_to(std::string &str, size_t max_char_len)
{
static const std::string ELLIPSIS = "\u22ef";
if (str.length() < max_char_len) {
return;
}
auto str_char_len_res = utf8_string_length(str);
if (str_char_len_res.isErr()) {
@ -130,12 +135,16 @@ void truncate_to(std::string &str, size_t max_char_len)
return;
}
auto to_remove = (str_char_len - max_char_len) + 1;
auto chars_to_remove = (str_char_len - max_char_len) + 1;
auto midpoint = str_char_len / 2;
auto to_keep_at_front = midpoint - (to_remove / 2);
str.erase(to_keep_at_front, to_remove);
str.insert(to_keep_at_front, ELLIPSIS);
auto chars_to_keep_at_front = midpoint - (chars_to_remove / 2);
auto bytes_to_keep_at_front =
utf8_char_to_byte_index(str, chars_to_keep_at_front);
auto remove_up_to_bytes =
utf8_char_to_byte_index(str, chars_to_keep_at_front + chars_to_remove);
auto bytes_to_remove = remove_up_to_bytes - bytes_to_keep_at_front;
str.erase(bytes_to_keep_at_front, bytes_to_remove);
str.insert(bytes_to_keep_at_front, ELLIPSIS);
}
bool is_url(const char *fn)
@ -190,3 +199,20 @@ std::string repeat(const std::string& input, size_t num)
std::fill_n(std::ostream_iterator<std::string>(os), num, input);
return os.str();
}
std::string center_str(const std::string &subject, size_t width)
{
std::string retval = subject;
truncate_to(retval, width);
auto retval_length = utf8_string_length(retval).unwrapOr(retval.length());
auto total_fill = width - retval_length;
auto before = total_fill / 2;
auto after = total_fill - before;
retval.insert(0, before, ' ');
retval.append(after, ' ');
return retval;
}

View File

@ -137,13 +137,17 @@ inline ssize_t utf8_char_to_byte_index(const std::string &str, ssize_t ch_index)
return retval;
}
inline Result<size_t, const char *> utf8_string_length(const std::string &str)
inline Result<size_t, const char *> utf8_string_length(const char *str, ssize_t len = -1)
{
size_t retval = 0;
for (size_t byte_index = 0; byte_index < str.length();) {
auto ch_size = TRY(ww898::utf::utf8::char_size([&str, byte_index]() {
return std::make_pair(str[byte_index], str.length() - byte_index);
if (len == -1) {
len = strlen(str);
}
for (ssize_t byte_index = 0; byte_index < len;) {
auto ch_size = TRY(ww898::utf::utf8::char_size([str, len, byte_index]() {
return std::make_pair(str[byte_index], len - byte_index);
}));
byte_index += ch_size;
retval += 1;
@ -152,6 +156,11 @@ inline Result<size_t, const char *> utf8_string_length(const std::string &str)
return Ok(retval);
}
inline Result<size_t, const char *> utf8_string_length(const std::string& str)
{
return utf8_string_length(str.c_str(), str.length());
}
bool is_url(const char *fn);
size_t abbreviate_str(char *str, size_t len, size_t max_len);
@ -160,4 +169,6 @@ void split_ws(const std::string &str, std::vector<std::string> &toks_out);
std::string repeat(const std::string& input, size_t num);
std::string center_str(const std::string& subject, size_t width);
#endif

View File

@ -84,11 +84,10 @@ std::string column_namer::add_column(const std::string &in_name)
while (this->existing_name(retval)) {
if (num == 0) {
log_debug("existing!");
this->cn_name_counters[retval] = num;
}
log_debug("dup %s", retval.c_str());
log_debug("column name already exists: %s", retval.c_str());
snprintf(buffer, buf_size, "%s_%d", base_name.c_str(), num);
retval = buffer;
num += 1;

View File

@ -424,7 +424,7 @@ static Result<string, string> execute_file_contents(exec_context &ec, const ghc:
char mode = '\0';
ec.ec_path_stack.emplace_back(path.parent_path());
ec.ec_output_stack.emplace_back(nonstd::nullopt);
exec_context::output_guard og(ec);
while ((line_size = getline(line.out(), &line_max_size, file)) != -1) {
line_number += 1;
@ -471,7 +471,6 @@ static Result<string, string> execute_file_contents(exec_context &ec, const ghc:
} else {
fclose(file);
}
ec.ec_output_stack.pop_back();
ec.ec_path_stack.pop_back();
return Ok(retval);
@ -846,3 +845,41 @@ std::string exec_context::get_error_prefix()
return fmt::format("{}:{}: error: ", source.first, source.second);
}
void exec_context::set_output(const string &name, FILE *file)
{
log_info("redirecting command output to: %s", name.c_str());
this->ec_output_stack.back().second | [](auto file) {
if (file != stdout) {
fclose(file);
}
};
this->ec_output_stack.back() = std::make_pair(name, file);
}
void exec_context::clear_output()
{
log_info("redirecting command output to screen");
this->ec_output_stack.back().second | [](auto file) {
if (file != stdout) {
fclose(file);
}
};
this->ec_output_stack.back() = std::make_pair("default", nonstd::nullopt);
}
exec_context::output_guard::output_guard(exec_context &context,
std::string name,
const nonstd::optional<FILE *> &file)
: sg_context(context) {
if (file) {
log_info("redirecting command output to: %s", name.c_str());
}
context.ec_output_stack.emplace_back(std::move(name), file);
}
exec_context::output_guard::~output_guard()
{
this->sg_context.clear_output();
this->sg_context.ec_output_stack.pop_back();
}

View File

@ -63,7 +63,7 @@ struct exec_context {
this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.emplace_back(".");
this->ec_source.emplace("command", 1);
this->ec_output_stack.emplace_back(nonstd::nullopt);
this->ec_output_stack.emplace_back("screen", nonstd::nullopt);
}
std::string get_error_prefix();
@ -79,14 +79,18 @@ struct exec_context {
for (auto iter = this->ec_output_stack.rbegin();
iter != this->ec_output_stack.rend();
++iter) {
if (*iter) {
return *iter;
if (iter->second && *iter->second) {
return *iter->second;
}
}
return nonstd::nullopt;
}
void set_output(const std::string& name, FILE *file);
void clear_output();
struct source_guard {
source_guard(exec_context &context) : sg_context(context) {
@ -99,6 +103,16 @@ struct exec_context {
exec_context &sg_context;
};
struct output_guard {
explicit output_guard(exec_context &context,
std::string name = "default",
const nonstd::optional<FILE *>& file = nonstd::nullopt);
~output_guard();
exec_context &sg_context;
};
source_guard enter_source(const std::string& path, int line_number) {
this->ec_source.emplace(path, line_number);
return source_guard(*this);
@ -120,7 +134,7 @@ struct exec_context {
std::map<std::string, std::string> ec_global_vars;
std::vector<ghc::filesystem::path> ec_path_stack;
std::stack<std::pair<std::string, int>> ec_source;
std::vector<nonstd::optional<FILE *>> ec_output_stack;
std::vector<std::pair<std::string, nonstd::optional<FILE *>>> ec_output_stack;
attr_line_t ec_accumulator;

View File

@ -356,8 +356,7 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
struct element blank;
blank.e_capture.c_begin = blank.e_capture.c_end =
free_row.front().e_capture.
c_begin;
free_row.front().e_capture.c_begin;
blank.e_token = DNT_KEY;
pair_subs.PUSH_BACK(blank);
pair_subs.PUSH_BACK(free_row.front());
@ -445,8 +444,8 @@ void data_parser::discover_format()
elem.e_capture = *pc_iter;
require(elem.e_capture.c_begin != -1);
require(elem.e_capture.c_end != -1);
require(elem.e_capture.c_begin >= 0);
require(elem.e_capture.c_end >= 0);
state_stack.top().update_for_element(elem);
switch (elem.e_token) {

View File

@ -257,6 +257,7 @@ public:
{
ELEMENT_TRACE;
require(elem.e_capture.c_end >= -1);
this->std::list<element>::push_front(elem);
};
@ -264,6 +265,7 @@ public:
{
ELEMENT_TRACE;
require(elem.e_capture.c_end >= -1);
this->std::list<element>::push_back(elem);
};

View File

@ -1,5 +1,5 @@
/* Generated by re2c 2.0.3 on Sat Feb 13 21:35:28 2021 */
#line 1 "../../lnav/src/data_scanner_re.re"
/* Generated by re2c 2.0.3 on Thu Feb 25 11:01:38 2021 */
#line 1 "../../lnav2/src/data_scanner_re.re"
/**
* Copyright (c) 2015, Timothy Stack
*
@ -41,7 +41,11 @@ bool data_scanner::tokenize2(pcre_context &pc, data_token_t &token_out)
{
# define YYCTYPE unsigned char
# define CAPTURE(tok) { \
pi.pi_next_offset = YYCURSOR.val - (const unsigned char *) pi.get_string(); \
if (YYCURSOR.val == EMPTY) { \
pi.pi_next_offset = pi.pi_length; \
} else { \
pi.pi_next_offset = YYCURSOR.val - (const unsigned char *) pi.get_string(); \
} \
cap[0].c_end = pi.pi_next_offset; \
cap[1].c_end = pi.pi_next_offset; \
token_out = tok; \
@ -102,10 +106,12 @@ bool data_scanner::tokenize2(pcre_context &pc, data_token_t &token_out)
pc.set_count(2);
cap[0].c_begin = pi.pi_next_offset;
cap[0].c_end = pi.pi_next_offset;
cap[1].c_begin = pi.pi_next_offset;
cap[1].c_end = pi.pi_next_offset;
#line 109 "../../lnav/src/data_scanner_re.cc"
#line 115 "../../lnav2/src/data_scanner_re.cc"
{
YYCTYPE yych;
unsigned int yyaccept = 0;
@ -360,9 +366,9 @@ yy2:
}
yy3:
++YYCURSOR;
#line 132 "../../lnav/src/data_scanner_re.re"
#line 138 "../../lnav2/src/data_scanner_re.re"
{ return false; }
#line 366 "../../lnav/src/data_scanner_re.cc"
#line 372 "../../lnav2/src/data_scanner_re.cc"
yy5:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -520,11 +526,11 @@ yy6:
default: goto yy7;
}
yy7:
#line 226 "../../lnav/src/data_scanner_re.re"
#line 232 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_SYMBOL);
}
#line 528 "../../lnav/src/data_scanner_re.cc"
#line 534 "../../lnav2/src/data_scanner_re.cc"
yy8:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
@ -543,14 +549,14 @@ yy8:
default: goto yy74;
}
yy9:
#line 231 "../../lnav/src/data_scanner_re.re"
#line 237 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_WHITE); }
#line 549 "../../lnav/src/data_scanner_re.cc"
#line 555 "../../lnav2/src/data_scanner_re.cc"
yy10:
++YYCURSOR;
#line 230 "../../lnav/src/data_scanner_re.re"
#line 236 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_LINE); }
#line 554 "../../lnav/src/data_scanner_re.cc"
#line 560 "../../lnav2/src/data_scanner_re.cc"
yy12:
yyaccept = 1;
yych = *(YYMARKER = ++YYCURSOR);
@ -572,9 +578,9 @@ yy12:
yy13:
++YYCURSOR;
yy14:
#line 233 "../../lnav/src/data_scanner_re.re"
#line 239 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_GARBAGE); }
#line 578 "../../lnav/src/data_scanner_re.cc"
#line 584 "../../lnav2/src/data_scanner_re.cc"
yy15:
yyaccept = 2;
yych = *(YYMARKER = ++YYCURSOR);
@ -1024,19 +1030,19 @@ yy18:
default: goto yy19;
}
yy19:
#line 198 "../../lnav/src/data_scanner_re.re"
#line 204 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_LPAREN); }
#line 1030 "../../lnav/src/data_scanner_re.cc"
#line 1036 "../../lnav2/src/data_scanner_re.cc"
yy20:
++YYCURSOR;
#line 199 "../../lnav/src/data_scanner_re.re"
#line 205 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_RPAREN); }
#line 1035 "../../lnav/src/data_scanner_re.cc"
#line 1041 "../../lnav2/src/data_scanner_re.cc"
yy22:
++YYCURSOR;
#line 191 "../../lnav/src/data_scanner_re.re"
#line 197 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_COMMA); }
#line 1040 "../../lnav/src/data_scanner_re.cc"
#line 1046 "../../lnav2/src/data_scanner_re.cc"
yy24:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -1351,9 +1357,9 @@ yy26:
default: goto yy28;
}
yy28:
#line 162 "../../lnav/src/data_scanner_re.re"
#line 168 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_PATH); }
#line 1357 "../../lnav/src/data_scanner_re.cc"
#line 1363 "../../lnav2/src/data_scanner_re.cc"
yy29:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
@ -1513,9 +1519,9 @@ yy29:
default: goto yy30;
}
yy30:
#line 217 "../../lnav/src/data_scanner_re.re"
#line 223 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_NUMBER); }
#line 1519 "../../lnav/src/data_scanner_re.cc"
#line 1525 "../../lnav2/src/data_scanner_re.cc"
yy31:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
@ -1998,14 +2004,14 @@ yy34:
default: goto yy35;
}
yy35:
#line 189 "../../lnav/src/data_scanner_re.re"
#line 195 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_COLON); }
#line 2004 "../../lnav/src/data_scanner_re.cc"
#line 2010 "../../lnav2/src/data_scanner_re.cc"
yy36:
++YYCURSOR;
#line 192 "../../lnav/src/data_scanner_re.re"
#line 198 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_SEMI); }
#line 2009 "../../lnav/src/data_scanner_re.cc"
#line 2015 "../../lnav2/src/data_scanner_re.cc"
yy38:
yyaccept = 6;
yych = *(YYMARKER = ++YYCURSOR);
@ -2080,19 +2086,19 @@ yy38:
default: goto yy39;
}
yy39:
#line 200 "../../lnav/src/data_scanner_re.re"
#line 206 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_LANGLE); }
#line 2086 "../../lnav/src/data_scanner_re.cc"
#line 2092 "../../lnav2/src/data_scanner_re.cc"
yy40:
++YYCURSOR;
#line 190 "../../lnav/src/data_scanner_re.re"
#line 196 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_EQUALS); }
#line 2091 "../../lnav/src/data_scanner_re.cc"
#line 2097 "../../lnav2/src/data_scanner_re.cc"
yy42:
++YYCURSOR;
#line 201 "../../lnav/src/data_scanner_re.re"
#line 207 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_RANGLE); }
#line 2096 "../../lnav/src/data_scanner_re.cc"
#line 2102 "../../lnav2/src/data_scanner_re.cc"
yy44:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -2556,9 +2562,9 @@ yy50:
default: goto yy51;
}
yy51:
#line 196 "../../lnav/src/data_scanner_re.re"
#line 202 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_LSQUARE); }
#line 2562 "../../lnav/src/data_scanner_re.cc"
#line 2568 "../../lnav2/src/data_scanner_re.cc"
yy52:
yych = *++YYCURSOR;
switch (yych) {
@ -2567,9 +2573,9 @@ yy52:
}
yy53:
++YYCURSOR;
#line 197 "../../lnav/src/data_scanner_re.re"
#line 203 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_RSQUARE); }
#line 2573 "../../lnav/src/data_scanner_re.cc"
#line 2579 "../../lnav2/src/data_scanner_re.cc"
yy55:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -3116,14 +3122,14 @@ yy62:
default: goto yy63;
}
yy63:
#line 194 "../../lnav/src/data_scanner_re.re"
#line 200 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_LCURLY); }
#line 3122 "../../lnav/src/data_scanner_re.cc"
#line 3128 "../../lnav2/src/data_scanner_re.cc"
yy64:
++YYCURSOR;
#line 195 "../../lnav/src/data_scanner_re.re"
#line 201 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_RCURLY); }
#line 3127 "../../lnav/src/data_scanner_re.cc"
#line 3133 "../../lnav2/src/data_scanner_re.cc"
yy66:
yych = *++YYCURSOR;
switch (yych) {
@ -3670,7 +3676,7 @@ yy79:
default: goto yy80;
}
yy80:
#line 134 "../../lnav/src/data_scanner_re.re"
#line 140 "../../lnav2/src/data_scanner_re.re"
{
CAPTURE(DT_QUOTED_STRING);
switch (pi.get_string()[cap[1].c_begin]) {
@ -3683,7 +3689,7 @@ yy80:
cap[1].c_end -= 1;
return true;
}
#line 3687 "../../lnav/src/data_scanner_re.cc"
#line 3693 "../../lnav2/src/data_scanner_re.cc"
yy81:
yych = *++YYCURSOR;
switch (yych) {
@ -5152,9 +5158,9 @@ yy102:
}
yy103:
++YYCURSOR;
#line 193 "../../lnav/src/data_scanner_re.re"
#line 199 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_EMPTY_CONTAINER); }
#line 5158 "../../lnav/src/data_scanner_re.cc"
#line 5164 "../../lnav2/src/data_scanner_re.cc"
yy105:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
@ -5780,9 +5786,9 @@ yy114:
default: goto yy115;
}
yy115:
#line 216 "../../lnav/src/data_scanner_re.re"
#line 222 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_PERCENTAGE); }
#line 5786 "../../lnav/src/data_scanner_re.cc"
#line 5792 "../../lnav2/src/data_scanner_re.cc"
yy116:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -6016,9 +6022,9 @@ yy117:
default: goto yy118;
}
yy118:
#line 215 "../../lnav/src/data_scanner_re.re"
#line 221 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_OCTAL_NUMBER); }
#line 6022 "../../lnav/src/data_scanner_re.cc"
#line 6028 "../../lnav2/src/data_scanner_re.cc"
yy119:
yyaccept = 4;
yych = *(YYMARKER = ++YYCURSOR);
@ -6364,9 +6370,9 @@ yy121:
default: goto yy122;
}
yy122:
#line 218 "../../lnav/src/data_scanner_re.re"
#line 224 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_HEX_NUMBER); }
#line 6370 "../../lnav/src/data_scanner_re.cc"
#line 6376 "../../lnav2/src/data_scanner_re.cc"
yy123:
yyaccept = 10;
yych = *(YYMARKER = ++YYCURSOR);
@ -7489,11 +7495,11 @@ yy133:
default: goto yy134;
}
yy134:
#line 146 "../../lnav/src/data_scanner_re.re"
#line 152 "../../lnav2/src/data_scanner_re.re"
{
CAPTURE(DT_WORD);
}
#line 7497 "../../lnav/src/data_scanner_re.cc"
#line 7503 "../../lnav2/src/data_scanner_re.cc"
yy135:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -9296,7 +9302,7 @@ yy160:
yyt1 = yyt2;
yy161:
YYCURSOR = yyt1;
#line 149 "../../lnav/src/data_scanner_re.re"
#line 155 "../../lnav2/src/data_scanner_re.re"
{
CAPTURE(DT_QUOTED_STRING);
switch (pi.get_string()[cap[1].c_begin]) {
@ -9309,7 +9315,7 @@ yy161:
cap[1].c_end -= 1;
return true;
}
#line 9313 "../../lnav/src/data_scanner_re.cc"
#line 9319 "../../lnav2/src/data_scanner_re.cc"
yy162:
yyaccept = 12;
yych = *(YYMARKER = ++YYCURSOR);
@ -12758,9 +12764,9 @@ yy200:
++YYCURSOR;
yy201:
YYCURSOR = yyt2;
#line 175 "../../lnav/src/data_scanner_re.re"
#line 181 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_IPV6_ADDRESS); }
#line 12764 "../../lnav/src/data_scanner_re.cc"
#line 12770 "../../lnav2/src/data_scanner_re.cc"
yy202:
yych = *++YYCURSOR;
switch (yych) {
@ -14038,11 +14044,11 @@ yy217:
yy218:
++YYCURSOR;
yy219:
#line 181 "../../lnav/src/data_scanner_re.re"
#line 187 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_XML_OPEN_TAG);
}
#line 14046 "../../lnav/src/data_scanner_re.cc"
#line 14052 "../../lnav2/src/data_scanner_re.cc"
yy220:
yych = *++YYCURSOR;
switch (yych) {
@ -14191,9 +14197,9 @@ yy223:
yyt3 = yyt4;
yy224:
YYCURSOR = yyt3;
#line 224 "../../lnav/src/data_scanner_re.re"
#line 230 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_WORD); }
#line 14197 "../../lnav/src/data_scanner_re.cc"
#line 14203 "../../lnav2/src/data_scanner_re.cc"
yy225:
yych = *++YYCURSOR;
switch (yych) {
@ -21698,11 +21704,11 @@ yy310:
yy311:
++YYCURSOR;
yy312:
#line 177 "../../lnav/src/data_scanner_re.re"
#line 183 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_XML_EMPTY_TAG);
}
#line 21706 "../../lnav/src/data_scanner_re.cc"
#line 21712 "../../lnav2/src/data_scanner_re.cc"
yy313:
yych = *++YYCURSOR;
switch (yych) {
@ -21714,11 +21720,11 @@ yy313:
}
yy315:
++YYCURSOR;
#line 185 "../../lnav/src/data_scanner_re.re"
#line 191 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_XML_CLOSE_TAG);
}
#line 21722 "../../lnav/src/data_scanner_re.cc"
#line 21728 "../../lnav2/src/data_scanner_re.cc"
yy317:
yych = *++YYCURSOR;
yy318:
@ -22883,9 +22889,9 @@ yy332:
default: goto yy334;
}
yy334:
#line 220 "../../lnav/src/data_scanner_re.re"
#line 226 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_EMAIL); }
#line 22889 "../../lnav/src/data_scanner_re.cc"
#line 22895 "../../lnav2/src/data_scanner_re.cc"
yy335:
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -23273,11 +23279,11 @@ yy338:
default: goto yy340;
}
yy340:
#line 211 "../../lnav/src/data_scanner_re.re"
#line 217 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_VERSION_NUMBER);
}
#line 23281 "../../lnav/src/data_scanner_re.cc"
#line 23287 "../../lnav2/src/data_scanner_re.cc"
yy341:
yyaccept = 18;
yych = *(YYMARKER = ++YYCURSOR);
@ -25711,7 +25717,7 @@ yy362:
default: goto yy363;
}
yy363:
#line 165 "../../lnav/src/data_scanner_re.re"
#line 171 "../../lnav2/src/data_scanner_re.re"
{
if ((YYCURSOR - (const unsigned char *) pi.get_string()) == 17) {
RET(DT_MAC_ADDRESS);
@ -25719,7 +25725,7 @@ yy363:
RET(DT_HEX_DUMP);
}
}
#line 25723 "../../lnav/src/data_scanner_re.cc"
#line 25729 "../../lnav2/src/data_scanner_re.cc"
yy364:
yyaccept = 19;
yych = *(YYMARKER = ++YYCURSOR);
@ -29755,9 +29761,9 @@ yy414:
++YYCURSOR;
yy415:
YYCURSOR = yyt1;
#line 222 "../../lnav/src/data_scanner_re.re"
#line 228 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_CONSTANT); }
#line 29761 "../../lnav/src/data_scanner_re.cc"
#line 29767 "../../lnav2/src/data_scanner_re.cc"
yy416:
yych = *++YYCURSOR;
switch (yych) {
@ -30086,9 +30092,9 @@ yy422:
++YYCURSOR;
yy423:
YYCURSOR = yyt1;
#line 163 "../../lnav/src/data_scanner_re.re"
#line 169 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_TIME); }
#line 30092 "../../lnav/src/data_scanner_re.cc"
#line 30098 "../../lnav2/src/data_scanner_re.cc"
yy424:
yych = *++YYCURSOR;
switch (yych) {
@ -31042,9 +31048,9 @@ yy436:
default: goto yy438;
}
yy438:
#line 209 "../../lnav/src/data_scanner_re.re"
#line 215 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_NUMBER); }
#line 31048 "../../lnav/src/data_scanner_re.cc"
#line 31054 "../../lnav2/src/data_scanner_re.cc"
yy439:
yyaccept = 21;
yych = *(YYMARKER = ++YYCURSOR);
@ -32454,9 +32460,9 @@ yy453:
default: goto yy455;
}
yy455:
#line 161 "../../lnav/src/data_scanner_re.re"
#line 167 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_URL); }
#line 32460 "../../lnav/src/data_scanner_re.cc"
#line 32466 "../../lnav2/src/data_scanner_re.cc"
yy456:
yych = *++YYCURSOR;
switch (yych) {
@ -46940,9 +46946,9 @@ yy625:
yyt1 = yyt2;
yy626:
YYCURSOR = yyt1;
#line 164 "../../lnav/src/data_scanner_re.re"
#line 170 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_TIME); }
#line 46946 "../../lnav/src/data_scanner_re.cc"
#line 46952 "../../lnav2/src/data_scanner_re.cc"
yy627:
yyaccept = 26;
yych = *(YYMARKER = ++YYCURSOR);
@ -47282,11 +47288,11 @@ yy634:
++YYCURSOR;
yy635:
YYCURSOR = yyt1;
#line 203 "../../lnav/src/data_scanner_re.re"
#line 209 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_IPV4_ADDRESS);
}
#line 47290 "../../lnav/src/data_scanner_re.cc"
#line 47296 "../../lnav2/src/data_scanner_re.cc"
yy636:
yyaccept = 27;
yych = *(YYMARKER = ++YYCURSOR);
@ -49067,11 +49073,11 @@ yy652:
default: goto yy653;
}
yy653:
#line 172 "../../lnav/src/data_scanner_re.re"
#line 178 "../../lnav2/src/data_scanner_re.re"
{
RET(DT_DATE);
}
#line 49075 "../../lnav/src/data_scanner_re.cc"
#line 49081 "../../lnav2/src/data_scanner_re.cc"
yy654:
yyaccept = 28;
yych = *(YYMARKER = ++YYCURSOR);
@ -82935,10 +82941,10 @@ yy990:
default: goto yy991;
}
yy991:
#line 207 "../../lnav/src/data_scanner_re.re"
#line 213 "../../lnav2/src/data_scanner_re.re"
{ RET(DT_UUID); }
#line 82941 "../../lnav/src/data_scanner_re.cc"
#line 82947 "../../lnav2/src/data_scanner_re.cc"
}
#line 235 "../../lnav/src/data_scanner_re.re"
#line 241 "../../lnav2/src/data_scanner_re.re"
}

View File

@ -39,7 +39,11 @@ bool data_scanner::tokenize2(pcre_context &pc, data_token_t &token_out)
{
# define YYCTYPE unsigned char
# define CAPTURE(tok) { \
pi.pi_next_offset = YYCURSOR.val - (const unsigned char *) pi.get_string(); \
if (YYCURSOR.val == EMPTY) { \
pi.pi_next_offset = pi.pi_length; \
} else { \
pi.pi_next_offset = YYCURSOR.val - (const unsigned char *) pi.get_string(); \
} \
cap[0].c_end = pi.pi_next_offset; \
cap[1].c_end = pi.pi_next_offset; \
token_out = tok; \
@ -100,7 +104,9 @@ bool data_scanner::tokenize2(pcre_context &pc, data_token_t &token_out)
pc.set_count(2);
cap[0].c_begin = pi.pi_next_offset;
cap[0].c_end = pi.pi_next_offset;
cap[1].c_begin = pi.pi_next_offset;
cap[1].c_end = pi.pi_next_offset;
/*!re2c
re2c:yyfill:enable = 0;

View File

@ -37,6 +37,8 @@
const char *db_label_source::NULL_STR = "<NULL>";
constexpr size_t MAX_COLUMN_WIDTH = 120;
void db_label_source::text_value_for_line(textview_curses &tc, int row,
std::string &label_out,
text_sub_source::line_flags_t flags)
@ -51,14 +53,20 @@ void db_label_source::text_value_for_line(textview_curses &tc, int row,
return;
}
for (int lpc = 0; lpc < (int)this->dls_rows[row].size(); lpc++) {
int padding = (this->dls_headers[lpc].hm_column_size -
strlen(this->dls_rows[row][lpc]) -
1);
auto actual_col_size = std::min(MAX_COLUMN_WIDTH,
this->dls_headers[lpc].hm_column_size);
auto cell_str = std::string(this->dls_rows[row][lpc]);
truncate_to(cell_str, MAX_COLUMN_WIDTH);
auto cell_length = utf8_string_length(cell_str)
.unwrapOr(MAX_COLUMN_WIDTH);
auto padding = actual_col_size - cell_length;
this->dls_cell_width[lpc] = cell_str.length() + padding;
if (this->dls_headers[lpc].hm_column_type != SQLITE3_TEXT) {
label_out.append(padding, ' ');
}
label_out.append(this->dls_rows[row][lpc]);
label_out.append(cell_str);
if (this->dls_headers[lpc].hm_column_type == SQLITE3_TEXT) {
label_out.append(padding, ' ');
}
@ -79,7 +87,7 @@ void db_label_source::text_attrs_for_line(textview_curses &tc, int row,
if (row % 2 == 0) {
sa.emplace_back(lr2, &view_curses::VC_STYLE, A_BOLD);
}
lr.lr_start += this->dls_headers[lpc].hm_column_size - 1;
lr.lr_start += this->dls_cell_width[lpc];
lr.lr_end = lr.lr_start + 1;
sa.emplace_back(lr, &view_curses::VC_GRAPHIC, ACS_VLINE);
lr.lr_start += 1;
@ -122,10 +130,11 @@ void db_label_source::push_header(const std::string &colstr, int type,
bool graphable)
{
this->dls_headers.emplace_back(colstr);
this->dls_cell_width.push_back(0);
header_meta &hm = this->dls_headers.back();
hm.hm_column_size = colstr.length() + 1;
hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length());
hm.hm_column_type = type;
hm.hm_graphable = graphable;
if (colstr == "log_time") {
@ -140,12 +149,6 @@ void db_label_source::push_column(const char *colstr)
double num_value = 0.0;
size_t value_len;
if (colstr == nullptr) {
value_len = 0;
}
else {
value_len = strlen(colstr);
}
if (colstr == nullptr) {
colstr = NULL_STR;
}
@ -155,6 +158,7 @@ void db_label_source::push_column(const char *colstr)
throw "out of memory";
}
}
value_len = strlen(colstr);
if (index == this->dls_time_column_index) {
date_time_scanner dts;
@ -175,7 +179,8 @@ void db_label_source::push_column(const char *colstr)
this->dls_rows.back().push_back(colstr);
this->dls_headers[index].hm_column_size =
std::max(this->dls_headers[index].hm_column_size, strlen(colstr) + 1);
std::max(this->dls_headers[index].hm_column_size,
utf8_string_length(colstr, value_len).unwrapOr(value_len));
if (colstr != nullptr && this->dls_headers[index].hm_graphable) {
if (sscanf(colstr, "%lf", &num_value) != 1) {
@ -215,6 +220,7 @@ void db_label_source::clear()
}
this->dls_rows.clear();
this->dls_time_column.clear();
this->dls_cell_width.clear();
}
long db_label_source::column_name_to_index(const std::string &name) const
@ -365,13 +371,25 @@ bool db_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
for (size_t lpc = 0;
lpc < this->dos_labels->dls_headers.size();
lpc++) {
int before, total_fill =
dls->dls_headers[lpc].hm_column_size -
dls->dls_headers[lpc].hm_name.length();
auto actual_col_size = std::min(
MAX_COLUMN_WIDTH, dls->dls_headers[lpc].hm_column_size);
std::string cell_title = dls->dls_headers[lpc].hm_name;
struct line_range header_range(line.length(),
line.length() +
dls->dls_headers[lpc].hm_column_size);
truncate_to(cell_title, MAX_COLUMN_WIDTH);
auto cell_length = utf8_string_length(cell_title)
.unwrapOr(actual_col_size);
int before, total_fill = actual_col_size - cell_length;
auto line_len_before = line.length();
before = total_fill / 2;
total_fill -= before;
line.append(before, ' ');
line.append(cell_title);
line.append(total_fill, ' ');
line.append(1, ' ');
struct line_range header_range(line_len_before, line.length());
int attrs =
vc.attrs_for_ident(dls->dls_headers[lpc].hm_name) | A_REVERSE;
@ -379,12 +397,6 @@ bool db_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
attrs = A_UNDERLINE;
}
sa.emplace_back(header_range, &view_curses::VC_STYLE, attrs);
before = total_fill / 2;
total_fill -= before;
line.append(before, ' ');
line.append(dls->dls_headers[lpc].hm_name);
line.append(total_fill, ' ');
}
struct line_range lr(0);

View File

@ -61,7 +61,7 @@ public:
size_t retval = 0;
for (auto &dls_header : this->dls_headers) {
retval += dls_header.hm_column_size;
retval += dls_header.hm_column_size + 1;
}
return retval;
};
@ -75,8 +75,6 @@ public:
void push_header(const std::string &colstr, int type, bool graphable);
/* TODO: add support for left and right justification... numbers should */
/* be right justified and strings should be left. */
void push_column(const char *colstr);
void clear();
@ -117,8 +115,9 @@ public:
stacked_bar_chart<std::string> dls_chart;
std::vector<header_meta> dls_headers;
std::vector<std::vector<const char *> > dls_rows;
std::vector<std::vector<const char *>> dls_rows;
std::vector<struct timeval> dls_time_column;
std::vector<size_t> dls_cell_width;
int dls_time_column_index{-1};
static const char *NULL_STR;

View File

@ -669,8 +669,8 @@ static void floorFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
** string that constains s contatenated n times
*/
static void replicateFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
unsigned char *z; /* input string */
unsigned char *zo; /* result string */
static const char *EMPTY = "";
unsigned char *z; /* result string */
i64 iCount; /* times to repeat */
i64 nLen; /* length of the input string (no multibyte considerations) */
i64 nTLen; /* length of the result string (no multibyte considerations) */
@ -683,28 +683,29 @@ static void replicateFunc(sqlite3_context *context, int argc, sqlite3_value **ar
if( iCount<0 ){
sqlite3_result_error(context, "domain error", -1);
}else{
return;
}
if (iCount == 0) {
sqlite3_result_text(context, EMPTY, 0, SQLITE_STATIC);
return;
}
nLen = sqlite3_value_bytes(argv[0]);
nTLen = nLen*iCount;
z= (unsigned char *) sqlite3_malloc(nTLen + 1);
zo= (unsigned char *) sqlite3_malloc(nLen + 1);
if (!z || !zo){
if (!z){
sqlite3_result_error_nomem(context);
if (z) sqlite3_free(z);
if (zo) sqlite3_free(zo);
return;
}
strcpy((char*)zo, (char*)sqlite3_value_text(argv[0]));
auto zo = sqlite3_value_text(argv[0]);
for(i=0; i<iCount; ++i){
strcpy((char*)(z+i*nLen), (char*)zo);
}
sqlite3_result_text(context, (char*)z, -1, SQLITE_TRANSIENT);
sqlite3_free(z);
sqlite3_free(zo);
}
sqlite3_result_text(context, (char*)z, -1, sqlite3_free);
}
/*

View File

@ -45,6 +45,11 @@ enum class help_context_t {
HC_SQL_TABLE_VALUED_FUNCTION,
};
enum class help_function_type_t {
HFT_REGULAR,
HFT_AGGREGATE,
};
enum class help_nargs_t {
HN_REQUIRED,
HN_OPTIONAL,
@ -82,6 +87,8 @@ struct help_text {
std::vector<const char *> ht_enum_values;
std::vector<const char *> ht_tags;
std::vector<const char *> ht_opposites;
help_function_type_t ht_function_type{help_function_type_t::HFT_REGULAR};
void *ht_impl{nullptr};
help_text() = default;
@ -104,6 +111,12 @@ struct help_text {
return *this;
};
help_text &sql_agg_function() noexcept {
this->ht_context = help_context_t::HC_SQL_FUNCTION;
this->ht_function_type = help_function_type_t::HFT_AGGREGATE;
return *this;
};
help_text &sql_table_valued_function() noexcept {
this->ht_context = help_context_t::HC_SQL_TABLE_VALUED_FUNCTION;
return *this;
@ -171,6 +184,12 @@ struct help_text {
help_text &with_opposites(const std::initializer_list<const char*> &opps) noexcept;
template<typename F>
help_text &with_impl(F impl) {
this->ht_impl = (void *) impl;
return *this;
}
void index_tags();
static std::multimap<std::string, help_text *> TAGGED;

View File

@ -466,6 +466,9 @@ static std::string link_name(const help_text &ht)
} else {
scrubbed_name = ht.ht_name;
}
if (ht.ht_function_type == help_function_type_t::HFT_AGGREGATE) {
scrubbed_name += "_agg";
}
for (auto &param : ht.ht_parameters) {
if (!is_sql_infix && param.ht_name[0]) {
continue;

View File

@ -44,7 +44,7 @@
:alt-msg Press t to switch to the text view
**See Also**
:ref:`echo`, :ref:`eval`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`echo`, :ref:`eval`, :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`
----
@ -67,7 +67,7 @@
:append-to /tmp/interesting-lines.txt
**See Also**
:ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`echo`, :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`
----
@ -214,7 +214,7 @@
:create-logline-table task_durations
**See Also**
:ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`
:ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`
----
@ -238,7 +238,7 @@
:create-search-table task_durations duration=(?<duration>\d+)
**See Also**
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`delete_search_table`, :ref:`delete_search_table`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`delete_search_table`, :ref:`delete_search_table`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`
----
@ -295,7 +295,7 @@
:delete-logline-table task_durations
**See Also**
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`
----
@ -318,7 +318,7 @@
:delete-search-table task_durations
**See Also**
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`
:ref:`create_logline_table`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`create_search_table`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`
----
@ -387,7 +387,7 @@
:echo *msg*
^^^^^^^^^^^
Echo the given message
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**
* **msg\*** --- The message to display
@ -400,7 +400,7 @@
:echo Hello, World!
**See Also**
:ref:`alt_msg`, :ref:`eval`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :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`
----
@ -452,12 +452,6 @@
* **command\*** --- The command or query to perform substitution on.
**Examples**
To output the user's home directory:
.. code-block:: lnav
:eval :echo $HOME
To substitute the table name from a variable:
.. code-block:: lnav
@ -465,7 +459,7 @@
:eval ;SELECT * FROM ${table}
**See Also**
:ref:`alt_msg`, :ref:`echo`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`echo`, :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`
----
@ -848,7 +842,7 @@
:pipe-line-to sed -e 's/foo/bar/g'
**See Also**
:ref:`append_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`append_to`, :ref:`echo`, :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`
----
@ -871,7 +865,7 @@
:pipe-to sed -e s/foo/bar/g
**See Also**
:ref:`append_to`, :ref:`pipe_line_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_to`
:ref:`append_to`, :ref:`echo`, :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`
----
@ -980,7 +974,7 @@
:redirect-to *\[path\]*
^^^^^^^^^^^^^^^^^^^^^^^
Redirect the output of commands to the given file
Redirect the output of commands that write to stdout to the given file
**Parameters**
* **path** --- The path to the file to write. If not specified, the current redirect will be cleared
@ -993,7 +987,7 @@
:redirect-to /tmp/script-output.txt
**See Also**
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`write_cols_to`, :ref:`write_cols_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_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :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`
----
@ -1352,12 +1346,12 @@
----
.. _write_cols_to:
.. _write_table_to:
:write-cols-to *path*
^^^^^^^^^^^^^^^^^^^^^
:write-table-to *path*
^^^^^^^^^^^^^^^^^^^^^^
Write SQL results to the given file in a columnar format
Write SQL results to the given file in a tabular format
**Parameters**
* **path\*** --- The path to the file to write
@ -1367,10 +1361,10 @@
.. code-block:: lnav
:write-cols-to /tmp/table.txt
: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:`eval`, :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:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :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_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`
----
@ -1393,7 +1387,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:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_to`, :ref:`write_cols_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:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :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`
----
@ -1416,7 +1410,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:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_to`, :ref:`write_cols_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_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :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_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`
----
@ -1439,7 +1433,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:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_to`, :ref:`write_cols_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_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :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_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`
----
@ -1462,7 +1456,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:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_to`, :ref:`write_cols_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_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :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_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`
----
@ -1485,7 +1479,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:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_to`, :ref:`write_cols_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_to`, :ref:`write_to`
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :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_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`
----
@ -1508,7 +1502,7 @@
:write-to /tmp/interesting-lines.txt
**See Also**
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_cols_to`, :ref:`write_cols_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:`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`
----

View File

@ -771,7 +771,7 @@ char(*X*)
HI
**See Also**
:ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -804,7 +804,7 @@ charindex(*needle*, *haystack*, *\[start\]*)
0
**See Also**
:ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1068,7 +1068,7 @@ endswith(*str*, *suffix*)
0
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1123,7 +1123,7 @@ extract(*str*)
{"col_0":1.0,"col_1":2.0}
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1273,12 +1273,12 @@ group_concat(*X*, *\[sep\]*)
hw,gw
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
.. _group_spooky_hash:
.. _group_spooky_hash_agg:
group_spooky_hash(*str*)
^^^^^^^^^^^^^^^^^^^^^^^^
@ -1297,7 +1297,7 @@ group_spooky_hash(*str*)
4e7a190aead058cb123c94290f29c34a
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1324,6 +1324,30 @@ hex(*X*)
----
.. _humanize_file_size:
humanize_file_size(*value*)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Format the given file size as a human-friendly string
**Parameters**
* **value\*** --- The file size to format
**Examples**
To format an amount:
.. code-block:: custsqlite
;SELECT humanize_file_size(10 * 1024 * 1024)
10.0MB
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
.. _ifnull:
ifnull(*X*, *Y*)
@ -1367,7 +1391,7 @@ instr(*haystack*, *needle*)
2
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1720,7 +1744,7 @@ leftstr(*str*, *N*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1744,7 +1768,7 @@ length(*str*)
3
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1912,7 +1936,7 @@ lower(*str*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1944,7 +1968,7 @@ ltrim(*str*, *\[chars\]*)
c
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2101,7 +2125,7 @@ padc(*str*, *len*)
abcdef ghi
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2133,7 +2157,7 @@ padl(*str*, *len*)
abcdef
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2165,7 +2189,7 @@ padr(*str*, *len*)
abcdefghi
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2263,7 +2287,7 @@ printf(*format*, *X*)
value: 00011
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2287,7 +2311,7 @@ proper(*str*)
Hello, World!
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2470,7 +2494,7 @@ regexp_capture(*string*, *pattern*)
1 2 3 8 9 2
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2509,7 +2533,7 @@ regexp_match(*re*, *str*)
{"num":123,"str":"four"}
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2542,7 +2566,7 @@ regexp_replace(*str*, *re*, *repl*)
<123> <abc>
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2575,7 +2599,7 @@ replace(*str*, *old*, *replacement*)
zbc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2600,7 +2624,7 @@ replicate(*str*, *N*)
abcabcabc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2624,7 +2648,7 @@ reverse(*str*)
cba
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2656,7 +2680,7 @@ rightstr(*str*, *N*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2713,7 +2737,7 @@ row_number()
.. code-block:: custsqlite
;SELECT row_number() OVER (PARTITION BY ex_procname ORDER BY log_line) AS msg_num, ex_procname, log_body FROM lnav_example_log
msg_num ex_procname log_body
msg_num ex_procname log_body
1 gw Goodbye, World!
2 gw Goodbye, World!
3 gw Goodbye, World!
@ -2752,7 +2776,7 @@ rtrim(*str*, *\[chars\]*)
a
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2795,6 +2819,56 @@ sign(*num*)
----
.. _sparkline:
sparkline(*value*, *\[upper\]*)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Converts a numeric value on a range to a bar chart character
**Parameters**
* **value\*** --- The numeric value to convert
* **upper** --- The upper bound of the numeric range (default: 100)
**Examples**
To get the unicode block element for the value 32 in the range of 0-128:
.. code-block:: custsqlite
;SELECT sparkline(32, 128)
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
.. _sparkline_agg:
sparkline(*value*, *\[upper\]*)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
An aggregate function to convert numeric values to a sparkline bar chart
**Parameters**
* **value\*** --- The numeric values to chart
* **upper** --- The upper bound of the numeric range. If not provided, the default is derived from all of the provided values
**Examples**
To chart the values in a JSON array:
.. code-block:: custsqlite
;SELECT sparkline(value) FROM json_each('[0, 1, 2, 3, 4, 5, 6, 7, 8]')
▁▂▃▄▅▆▇█
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
.. _spooky_hash:
spooky_hash(*str*)
@ -2835,7 +2909,7 @@ spooky_hash(*str*)
f96b3d9c1a19f4394c97a1b79b1880df
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2949,7 +3023,7 @@ startswith(*str*, *prefix*)
0
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2974,7 +3048,7 @@ strfilter(*source*, *include*)
bcbc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3061,7 +3135,7 @@ substr(*str*, *start*, *\[size\]*)
b
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3184,11 +3258,8 @@ timeslice(*time*, *slice*)
.. code-block:: custsqlite
;SELECT timeslice(log_time, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY slice
slice count(*)
2017-02-03 04:05:00.000 2
2017-02-03 04:25:00.000 1
2017-02-03 04:55:00.000 1
;SELECT timeslice(log_time_msecs, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY slice
2017-01-01 05:00:00.000
**See Also**
:ref:`date`, :ref:`datetime`, :ref:`julianday`, :ref:`strftime`, :ref:`time`, :ref:`timediff`
@ -3258,7 +3329,7 @@ trim(*str*, *\[chars\]*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3311,7 +3382,7 @@ unicode(*X*)
97
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath`
----
@ -3349,7 +3420,7 @@ upper(*str*)
ABC
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath`
----
@ -3371,7 +3442,7 @@ xpath(*xpath*, *xmldoc*)
.. code-block:: custsqlite
;SELECT * FROM xpath('/abc/def', '<abc><def a="b">Hello</def><def>Bye</def></abc>')
result node_path node_attr node_text
result node_path node_attr node_text
<def a="b">Hello</def> /abc/def[1] {"a":"b"} Hello
<def>Bye</def> /abc/def[2] {} Bye
@ -3380,7 +3451,7 @@ xpath(*xpath*, *xmldoc*)
.. code-block:: custsqlite
;SELECT * FROM xpath('/abc/def/@a', '<abc><def a="b">Hello</def><def>Bye</def></abc>')
result node_path node_attr node_text
result node_path node_attr node_text
b /abc/def[1]/@a {"a":"b"} Hello
To select the text nodes on the path '/abc/def':
@ -3388,11 +3459,11 @@ xpath(*xpath*, *xmldoc*)
.. code-block:: custsqlite
;SELECT * FROM xpath('/abc/def/text()', '<abc><def a="b">Hello &#x2605;</def></abc>')
result node_path node_attr node_text
Hello ★ /abc/def/text() {} Hello ★
result node_path node_attr node_text
Hello ★ /abc/def/text() {} Hello ★
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline_agg`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`
----

View File

@ -256,6 +256,7 @@ bool setup_logline_table(exec_context &ec)
{
// Hidden columns don't show up in the table_info pragma.
static const char *hidden_table_columns[] = {
"log_time_msecs",
"log_path",
"log_text",
"log_body",
@ -2617,7 +2618,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
return EXIT_FAILURE;
}
init_session();
lnav_data.ld_exec_context.ec_output_stack.back() = stdout;
lnav_data.ld_exec_context.set_output("stdout", stdout);
alerter::singleton().enabled(false);
log_tc = &lnav_data.ld_views[LNV_LOG];

View File

@ -666,12 +666,8 @@ static Result<string, string> com_save_to(exec_context &ec, string cmdline, vect
vector<string> split_args;
shlex lexer(fn);
scoped_resolver scopes = {
&ec.ec_local_vars.top(),
&ec.ec_global_vars,
};
if (!lexer.split(split_args, scopes)) {
if (!lexer.split(split_args, ec.create_resolver())) {
return ec.make_error("unable to parse arguments");
}
if (split_args.size() > 1) {
@ -685,16 +681,15 @@ static Result<string, string> com_save_to(exec_context &ec, string cmdline, vect
mode = "w";
}
textview_curses * tc = *lnav_data.ld_view_stack.top();
bookmark_vector<vis_line_t> &bv =
tc->get_bookmarks()[&textview_curses::BM_USER];
db_label_source &dls = lnav_data.ld_db_row_source;
db_overlay_source &dos = lnav_data.ld_db_overlay;
auto *tc = *lnav_data.ld_view_stack.top();
auto &bv = tc->get_bookmarks()[&textview_curses::BM_USER];
auto &dls = lnav_data.ld_db_row_source;
if (args[0] == "write-csv-to" ||
args[0] == "write-json-to" ||
args[0] == "write-jsonlines-to" ||
args[0] == "write-cols-to") {
args[0] == "write-cols-to" ||
args[0] == "write-table-to") {
if (dls.dls_headers.empty()) {
return ec.make_error("no query result to write, use ';' to execute a query");
}
@ -791,26 +786,84 @@ static Result<string, string> com_save_to(exec_context &ec, string cmdline, vect
line_count += 1;
}
}
else if (args[0] == "write-cols-to") {
attr_line_t header_line;
else if (args[0] == "write-cols-to" || args[0] == "write-table-to") {
bool first = true;
dos.list_value_for_overlay(lnav_data.ld_views[LNV_DB], 0, 1, 0_vl, header_line);
fputs(header_line.get_string().c_str(), outfile);
fputc('\n', outfile);
for (size_t lpc = 0; lpc < dls.text_line_count(); lpc++) {
if (ec.ec_dry_run && lpc > 10) {
fprintf(outfile, "\u250f");
for (const auto& hdr : dls.dls_headers) {
auto cell_line = repeat("\u2501", hdr.hm_column_size);
if (!first) {
fprintf(outfile, "\u2533");
}
fprintf(outfile, "%s", cell_line.c_str());
first = false;
}
fprintf(outfile, "\u2513\n");
for (const auto& hdr : dls.dls_headers) {
auto centered_hdr = center_str(hdr.hm_name, hdr.hm_column_size);
fprintf(outfile, "\u2503");
fprintf(outfile, "%s", centered_hdr.c_str());
}
fprintf(outfile, "\u2503\n");
first = true;
fprintf(outfile, "\u2521");
for (const auto& hdr : dls.dls_headers) {
auto cell_line = repeat("\u2501", hdr.hm_column_size);
if (!first) {
fprintf(outfile, "\u2547");
}
fprintf(outfile, "%s", cell_line.c_str());
first = false;
}
fprintf(outfile, "\u2529\n");
for (size_t row = 0; row < dls.text_line_count(); row++) {
if (ec.ec_dry_run && row > 10) {
break;
}
string line;
for (size_t col = 0; col < dls.dls_headers.size(); col++) {
const auto& hdr = dls.dls_headers[col];
dls.text_value_for_line(lnav_data.ld_views[LNV_DB], lpc, line,
text_sub_source::RF_RAW);
fputs(line.c_str(), outfile);
fputc('\n', outfile);
fprintf(outfile, "\u2502");
auto cell = dls.dls_rows[row][col];
auto cell_byte_len = strlen(cell);
auto cell_length = utf8_string_length(cell, cell_byte_len)
.unwrapOr(cell_byte_len);
auto padding = hdr.hm_column_size - cell_length;
if (hdr.hm_column_type != SQLITE3_TEXT) {
fprintf(outfile, "%s", std::string(padding, ' ').c_str());
}
fprintf(outfile, "%s", cell);
if (hdr.hm_column_type == SQLITE3_TEXT) {
fprintf(outfile, "%s", std::string(padding, ' ').c_str());
}
}
fprintf(outfile, "\u2502\n");
line_count += 1;
}
first = true;
fprintf(outfile, "\u2514");
for (const auto& hdr : dls.dls_headers) {
auto cell_line = repeat("\u2501", hdr.hm_column_size);
if (!first) {
fprintf(outfile, "\u2534");
}
fprintf(outfile, "%s", cell_line.c_str());
first = false;
}
fprintf(outfile, "\u2518\n");
}
else if (args[0] == "write-json-to") {
yajlpp_gen gen;
@ -1123,7 +1176,7 @@ static Result<string, string> com_redirect_to(exec_context &ec, string cmdline,
return Ok(string("info: redirect will be cleared"));
}
ec.ec_output_stack.back() = nonstd::nullopt;
ec.clear_output();
return Ok(string("info: cleared redirect"));
}
@ -1146,14 +1199,19 @@ static Result<string, string> com_redirect_to(exec_context &ec, string cmdline,
return Ok("info: output will be redirected to -- " + split_args[0]);
}
FILE *file = fopen(split_args[0].c_str(), "w");
nonstd::optional<FILE *> file;
if (file == nullptr) {
return ec.make_error("unable to open file -- {}", split_args[0]);
if (split_args[0] == "-") {
ec.clear_output();
} else {
FILE *file = fopen(split_args[0].c_str(), "w");
if (file == nullptr) {
return ec.make_error("unable to open file -- {}", split_args[0]);
}
ec.set_output(split_args[0], file);
}
ec.ec_output_stack.back() = file;
return Ok("info: redirecting output to file -- " + split_args[0]);
}
@ -3396,20 +3454,24 @@ static Result<string, string> com_echo(exec_context &ec, string cmdline, vector<
}
else if (args.size() >= 1) {
bool lf = true;
string src;
if (args.size() > 2 && args[1] == "-n") {
string::size_type index_in_cmdline = cmdline.find(args[1]);
lf = false;
retval = cmdline.substr(index_in_cmdline + args[1].length() + 1);
src = cmdline.substr(index_in_cmdline + args[1].length() + 1);
}
else if (args.size() >= 2) {
retval = cmdline.substr(args[0].length() + 1);
src = cmdline.substr(args[0].length() + 1);
}
else {
retval = "";
src = "";
}
auto lexer = shlex(src);
lexer.eval(retval, ec.create_resolver());
auto ec_out = ec.get_output();
if (ec.ec_dry_run) {
lnav_data.ld_preview_status_source.get_description()
@ -4804,11 +4866,11 @@ readline_context::command_t STD_COMMANDS[] = {
})
},
{
"write-cols-to",
"write-table-to",
com_save_to,
help_text(":write-cols-to")
.with_summary("Write SQL results to the given file in a columnar format")
help_text(":write-table-to")
.with_summary("Write SQL results to the given file in a tabular format")
.with_parameter(help_text("path", "The path to the file to write"))
.with_tags({"io", "scripting", "sql"})
.with_example({
@ -4873,7 +4935,8 @@ readline_context::command_t STD_COMMANDS[] = {
com_redirect_to,
help_text(":redirect-to")
.with_summary("Redirect the output of commands to the given file")
.with_summary("Redirect the output of commands that write to "
"stdout to the given file")
.with_parameter(help_text(
"path", "The path to the file to write."
" If not specified, the current redirect will be cleared")
@ -5256,9 +5319,13 @@ readline_context::command_t STD_COMMANDS[] = {
com_echo,
help_text(":echo")
.with_summary("Echo the given message")
.with_summary(
"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 '$'")
.with_parameter(help_text("msg", "The message to display"))
.with_tags({"scripting"})
.with_tags({"io", "scripting"})
.with_example({
"To output 'Hello, World!'",
"Hello, World!"
@ -5289,10 +5356,6 @@ readline_context::command_t STD_COMMANDS[] = {
.with_tags({"scripting"})
.with_examples(
{
{
"To output the user's home directory",
":echo $HOME"
},
{
"To substitute the table name from a variable",
";SELECT * FROM ${table}"
@ -5354,6 +5417,7 @@ readline_context::command_t STD_COMMANDS[] = {
static unordered_map<char const *, vector<char const *>> aliases = {
{ "quit", { "q", "q!" } },
{ "write-table-to", { "write-cols-to", }}
};
void init_lnav_commands(readline_context::command_map_t &cmd_map)

View File

@ -2099,7 +2099,7 @@ public:
virtual bool next(log_cursor &lc, logfile_sub_source &lss)
{
lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1);
lc.lc_curr_line = lc.lc_curr_line + 1_vl;
lc.lc_sub_index = 0;
if (lc.is_eof()) {
@ -2107,7 +2107,7 @@ public:
}
content_line_t cl(lss.at(lc.lc_curr_line));
shared_ptr<logfile> lf = lss.find(cl);
auto lf = lss.find_file_ptr(cl);
auto lf_iter = lf->begin() + cl;
uint8_t mod_id = lf_iter->get_module_id();
@ -2115,12 +2115,12 @@ public:
return false;
}
auto format = lf->get_format();
this->elt_module_format.mf_mod_format = nullptr;
if (format->get_name() == this->lfvi_format.get_name()) {
if (lf->get_format_name() == this->lfvi_format.get_name()) {
return true;
} else if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
auto format = lf->get_format();
return lf->read_line(lf_iter).map([this, format, cl](auto line) {
std::vector<logline_value> values;
shared_buffer_ref body_ref;

View File

@ -60,6 +60,7 @@ static const char *LOG_COLUMNS = R"( (
static const char *LOG_FOOTER_COLUMNS = R"(
-- END Format-specific fields
log_time_msecs INTEGER HIDDEN, -- The adjusted timestamp for the log message as the number of milliseconds from the epoch
log_path TEXT HIDDEN COLLATE naturalnocase, -- The path to the log file this message is from
log_text TEXT HIDDEN, -- The full text of the log message
log_body TEXT HIDDEN -- The body of the log message
@ -518,6 +519,10 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
switch (post_col_number) {
case 0: {
sqlite3_result_int64(ctx, ll->get_time_in_millis());
break;
}
case 1: {
const string &fn = lf->get_filename();
sqlite3_result_text(ctx,
@ -526,7 +531,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
SQLITE_STATIC);
break;
}
case 1: {
case 2: {
shared_buffer_ref line;
lf->read_full_message(ll, line);
@ -536,7 +541,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
SQLITE_TRANSIENT);
break;
}
case 2: {
case 3: {
if (vc->line_values.empty()) {
lf->read_full_message(ll, vc->log_msg);
vt->vi->extract(lf, line_number, vc->log_msg, vc->line_values);

View File

@ -137,6 +137,7 @@ public:
keys_inout.emplace_back("log_line");
keys_inout.emplace_back("min(log_line)");
keys_inout.emplace_back("log_mark");
keys_inout.emplace_back("log_time_msecs");
};
virtual void extract(std::shared_ptr<logfile> lf,

View File

@ -651,3 +651,12 @@ logfile::read_raw_message(logfile::const_iterator ll)
return this->lf_line_buffer.read_range(this->get_file_range(ll));
}
intern_string_t logfile::get_format_name() const
{
if (this->lf_format) {
return this->lf_format->get_name();
}
return {};
}

View File

@ -164,6 +164,8 @@ public:
*/
std::shared_ptr<log_format> get_format() const { return this->lf_format; };
intern_string_t get_format_name() const;
text_format_t get_text_format() const {
return this->lf_text_format;
}

View File

@ -1068,6 +1068,10 @@ bool logfile_sub_source::eval_sql_filter(sqlite3_stmt *stmt, iterator ld, logfil
SQLITE_STATIC);
continue;
}
if (strcmp(name, ":log_time_msecs") == 0) {
sqlite3_bind_int64(stmt, lpc + 1, ll->get_time_in_millis());
continue;
}
if (strcmp(name, ":log_mark") == 0) {
sqlite3_bind_int(stmt, lpc + 1, ll->is_marked());
continue;

View File

@ -501,6 +501,14 @@ public:
return retval;
};
logfile *find_file_ptr(content_line_t &line)
{
auto retval = this->lss_files[line / MAX_LINES_PER_FILE]->get_file_ptr();
line = content_line_t(line % MAX_LINES_PER_FILE);
return retval;
};
logline *find_line(content_line_t line)
{
logline *retval = nullptr;
@ -599,7 +607,7 @@ public:
{
this->ld_filter_state.lfo_filter_state.clear();
};
\
void set_file(const std::shared_ptr<logfile> &lf) {
this->ld_filter_state.lfo_filter_state.tfs_logfile = lf;
lf->set_logline_observer(&this->ld_filter_state);
@ -609,6 +617,10 @@ public:
return this->ld_filter_state.lfo_filter_state.tfs_logfile;
};
logfile *get_file_ptr() const {
return this->ld_filter_state.lfo_filter_state.tfs_logfile.get();
};
bool is_visible() const {
return this->ld_visible;
}

View File

@ -200,7 +200,6 @@ bool rl_sql_help(readline_curses *rc)
auto vtab_module_iter = vtab_module_ddls.find(intern_ident);
string ddl;
log_debug("ident %s", ident.c_str());
if (vtab != nullptr) {
ddl = trim(vtab->get_table_statement());
} else if (vtab_module_iter != vtab_module_ddls.end()) {
@ -367,7 +366,6 @@ static void rl_search_internal(readline_curses *rc, ln_mode_t mode, bool complet
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
tc->reload_data();
log_debug("rl_search_int");
switch (mode) {
case LNM_SEARCH:
case LNM_SEARCH_FILTERS:
@ -633,22 +631,23 @@ static void rl_callback_int(readline_curses *rc, bool is_alt)
rc->set_value("Unable to open temporary output file: " +
string(strerror(errno)));
} else {
auto_fd fd(fileno(tmpout));
auto_fd fd_copy((const auto_fd &) fd);
auto fd_copy = auto_fd::dup_of(fileno(tmpout));
char desc[256], timestamp[32];
time_t current_time = time(NULL);
time_t current_time = time(nullptr);
string path_and_args = rc->get_value();
ec.ec_output_stack.back() = tmpout.in();
string result = execute_file(ec, path_and_args)
.map(ok_prefix)
.orElse(err_to_ok).unwrap();
string::size_type lf_index = result.find('\n');
if (lf_index != string::npos) {
result = result.substr(0, lf_index);
{
exec_context::output_guard og(ec, "tmp", tmpout.release());
string result = execute_file(ec, path_and_args)
.map(ok_prefix)
.orElse(err_to_ok).unwrap();
string::size_type lf_index = result.find('\n');
if (lf_index != string::npos) {
result = result.substr(0, lf_index);
}
rc->set_value(result);
}
rc->set_value(result);
ec.ec_output_stack.back() = nonstd::nullopt;
struct stat st;
@ -666,7 +665,7 @@ static void rl_callback_int(readline_curses *rc, bool is_alt)
.with_detect_format(false);
lnav_data.ld_files_to_front.emplace_back(desc, 0);
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(X, "to close the file"));
}

View File

@ -223,6 +223,7 @@ void add_filter_expr_possibilities(readline_curses *rlc, int context, const std:
static const char *BUILTIN_VARS[] = {
":log_level",
":log_time",
":log_time_msecs",
":log_mark",
":log_comment",
":log_tags",

View File

@ -274,7 +274,7 @@ public:
}
};
int64_t to_microseconds() {
int64_t to_microseconds() const {
int64_t retval;
retval = this->rt_field[RTF_YEARS].value * 12;

View File

@ -20,4 +20,4 @@
(SELECT lt2.start_time AS end_time FROM lease_times AS lt2 WHERE lt1.start_time < lt2.start_time LIMIT 1) AS end_time,
ip
FROM lease_times AS lt1)
:write-cols-to -
:write-table-to -

View File

@ -5,6 +5,8 @@
# @description: Partition the log view based on boot messages from the Linux kernel.
#
;UPDATE syslog_log SET log_part = 'Boot: ' || log_time WHERE log_text LIKE '%kernel: Linux version%';
;UPDATE syslog_log
SET log_part = 'Boot: ' || log_time
WHERE log_text LIKE '%kernel:%Linux version%';
;SELECT 'Created ' || changes() || ' partitions(s)';

View File

@ -193,3 +193,22 @@ void shlex::scan_variable_ref(pcre_context::capture_t &cap_out,
token_out = shlex_token_t::ST_ERROR;
}
}
void shlex::resolve_home_dir(std::string &result,
const pcre_context::capture_t cap) const
{
if (cap.length() == 1) {
result.append(getenv_opt("HOME").value_or("~"));
} else {
auto username = (char *) alloca(cap.length());
memcpy(username, &this->s_str[cap.c_begin + 1], cap.length() - 1);
username[cap.length() - 1] = '\0';
auto pw = getpwnam(username);
if (pw != nullptr) {
result.append(pw->pw_dir);
} else {
result.append(&this->s_str[cap.c_begin], cap.length());
}
}
}

View File

@ -141,6 +141,14 @@ public:
case shlex_token_t::ST_TILDE:
this->resolve_home_dir(result, cap);
break;
case shlex_token_t::ST_DOUBLE_QUOTE_START:
case shlex_token_t::ST_DOUBLE_QUOTE_END:
result.append("\"");
break;
case shlex_token_t::ST_SINGLE_QUOTE_START:
case shlex_token_t::ST_SINGLE_QUOTE_END:
result.append("'");
break;
default:
break;
}
@ -220,22 +228,7 @@ public:
void scan_variable_ref(pcre_context::capture_t &cap_out, shlex_token_t &token_out);
void resolve_home_dir(std::string& result, const pcre_context::capture_t cap) const {
if (cap.length() == 1) {
result.append(getenv_opt("HOME").value_or("~"));
} else {
auto username = (char *) alloca(cap.length());
memcpy(username, &this->s_str[cap.c_begin + 1], cap.length() - 1);
username[cap.length() - 1] = '\0';
auto pw = getpwnam(username);
if (pw != nullptr) {
result.append(pw->pw_dir);
} else {
result.append(&this->s_str[cap.c_begin], cap.length());
}
}
}
void resolve_home_dir(std::string& result, const pcre_context::capture_t cap) const;
enum class state_t {
STATE_NORMAL,

View File

@ -98,7 +98,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
agg_funcs[i].nArg,
SQLITE_UTF8,
(void *) &agg_funcs[i],
0,
nullptr,
agg_funcs[i].xStep,
agg_funcs[i].xFinalize);

View File

@ -17,6 +17,7 @@
#include "pcrepp/pcrepp.hh"
#include "base/humanize.hh"
#include "base/string_util.hh"
#include "yajlpp/yajlpp.hh"
#include "column_namer.hh"
@ -248,6 +249,60 @@ static void sql_spooky_hash_final(sqlite3_context *context)
}
}
struct sparkline_context {
bool sc_initialized{true};
double sc_max_value{0.0};
std::vector<double> sc_values;
};
static void sparkline_step(sqlite3_context *context,
int argc,
sqlite3_value **argv)
{
auto *sc = (sparkline_context *)
sqlite3_aggregate_context(context, sizeof(sparkline_context));
if (!sc->sc_initialized) {
new (sc) sparkline_context;
}
if (argc == 0) {
return;
}
sc->sc_values.push_back(sqlite3_value_double(argv[0]));
sc->sc_max_value = std::max(sc->sc_max_value, sc->sc_values.back());
if (argc >= 2) {
sc->sc_max_value = std::max(sc->sc_max_value,
sqlite3_value_double(argv[1]));
}
}
static void sparkline_final(sqlite3_context *context)
{
auto *sc = (sparkline_context *)
sqlite3_aggregate_context(context, sizeof(sparkline_context));
if (!sc->sc_initialized) {
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
return;
}
auto retval = (char *) sqlite3_malloc(sc->sc_values.size() * 3 + 1);
auto start = retval;
for (const auto& value : sc->sc_values) {
auto bar = humanize::sparkline(value, sc->sc_max_value);
strcpy(start, bar.c_str());
start += bar.length();
}
*start = '\0';
sqlite3_result_text(context, retval, -1, sqlite3_free);
}
int string_extension_functions(struct FuncDef **basic_funcs,
struct FuncDefAgg **agg_funcs)
{
@ -301,6 +356,34 @@ int string_extension_functions(struct FuncDef **basic_funcs,
})
),
sqlite_func_adapter<decltype(&humanize::file_size), humanize::file_size>::builder(
help_text("humanize_file_size",
"Format the given file size as a human-friendly string")
.sql_function()
.with_parameter({"value", "The file size to format"})
.with_tags({"string"})
.with_example({
"To format an amount",
"SELECT humanize_file_size(10 * 1024 * 1024)"
})
),
sqlite_func_adapter<decltype(&humanize::sparkline), humanize::sparkline>::builder(
help_text("sparkline",
"Converts a numeric value on a range to a bar chart character")
.sql_function()
.with_parameter({"value", "The numeric value to convert"})
.with_parameter(help_text(
"upper", "The upper bound of the numeric range (default: 100)")
.optional())
.with_tags({"string"})
.with_example({
"To get the unicode block element for the value 32 in the "
"range of 0-128",
"SELECT sparkline(32, 128)"
})
),
sqlite_func_adapter<decltype(&extract), extract>::builder(
help_text("extract",
"Automatically Parse and extract data from a string")
@ -388,7 +471,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
sql_spooky_hash_step, sql_spooky_hash_final,
help_text("group_spooky_hash",
"Compute the hash value for the given arguments")
.sql_function()
.sql_agg_function()
.with_parameter(help_text("str", "The string to hash")
.one_or_more())
.with_tags({"string"})
@ -398,6 +481,26 @@ int string_extension_functions(struct FuncDef **basic_funcs,
})
},
{
"sparkline", -1, 0,
sparkline_step, sparkline_final,
help_text("sparkline",
"An aggregate function to convert numeric values to a "
"sparkline bar chart")
.sql_agg_function()
.with_parameter({"value", "The numeric values to chart"})
.with_parameter(help_text(
"upper", "The upper bound of the numeric range. "
"If not provided, the default is derived from "
"all of the provided values")
.optional())
.with_tags({"string"})
.with_example({
"To chart the values in a JSON array",
"SELECT sparkline(value) FROM json_each('[0, 1, 2, 3, 4, 5, 6, 7, 8]')"
})
},
{nullptr}
};

View File

@ -234,15 +234,22 @@ void textview_curses::grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vi
this->tc_searching += 1;
this->tc_search_action(this);
bookmark_vector<vis_line_t> &search_bv = this->tc_bookmarks[&BM_SEARCH];
if (start != -1) {
auto pair = search_bv.equal_range(vis_line_t(start), vis_line_t(stop));
if (start != -1_vl) {
auto& search_bv = this->tc_bookmarks[&BM_SEARCH];
auto pair = search_bv.equal_range(start, stop);
if (pair.first != pair.second) {
this->set_needs_update();
}
for (auto mark_iter = pair.first;
mark_iter != pair.second;
++mark_iter) {
this->set_user_mark(&BM_SEARCH, *mark_iter, false);
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(&BM_SEARCH, *mark_iter, false);
}
}
if (pair.first != pair.second) {
search_bv.erase(pair.first, pair.second);
}
}
@ -652,6 +659,71 @@ textview_curses::horiz_shift(vis_line_t start, vis_line_t end, int off_start,
range_out = std::make_pair(prev_hit, next_hit);
}
void
textview_curses::set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked)
{
bookmark_vector<vis_line_t> &bv = this->tc_bookmarks[bm];
bookmark_vector<vis_line_t>::iterator iter;
if (marked) {
bv.insert_once(vl);
}
else {
iter = std::lower_bound(bv.begin(), bv.end(), vl);
if (iter != bv.end() && *iter == vl) {
bv.erase(iter);
}
}
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, vl, marked);
}
if (marked) {
this->search_range(vl, vl + 1_vl);
this->search_new_data();
}
this->set_needs_update();
}
void
textview_curses::toggle_user_mark(bookmark_type_t *bm, vis_line_t start_line,
vis_line_t end_line)
{
if (end_line == -1) {
end_line = start_line;
}
if (start_line > end_line) {
std::swap(start_line, end_line);
}
if (start_line >= this->get_inner_height()) {
return;
}
if (end_line >= this->get_inner_height()) {
end_line = vis_line_t(this->get_inner_height() - 1);
}
for (vis_line_t curr_line = start_line; curr_line <= end_line;
++curr_line) {
bookmark_vector<vis_line_t> &bv = this->tc_bookmarks[bm];
bookmark_vector<vis_line_t>::iterator iter;
bool added;
iter = bv.insert_once(curr_line);
if (iter == bv.end()) {
added = true;
}
else {
bv.erase(iter);
added = false;
}
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, curr_line, added);
}
}
this->search_range(start_line, end_line + 1_vl);
this->search_new_data();
}
void text_time_translator::scroll_invoked(textview_curses *tc)
{
if (tc->get_inner_height() > 0) {

View File

@ -635,64 +635,9 @@ public:
void toggle_user_mark(bookmark_type_t *bm,
vis_line_t start_line,
vis_line_t end_line = vis_line_t(-1))
{
if (end_line == -1) {
end_line = start_line;
}
if (start_line > end_line) {
std::swap(start_line, end_line);
}
vis_line_t end_line = vis_line_t(-1));;
if (start_line >= this->get_inner_height()) {
return;
}
if (end_line >= this->get_inner_height()) {
end_line = vis_line_t(this->get_inner_height() - 1);
}
for (vis_line_t curr_line = start_line; curr_line <= end_line;
++curr_line) {
bookmark_vector<vis_line_t> &bv = this->tc_bookmarks[bm];
bookmark_vector<vis_line_t>::iterator iter;
bool added;
iter = bv.insert_once(curr_line);
if (iter == bv.end()) {
added = true;
}
else {
bv.erase(iter);
added = false;
}
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, curr_line, added);
}
}
this->search_range(start_line, end_line + 1_vl);
this->search_new_data();
};
void set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked) {
bookmark_vector<vis_line_t> &bv = this->tc_bookmarks[bm];
bookmark_vector<vis_line_t>::iterator iter;
if (marked) {
bv.insert_once(vl);
}
else {
iter = std::lower_bound(bv.begin(), bv.end(), vl);
if (iter != bv.end() && *iter == vl) {
bv.erase(iter);
}
}
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, vl, marked);
}
this->search_range(vl, vl + 1_vl);
this->search_new_data();
this->set_needs_update();
};
void set_user_mark(bookmark_type_t *bm, vis_line_t vl, bool marked);;
textview_curses &set_sub_source(text_sub_source *src) {
this->tc_sub_source = src;

View File

@ -36,8 +36,10 @@
#include <stdint.h>
#include <string>
#include <unordered_map>
#include "base/date_time_scanner.hh"
#include "base/lrucache.hpp"
#include "sql_util.hh"
#include "relative_time.hh"
@ -45,47 +47,75 @@
using namespace std;
static string timeslice(const char *time_in, nonstd::optional<const char *> slice_in_opt)
static lnav::sqlite::text_buffer timeslice(sqlite3_value *time_in, nonstd::optional<const char *> slice_in_opt)
{
const char *slice_in = slice_in_opt.value_or("15m");
auto parse_res = relative_time::from_str(slice_in, strlen(slice_in));
date_time_scanner dts;
time_t now;
thread_local date_time_scanner dts;
thread_local struct {
std::string c_slice_str;
relative_time c_rel_time;
} cache;
const auto slice_in = string_fragment(slice_in_opt.value_or("15m"));
time(&now);
dts.set_base_time(now);
if (parse_res.isErr()) {
throw sqlite_func_error("unable to parse time slice value: {} -- {}",
slice_in, parse_res.unwrapErr().pe_msg);
}
auto rt = parse_res.unwrap();
if (rt.empty()) {
if (slice_in.empty()) {
throw sqlite_func_error("no time slice value given");
}
if (rt.is_absolute()) {
throw sqlite_func_error("absolute time slices are not valid");
if (slice_in != cache.c_slice_str.c_str()) {
auto parse_res = relative_time::from_str(slice_in.data());
if (parse_res.isErr()) {
throw sqlite_func_error("unable to parse time slice value: {} -- {}",
slice_in, parse_res.unwrapErr().pe_msg);
}
cache.c_rel_time = parse_res.unwrap();
if (cache.c_rel_time.empty()) {
throw sqlite_func_error("could not determine a time slice from: {}",
slice_in);
}
if (cache.c_rel_time.is_absolute()) {
throw sqlite_func_error("absolute time slices are not valid");
}
cache.c_slice_str = slice_in.to_string();
}
struct exttm tm;
int64_t us, remainder;
struct timeval tv;
if (dts.scan(time_in, strlen(time_in), nullptr, &tm, tv) == nullptr) {
throw sqlite_func_error("unable to parse time value -- {}", time_in);
switch (sqlite3_value_type(time_in)) {
case SQLITE3_TEXT: {
const char *time_in_str = reinterpret_cast<const char *>(sqlite3_value_text(
time_in));
struct exttm tm;
if (dts.scan(time_in_str, strlen(time_in_str), nullptr, &tm, tv, false) == nullptr) {
dts.unlock();
if (dts.scan(time_in_str, strlen(time_in_str), nullptr, &tm, tv, false) == nullptr) {
throw sqlite_func_error("unable to parse time value -- {}",
time_in_str);
}
}
us = tv.tv_sec * 1000000LL + tv.tv_usec;
break;
}
case SQLITE_INTEGER: {
auto msecs = sqlite3_value_int64(time_in);
us = msecs * 1000LL;
break;
}
}
int64_t us = tv.tv_sec * 1000000LL + tv.tv_usec, remainder;
remainder = us % rt.to_microseconds();
remainder = us % cache.c_rel_time.to_microseconds();
us -= remainder;
tv.tv_sec = us / (1000 * 1000);
tv.tv_usec = us % (1000 * 1000);
tv.tv_sec = us / (1000LL * 1000LL);
tv.tv_usec = us % (1000LL * 1000LL);
char ts[64];
sql_strftime(ts, sizeof(ts), tv);
auto ts = lnav::sqlite::text_buffer::alloc(64);
ts.tb_length = sql_strftime(ts.tb_value, ts.tb_length, tv);
return ts;
}
@ -132,7 +162,7 @@ int time_extension_functions(struct FuncDef **basic_funcs,
})
.with_example({
"To group log messages into five minute buckets and count them",
"SELECT timeslice(log_time, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY slice"
"SELECT timeslice(log_time_msecs, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY slice"
})
),

View File

@ -172,6 +172,27 @@ inline void to_sqlite(sqlite3_context *ctx, const char *str)
}
}
namespace lnav {
namespace sqlite {
struct text_buffer {
static text_buffer alloc(size_t size) {
return text_buffer {
(char *) malloc(size),
size,
};
}
char *tb_value;
size_t tb_length;
};
}
}
inline void to_sqlite(sqlite3_context *ctx, const lnav::sqlite::text_buffer &str)
{
sqlite3_result_text(ctx, str.tb_value, str.tb_length, free);
}
inline void to_sqlite(sqlite3_context *ctx, const std::string &str)
{
sqlite3_result_text(ctx, str.c_str(), str.length(), SQLITE_TRANSIENT);

View File

@ -1233,10 +1233,11 @@ Parameter
X The unicode code point values
See Also
charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Example
#1 To get a string with the code points 0x48 and 0x49:
;SELECT char(0x48, 0x49)
@ -1252,11 +1253,12 @@ Parameters
haystack The string to search within
start The one-based index within the haystack to start the search
See Also
char(), endswith(), extract(), group_concat(), group_spooky_hash(), instr(),
leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
char(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To search for the string 'abc' within 'abcabc' and starting at position 2:
;SELECT charindex('abc', 'abcabc', 2)
@ -1402,11 +1404,12 @@ Parameters
str The string to test
suffix The suffix to check in the string
See Also
char(), charindex(), extract(), group_concat(), group_spooky_hash(), instr(),
leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
char(), charindex(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To test if the string 'notbad.jpg' ends with '.jpg':
;SELECT endswith('notbad.jpg', '.jpg')
@ -1436,11 +1439,12 @@ Synopsis
Parameter
str The string to parse
See Also
char(), charindex(), endswith(), group_concat(), group_spooky_hash(), instr(),
leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
char(), charindex(), endswith(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To extract key/value pairs from a string:
;SELECT extract('foo=1 bar=2 name="Rolo Tomassi"')
@ -1518,11 +1522,12 @@ Parameters
X The value to concatenate.
sep The separator to place between the values.
See Also
char(), charindex(), endswith(), extract(), group_spooky_hash(), instr(),
leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
char(), charindex(), endswith(), extract(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To concatenate the values of the column 'ex_procname' from the table 'lnav_example_log'
:
@ -1545,11 +1550,12 @@ Synopsis
Parameter
str The string to hash
See Also
char(), charindex(), endswith(), extract(), group_concat(), instr(), leftstr(),
length(), lower(), ltrim(), padc(), padl(), padr(), printf(), proper(),
regexp_capture(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), spooky_hash(), startswith(), strfilter(),
substr(), trim(), unicode(), upper(), xpath()
char(), charindex(), endswith(), extract(), group_concat(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Example
#1 To produce a hash of all of the values of 'column1':
;SELECT group_spooky_hash(column1) FROM (VALUES ('abc'), ('123'))
@ -1568,6 +1574,24 @@ Example
Synopsis
humanize_file_size(value) -- Format the given file size as a human-friendly
string
Parameter
value The file size to format
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To format an amount:
;SELECT humanize_file_size(10 * 1024 * 1024)
Synopsis
ifnull(X, Y) -- Returns a copy of its first non-NULL argument, or NULL if
both arguments are NULL
@ -1590,10 +1614,11 @@ Parameters
needle The string to look for in the haystack
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), leftstr(), length(), lower(), ltrim(), padc(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To test get the position of 'b' in the string 'abc':
;SELECT instr('abc', 'b')
@ -1795,10 +1820,11 @@ Parameters
N The number of characters from the left side of the string to return.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), length(), lower(), ltrim(), padc(), padl(), padr(), printf(), proper(),
regexp_capture(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), spooky_hash(), startswith(), strfilter(),
substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), length(), lower(), ltrim(), padc(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To get the first character of the string 'abc':
;SELECT leftstr('abc', 1)
@ -1816,10 +1842,11 @@ Parameter
str The string to determine the length of
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), lower(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), lower(), ltrim(), padc(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To get the length of the string 'abc':
;SELECT length('abc')
@ -1911,10 +1938,11 @@ Parameter
str The string to convert.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), ltrim(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), ltrim(), padc(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To lowercase the string 'AbC':
;SELECT lower('AbC')
@ -1930,10 +1958,11 @@ Parameters
chars The characters to trim. Defaults to spaces.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), padc(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), padc(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To trim the leading whitespace from the string ' abc':
;SELECT ltrim(' abc')
@ -2028,10 +2057,11 @@ Parameters
len The minimum desired length of the output string
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padl(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padl(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To pad the string 'abc' to a length of six characters:
;SELECT padc('abc', 6) || 'def'
@ -2050,10 +2080,11 @@ Parameters
len The minimum desired length of the output string
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padr(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To pad the string 'abc' to a length of six characters:
;SELECT padl('abc', 6)
@ -2072,10 +2103,11 @@ Parameters
len The minimum desired length of the output string
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), printf(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To pad the string 'abc' to a length of six characters:
;SELECT padr('abc', 6) || 'def'
@ -2128,10 +2160,11 @@ Parameters
X The argument to substitute at a given position in the format.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To substitute 'World' into the string 'Hello, %s!':
;SELECT printf('Hello, %s!', 'World')
@ -2152,10 +2185,11 @@ Parameter
str The string to capitalize.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), regexp_capture(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To capitalize the words in the string 'hello, world!':
;SELECT proper('hello, world!')
@ -2256,10 +2290,11 @@ Results
content The captured value from the string.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), spooky_hash(), startswith(), strfilter(),
substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), sparkline(), sparkline(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(),
xpath()
Example
#1 To extract the key/value pairs 'a'/1 and 'b'/2 from the string 'a=1; b=2':
;SELECT * FROM regexp_capture('a=1; b=2', '(\w+)=(\d+)')
@ -2274,10 +2309,11 @@ Parameters
str The string to test against the regular expression
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_replace(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_replace(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To capture the digits from the string '123':
;SELECT regexp_match('(\d+)', '123')
@ -2304,10 +2340,11 @@ Parameters
backslash followed by the number of the group, starting with 1.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_match(), replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_match(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper(), xpath()
Examples
#1 To replace the word at the start of the string 'Hello, World!' with 'Goodbye':
;SELECT regexp_replace('Hello, World!', '^(\w+)', 'Goodbye')
@ -2328,10 +2365,11 @@ Parameters
replacement The string to replace any occurrences of the old string with.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replicate(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To replace the string 'x' with 'z' in 'abc':
;SELECT replace('abc', 'x', 'z')
@ -2349,10 +2387,11 @@ Parameters
N The number of times to replicate the string.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), reverse(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), reverse(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To repeat the string 'abc' three times:
;SELECT replicate('abc', 3)
@ -2365,10 +2404,11 @@ Parameter
str The string to reverse.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), rightstr(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), rightstr(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To reverse the string 'abc':
;SELECT reverse('abc')
@ -2383,10 +2423,11 @@ Parameters
N The number of characters from the right side of the string to return.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rtrim(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rtrim(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To get the last character of the string 'abc':
;SELECT rightstr('abc', 1)
@ -2443,10 +2484,11 @@ Parameters
chars The characters to trim. Defaults to spaces.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), spooky_hash(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), sparkline(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To trim the whitespace from the end of the string 'abc ':
;SELECT rtrim('abc ')
@ -2479,16 +2521,37 @@ Examples
Synopsis
sparkline(value, [upper]) -- An aggregate function to convert numeric values
to a sparkline bar chart
Parameters
value The numeric values to chart
upper The upper bound of the numeric range. If not provided, the default
is derived from all of the provided values
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To chart the values in a JSON array:
;SELECT sparkline(value) FROM json_each('[0, 1, 2, 3, 4, 5, 6, 7, 8]')
Synopsis
spooky_hash(str, ...) -- Compute the hash value for the given arguments.
Parameter
str The string to hash
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), startswith(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To produce a hash for the string 'Hello, World!':
;SELECT spooky_hash('Hello, World!')
@ -2557,10 +2620,11 @@ Parameters
prefix The prefix to check in the string
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
strfilter(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Examples
#1 To test if the string 'foobar' starts with 'foo':
;SELECT startswith('foobar', 'foo')
@ -2579,10 +2643,11 @@ Parameters
include The characters to include in the result
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), substr(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), substr(), trim(),
unicode(), upper(), xpath()
Example
#1 To get the 'b', 'c', and 'd' characters from the string 'abcabc':
;SELECT strfilter('abcabc', 'bcd')
@ -2627,10 +2692,11 @@ Parameters
the characters before the start are returned.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), trim(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), trim(),
unicode(), upper(), xpath()
Examples
#1 To get the substring starting at the second character until the end of the string 'abc'
:
@ -2719,8 +2785,8 @@ Examples
#2 To group log messages into five minute buckets and count them:
;SELECT timeslice(log_time, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY
slice
;SELECT timeslice(log_time_msecs, '5m') AS slice, count(*) FROM lnav_example_log GROUP
BY slice
@ -2753,10 +2819,11 @@ Parameters
chars The characters to trim. Defaults to spaces.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), unicode(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath()
Examples
#1 To trim whitespace from the start and end of the string ' abc ':
;SELECT trim(' abc ')
@ -2790,10 +2857,11 @@ Parameter
X The string to examine.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), trim(), upper(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), upper(), xpath()
Example
#1 To get the unicode code point for the first character of 'abc':
;SELECT unicode('abc')
@ -2813,10 +2881,11 @@ Parameter
str The string to convert.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), trim(), unicode(), xpath()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), xpath()
Example
#1 To uppercase the string 'aBc':
;SELECT upper('aBc')
@ -2836,10 +2905,11 @@ Results
node_text The node's text value.
See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), spooky_hash(),
startswith(), strfilter(), substr(), trim(), unicode(), upper()
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
sparkline(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
trim(), unicode(), upper()
Examples
#1 To select the XML nodes on the path '/abc/def':
;SELECT * FROM xpath('/abc/def', '<abc><def a="b">Hello</def><def>Bye</def></abc>')

View File

@ -1,3 +1,3 @@
;SELECT 'World' as name
:eval :echo Hello, ${name}!
:echo Hello, ${name}!
Goodbye, ${name}!

View File

@ -1,2 +1,2 @@
:eval :echo nested here $0 $1 $2
:echo nested here $0 $1 $2

View File

@ -41,16 +41,43 @@ int main(int argc, char *argv[])
int lpc, retval = EXIT_SUCCESS;
bookmark_vector<vis_line_t> bv, bv_cp;
bv.insert_once(vis_line_t(1));
bv.insert_once(vis_line_t(1));
bv.insert_once(vis_line_t(2));
bv.insert_once(vis_line_t(2));
assert(bv.size() == 1);
bv.insert_once(vis_line_t(4));
bv.insert_once(vis_line_t(3));
bv.insert_once(vis_line_t(2));
assert(bv[0] == 1);
assert(bv[1] == 2);
assert(bv[2] == 3);
assert(bv[0] == 2);
assert(bv[1] == 3);
assert(bv[2] == 4);
{
auto range = bv.equal_range(0_vl, 5_vl);
assert(range.first != range.second);
assert(*range.first == 2_vl);
++range.first;
assert(range.first != range.second);
assert(*range.first == 3_vl);
++range.first;
assert(range.first != range.second);
assert(*range.first == 4_vl);
++range.first;
assert(range.first == range.second);
}
{
auto range = bv.equal_range(0_vl, 1_vl);
assert(range.first == range.second);
}
{
auto range = bv.equal_range(10_vl, 10_vl);
assert(range.first == range.second);
}
bv.clear();
assert(bv.next(vis_line_t(0)) == -1);
assert(bv.prev(vis_line_t(0)) == -1);

View File

@ -60,12 +60,15 @@ EOF
run_test ${lnav_test} -n -d /tmp/lnav.err \
-c ";SELECT 1 AS c1, 'Hello, World!' AS c2" \
-c ":write-cols-to -" \
-c ":write-table-to -" \
"${test_dir}/logfile_access_log.*"
check_output "writing columns does not work?" <<EOF
c1 c2
1 Hello, World!
┏━━┳━━━━━━━━━━━━━┓
┃c1┃ c2 ┃
┡━━╇━━━━━━━━━━━━━┩
│ 1│Hello, World!│
└━━┴━━━━━━━━━━━━━┘
EOF
@ -1103,7 +1106,7 @@ EOF
export XYZ="World"
run_test ${lnav_test} -n \
-c ':echo Hello, $XYZ!' \
-c ':echo Hello, \$XYZ!' \
${test_dir}/logfile_access_log.0
check_output "echo hello" <<EOF
@ -1123,7 +1126,7 @@ EOF
run_test ${lnav_test} -n \
-c ':eval :echo Hello, $XYZ!' \
-c ':echo Hello, $XYZ!' \
${test_dir}/logfile_access_log.0
check_output "eval echo hello" <<EOF

View File

@ -46,7 +46,7 @@ check_output_ws "single" <<EOF
'abc'
sst ^
sen ^
eval -- abc
eval -- 'abc'
split:
0 -- abc
EOF
@ -57,7 +57,7 @@ check_output_ws "double" <<EOF
"def"
dst ^
den ^
eval -- def
eval -- "def"
split:
0 -- def
EOF
@ -68,7 +68,7 @@ check_output_ws "double with single" <<EOF
"'"
dst ^
den ^
eval -- '
eval -- "'"
split:
0 -- '
EOF
@ -79,7 +79,7 @@ check_output_ws "single with double" <<EOF
'"'
sst ^
sen ^
eval -- "
eval -- '"'
split:
0 -- "
EOF
@ -91,7 +91,7 @@ check_output_ws "double w/ref" <<EOF
dst ^
ref ^--^
den ^
eval -- abc xyz 123
eval -- "abc xyz 123"
split:
0 -- abc xyz 123
EOF
@ -103,7 +103,7 @@ check_output_ws "double w/quoted-ref" <<EOF
dst ^
qrf ^----^
den ^
eval -- abc xyz 123
eval -- "abc xyz 123"
split:
0 -- abc xyz 123
EOF
@ -114,7 +114,7 @@ check_output_ws "single w/ref" <<EOF
'abc \$DEF 123'
sst ^
sen ^
eval -- abc \$DEF 123
eval -- 'abc \$DEF 123'
split:
0 -- abc \$DEF 123
EOF

View File

@ -2,6 +2,15 @@
lnav_test="${top_builddir}/src/lnav-test"
run_test ${lnav_test} -n \
-c ";SELECT replicate('foobar', 120)" \
${test_dir}/logfile_empty.0
check_output "long lines are not truncated?" <<EOF
replicate('foobar', 120)
foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar⋯oobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar
EOF
cp ${srcdir}/logfile_syslog.2 logfile_syslog_test.2
touch -t 201511030923 logfile_syslog_test.2
run_test ${lnav_test} -n \

View File

@ -1,5 +1,30 @@
#! /bin/bash
run_test ./drive_sql "select humanize_file_size()"
check_error_output "" <<EOF
error: sqlite3_exec failed -- wrong number of arguments to function humanize_file_size()
EOF
run_test ./drive_sql "select humanize_file_size('abc')"
check_error_output "" <<EOF
error: sqlite3_exec failed -- Expecting an integer for argument number 0
EOF
run_test ./drive_sql "select humanize_file_size(1, 2)"
check_error_output "" <<EOF
error: sqlite3_exec failed -- wrong number of arguments to function humanize_file_size()
EOF
run_test ./drive_sql "select humanize_file_size(10 * 1000 * 1000)"
check_output "" <<EOF
Row 0:
Column humanize_file_size(10 * 1000 * 1000): 9.5MB
EOF
run_test ./drive_sql "select startswith('.foo', '.')"
check_output "" <<EOF

View File

@ -1,4 +1,4 @@
:eval :echo toplevel here $1 $2
:echo toplevel here $1 $2
|nested.lnav abc $3

View File

@ -188,71 +188,6 @@ K 68
S 24 ┋ h ┋
A ····└ normal
K 6f
S 24 ┋ ┋
A ····└ carriage-return
S 14 ┋ Command Help :: ┋
A └ fg(#c0c0c0), bg(#000080), bold
A ··············└ normal, fg(#c0c0c0), bg(#000080)
A ···············└ fg(#000080), bg(#c0c0c0)
A ················└ fg(#000000), bg(#c0c0c0)
S 15 ┋Synopsis ┋
A └ normal, underline
A ········└ normal
CSI Erase to Right
S 16 ┋ :echo msg - Echo the given message ┋
A ···└ bold │
A ·······└ normal
A ········└ underline
A ···········└ normal
CSI Erase to Right
S 16 ┋ ┋
A ····································└ carriage-return
S 17 ┋Parameter ┋
A └ underline
A ·········└ normal
CSI Erase to Right
S 17 ┋ ┋
A ·········└ carriage-return
S 18 ┋ msg The message to display ┋
A ··└ fg(#008080), bold
A ·····└ normal
CSI Erase to Right
S 18 ┋ ┋
A ······························└ carriage-return
S 19 ┋See Also ┋
A └ underline
A ········└ normal
CSI Erase to Right
S 20 ┋ :alt-msg, :eval, :redirect-to, :write-cols-to, :write-csv-to, ┋
A ··└ bold │ │ │ │ │ │ │ │ │
A ··········└ normal │ │ │ │ │ │
A ············└ bold │ │ │ │ │ │
A ·················└ normal │ │ │ │ │
A ···················└ bold │ │ │ │ │
A ·······························└ normal │ │ │
A ·································└ bold │ │ │
A ···············································└ normal │
A ·················································└ bold │
A ······························································└ normal
CSI Erase to Right
S 20 ┋ ┋
A ·······························································└ carriage-return
S 21 ┋ :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, ┋
A ··└ bold │ │ │ │ │ │ │
A ················└ normal │ │ │ │ │
A ··················└ bold │ │ │ │ │
A ·····································└ normal │ │ │
A ·······································└ bold │ │ │
A ····················································└ normal │
A ······················································└ bold │
A ······································································└ normal
CSI Erase to Right
S 21 ┋ ┋
A ·······································································└ carriage-return
S 22 ┋ :write-to ┋
A ··└ bold │
A ···········└ normal
CSI Erase to Right
S 24 ┋ o ┋
A ·····└ normal
K 20
@ -275,33 +210,6 @@ A ········└ carriage-return
CSI Erase Below
S 24 ┋ ┋
A └ normal
S 14 ┋hoped that these features will allow the user to quickly and x┋
A └┛ alt
A ················································································└ normal
S 15 ┋efficiently zero in on problems. x┋
A └┛ alt
A ················································································└ normal
S 16 ┋ x┋
A └┛ alt
A ················································································└ normal
S 17 ┋ x┋
A └┛ alt
A ················································································└ normal
S 18 ┋OPENING PATHS/URLs x┋
A └┛ alt
A ················································································└ normal
S 19 ┋================== x┋
A └┛ alt
A ················································································└ normal
S 20 ┋ x┋
A └┛ alt
A ················································································└ normal
S 21 ┋The main arguments to lnav are the files, directories, glob patterns, x┋
A └┛ alt
A ················································································└ normal
S 22 ┋or URLs to be viewed. If no arguments are given, the default syslog x┋
A └┛ alt
A ················································································└ normal
S 23 ┋ L0 0% ┋
A ·└ fg(#000000), bg(#c0c0c0)
S 23 ┋ ?:View Help ┋