summaryrefslogtreecommitdiffstats
path: root/src/string-extension-functions.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/string-extension-functions.cc167
1 files changed, 102 insertions, 65 deletions
diff --git a/src/string-extension-functions.cc b/src/string-extension-functions.cc
index fccc7a9..59c5e83 100644
--- a/src/string-extension-functions.cc
+++ b/src/string-extension-functions.cc
@@ -35,6 +35,7 @@
#include "spookyhash/SpookyV2.h"
#include "sqlite-extension-func.hh"
#include "text_anonymizer.hh"
+#include "view_curses.hh"
#include "vtab_module.hh"
#include "vtab_module_json.hh"
#include "yajl/api/yajl_gen.h"
@@ -48,6 +49,35 @@
using namespace mapbox;
+enum class encode_algo {
+ base64,
+ hex,
+ uri,
+};
+
+template<>
+struct from_sqlite<encode_algo> {
+ inline encode_algo operator()(int argc, sqlite3_value** val, int argi)
+ {
+ const char* algo_name = (const char*) sqlite3_value_text(val[argi]);
+
+ if (strcasecmp(algo_name, "base64") == 0) {
+ return encode_algo::base64;
+ }
+ if (strcasecmp(algo_name, "hex") == 0) {
+ return encode_algo::hex;
+ }
+ if (strcasecmp(algo_name, "uri") == 0) {
+ return encode_algo::uri;
+ }
+
+ throw from_sqlite_conversion_error("value of 'base64', 'hex', or 'uri'",
+ argi);
+ }
+};
+
+namespace {
+
struct cache_entry {
std::shared_ptr<lnav::pcre2pp::code> re2;
std::shared_ptr<column_namer> cn{
@@ -176,24 +206,7 @@ regexp_match(string_fragment re, string_fragment str)
#endif
}
-json_string
-extract(const char* str)
-{
- data_scanner ds(str);
- data_parser dp(&ds);
-
- dp.parse();
- // dp.print(stderr, dp.dp_pairs);
-
- yajlpp_gen gen;
- yajl_gen_config(gen, yajl_gen_beautify, false);
-
- elements_to_json(gen, dp, &dp.dp_pairs);
-
- return json_string(gen);
-}
-
-json_string
+static json_string
logfmt2json(string_fragment line)
{
logfmt::parser p(line);
@@ -365,8 +378,8 @@ sparkline_final(sqlite3_context* context)
return;
}
- auto* retval = (char*) malloc(sc->sc_values.size() * 3 + 1);
- auto* start = retval;
+ auto retval = auto_mem<char>::malloc(sc->sc_values.size() * 3 + 1);
+ auto* start = retval.in();
for (const auto& value : sc->sc_values) {
auto bar = humanize::sparkline(value, sc->sc_max_value);
@@ -376,7 +389,7 @@ sparkline_final(sqlite3_context* context)
}
*start = '\0';
- sqlite3_result_text(context, retval, -1, free);
+ to_sqlite(context, std::move(retval));
sc->~sparkline_context();
}
@@ -448,33 +461,6 @@ sql_gzip(sqlite3_value* val)
return nonstd::nullopt;
}
-enum class encode_algo {
- base64,
- hex,
- uri,
-};
-
-template<>
-struct from_sqlite<encode_algo> {
- inline encode_algo operator()(int argc, sqlite3_value** val, int argi)
- {
- const char* algo_name = (const char*) sqlite3_value_text(val[argi]);
-
- if (strcasecmp(algo_name, "base64") == 0) {
- return encode_algo::base64;
- }
- if (strcasecmp(algo_name, "hex") == 0) {
- return encode_algo::hex;
- }
- if (strcasecmp(algo_name, "uri") == 0) {
- return encode_algo::uri;
- }
-
- throw from_sqlite_conversion_error("value of 'base64', 'hex', or 'uri'",
- argi);
- }
-};
-
#if defined(HAVE_LIBCURL)
static CURL*
get_curl_easy()
@@ -549,7 +535,7 @@ sql_encode(sqlite3_value* value, encode_algo algo)
for (int lpc = 0; lpc < text_len; lpc++) {
fmt::format_to(std::back_inserter(buf),
- FMT_STRING("{:x}"),
+ FMT_STRING("{:02x}"),
text[lpc]);
}
@@ -635,17 +621,18 @@ const char* curl_url_strerror(CURLUcode error);
#endif
static json_string
-sql_parse_url(string_fragment url_frag)
+sql_parse_url(std::string url)
{
static auto* CURL_HANDLE = get_curl_easy();
auto_mem<CURLU> cu(curl_url_cleanup);
cu = curl_url();
- auto rc = curl_url_set(cu, CURLUPART_URL, url_frag.data(), 0);
+ auto rc = curl_url_set(
+ cu, CURLUPART_URL, url.c_str(), CURLU_NON_SUPPORT_SCHEME);
if (rc != CURLUE_OK) {
throw lnav::console::user_message::error(
- attr_line_t("invalid URL: ").append_quoted(url_frag.to_string()))
+ attr_line_t("invalid URL: ").append(lnav::roles::file(url)))
.with_reason(curl_url_strerror(rc));
}
@@ -663,7 +650,7 @@ sql_parse_url(string_fragment url_frag)
} else {
root.gen();
}
- root.gen("user");
+ root.gen("username");
rc = curl_url_get(cu, CURLUPART_USER, url_part.out(), CURLU_URLDECODE);
if (rc == CURLUE_OK) {
root.gen(string_fragment::from_c_str(url_part.in()));
@@ -708,25 +695,26 @@ sql_parse_url(string_fragment url_frag)
robin_hood::unordered_set<std::string> seen_keys;
yajlpp_map query_map(gen);
+ for (size_t lpc = 0; url_part.in()[lpc]; lpc++) {
+ if (url_part.in()[lpc] == '+') {
+ url_part.in()[lpc] = ' ';
+ }
+ }
auto query_frag = string_fragment::from_c_str(url_part.in());
auto remaining = query_frag;
while (true) {
auto split_res
= remaining.split_when(string_fragment::tag1{'&'});
-
- if (!split_res) {
- break;
- }
-
auto_mem<char> kv_pair(curl_free);
- auto kv_pair_encoded = split_res->first;
+ auto kv_pair_encoded = split_res.first;
int out_len = 0;
kv_pair = curl_easy_unescape(CURL_HANDLE,
kv_pair_encoded.data(),
kv_pair_encoded.length(),
&out_len);
+
auto kv_pair_frag
= string_fragment::from_bytes(kv_pair.in(), out_len);
auto eq_index_opt = kv_pair_frag.find('=');
@@ -741,20 +729,20 @@ sql_parse_url(string_fragment url_frag)
query_map.gen(val);
}
} else {
- auto val_str = split_res->first.to_string();
+ auto val_str = split_res.first.to_string();
if (seen_keys.count(val_str) == 0) {
seen_keys.insert(val_str);
- query_map.gen(split_res->first);
+ query_map.gen(split_res.first);
query_map.gen();
}
}
- if (split_res->second.empty()) {
+ if (split_res.second.empty()) {
break;
}
- remaining = split_res->second;
+ remaining = split_res.second;
}
} else {
root.gen("query");
@@ -788,7 +776,7 @@ struct url_parts {
};
static const json_path_container url_params_handlers = {
- yajlpp::pattern_property_handler("(?<param>.+)")
+ yajlpp::pattern_property_handler("(?<param>.*)")
.for_field(&url_parts::up_parameters),
};
@@ -812,7 +800,7 @@ sql_unparse_url(string_fragment in)
auto parse_res = url_parts_handlers.parser_for(SRC).of(in);
if (parse_res.isErr()) {
- throw parse_res.unwrapErr();
+ throw parse_res.unwrapErr()[0];
}
auto up = parse_res.unwrap();
@@ -873,6 +861,36 @@ sql_unparse_url(string_fragment in)
return retval;
}
+} // namespace
+
+json_string
+extract(const char* str)
+{
+ data_scanner ds(str);
+ data_parser dp(&ds);
+
+ dp.parse();
+ // dp.print(stderr, dp.dp_pairs);
+
+ yajlpp_gen gen;
+ yajl_gen_config(gen, yajl_gen_beautify, false);
+
+ elements_to_json(gen, dp, &dp.dp_pairs);
+
+ return json_string(gen);
+}
+
+static std::string
+sql_humanize_id(string_fragment id)
+{
+ auto& vc = view_colors::singleton();
+ auto attrs = vc.attrs_for_ident(id.data(), id.length());
+
+ return fmt::format(FMT_STRING("\x1b[38;5;{}m{}\x1b[0m"),
+ attrs.ta_fg_color.value_or(COLOR_CYAN),
+ id);
+}
+
int
string_extension_functions(struct FuncDef** basic_funcs,
struct FuncDefAgg** agg_funcs)
@@ -892,6 +910,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
"Match a string against a regular expression and return "
"the capture groups as JSON.")
.sql_function()
+ .with_prql_path({"text", "regexp_match"})
.with_parameter({"re", "The regular expression to use"})
.with_parameter({
"str",
@@ -919,6 +938,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
"Replace the parts of a string that match a regular "
"expression.")
.sql_function()
+ .with_prql_path({"text", "regexp_replace"})
.with_parameter(
{"str", "The string to perform replacements on"})
.with_parameter({"re", "The regular expression to match"})
@@ -947,6 +967,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
"humanize_file_size",
"Format the given file size as a human-friendly string")
.sql_function()
+ .with_prql_path({"humanize", "file_size"})
.with_parameter({"value", "The file size to format"})
.with_tags({"string"})
.with_example({
@@ -954,6 +975,18 @@ string_extension_functions(struct FuncDef** basic_funcs,
"SELECT humanize_file_size(10 * 1024 * 1024)",
})),
+ sqlite_func_adapter<decltype(&sql_humanize_id), sql_humanize_id>::
+ builder(help_text("humanize_id",
+ "Colorize the given ID using ANSI escape codes.")
+ .sql_function()
+ .with_prql_path({"humanize", "id"})
+ .with_parameter({"id", "The identifier to color"})
+ .with_tags({"string"})
+ .with_example({
+ "To colorize the ID 'cluster1'",
+ "SELECT humanize_id('cluster1')",
+ })),
+
sqlite_func_adapter<decltype(&humanize::sparkline),
humanize::sparkline>::
builder(
@@ -964,6 +997,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
"aggregate version returns a string with a bar "
"character for every numeric input")
.sql_function()
+ .with_prql_path({"text", "sparkline"})
.with_parameter({"value", "The numeric value to convert"})
.with_parameter(help_text("upper",
"The upper bound of the numeric "
@@ -989,6 +1023,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
help_text("anonymize",
"Replace identifying information with random values.")
.sql_function()
+ .with_prql_path({"text", "anonymize"})
.with_parameter({"value", "The text to anonymize"})
.with_tags({"string"})
.with_example({
@@ -1000,6 +1035,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
help_text("extract",
"Automatically Parse and extract data from a string")
.sql_function()
+ .with_prql_path({"text", "discover"})
.with_parameter({"str", "The string to parse"})
.with_tags({"string"})
.with_example({
@@ -1015,6 +1051,7 @@ string_extension_functions(struct FuncDef** basic_funcs,
help_text("logfmt2json",
"Convert a logfmt-encoded string into JSON")
.sql_function()
+ .with_prql_path({"logfmt", "to_json"})
.with_parameter({"str", "The logfmt message to parse"})
.with_tags({"string"})
.with_example({