[status] some more user notification tweaks

This commit is contained in:
Timothy Stack 2022-07-15 21:41:47 -07:00
parent f5cc4b298f
commit 45270505e3
29 changed files with 376 additions and 296 deletions

View File

@ -1,7 +1,7 @@
ACLOCAL_AMFLAGS = -I .
SUBDIRS = src test
SUBDIRS = tools src test
noinst_SCRIPTS = TESTS_ENVIRONMENT

View File

@ -1,6 +1,6 @@
# aminclude_static.am generated automatically by Autoconf
# from AX_AM_MACROS_STATIC on Tue Jul 12 09:53:25 PDT 2022
# from AX_AM_MACROS_STATIC on Fri Jul 15 10:37:15 PDT 2022
# Code coverage

View File

@ -314,6 +314,7 @@ AC_SUBST(USER_CXXFLAGS)
AC_CONFIG_HEADERS([src/config.h])
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([TESTS_ENVIRONMENT])
AC_CONFIG_FILES([tools/Makefile])
AC_CONFIG_FILES([src/Makefile])
AC_CONFIG_FILES([src/base/Makefile])
AC_CONFIG_FILES([src/formats/logfmt/Makefile])
@ -321,7 +322,6 @@ AC_CONFIG_FILES([src/fmtlib/Makefile])
AC_CONFIG_FILES([src/pcrepp/Makefile])
AC_CONFIG_FILES([src/pugixml/Makefile])
AC_CONFIG_FILES([src/tailer/Makefile])
AC_CONFIG_FILES([src/tools/Makefile])
AC_CONFIG_FILES([src/yajl/Makefile])
AC_CONFIG_FILES([src/yajlpp/Makefile])
AC_CONFIG_FILES([src/third-party/base64/lib/Makefile])

View File

@ -22,7 +22,7 @@ add_subdirectory(formats/logfmt)
add_subdirectory(yajl)
add_subdirectory(yajlpp)
add_executable(bin2c bin2c.hh tools/bin2c.c)
add_executable(bin2c bin2c.hh ../tools/bin2c.c)
target_link_libraries(bin2c ZLIB::ZLIB)
add_executable(ptimec ptimec.hh ptimec.c)

View File

@ -3,7 +3,7 @@ include $(top_srcdir)/aminclude_static.am
CXXFLAGS =
SUBDIRS = tools fmtlib third-party/base64/lib pcrepp base tailer pugixml yajl yajlpp formats/logfmt .
SUBDIRS = fmtlib third-party/base64/lib pcrepp base tailer pugixml yajl yajlpp formats/logfmt .
bin_PROGRAMS = lnav
@ -23,10 +23,12 @@ RE2C_V = $(RE2C_V_@AM_V@)
RE2C_V_ = $(RE2C_V_@AM_DEFAULT_V@)
RE2C_V_0 = @echo " RE2C " $@;
BIN2C_PATH = ../tools/bin2c$(BUILD_EXEEXT)
include formats/formats.am
default-formats.h default-formats.cc: tools/bin2c$(BUILD_EXEEXT) $(FORMAT_FILES)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_format_json default-formats $(FORMAT_FILES)
default-formats.cc: $(BIN2C_PATH) $(FORMAT_FILES)
$(BIN2C_V)$(BIN2C_PATH) -n lnav_format_json default-formats $(FORMAT_FILES)
include keymaps/keymaps.am
include themes/themes.am
@ -37,34 +39,34 @@ CONFIG_FILES = \
$(THEME_FILES) \
$()
default-config.h default-config.cc: tools/bin2c$(BUILD_EXEEXT) $(CONFIG_FILES)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_config_json default-config $(CONFIG_FILES)
default-config.cc: $(BIN2C_PATH) $(CONFIG_FILES)
$(BIN2C_V)$(BIN2C_PATH) -n lnav_config_json default-config $(CONFIG_FILES)
include scripts/scripts.am
builtin-scripts.h builtin-scripts.cc: tools/bin2c$(BUILD_EXEEXT) $(BUILTIN_LNAVSCRIPTS)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS)
builtin-scripts.cc: $(BIN2C_PATH) $(BUILTIN_LNAVSCRIPTS)
$(BIN2C_V)$(BIN2C_PATH) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS)
builtin-sh-scripts.h builtin-sh-scripts.cc: tools/bin2c$(BUILD_EXEEXT) $(BUILTIN_SHSCRIPTS)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS)
builtin-sh-scripts.cc: $(BIN2C_PATH) $(BUILTIN_SHSCRIPTS)
$(BIN2C_V)$(BIN2C_PATH) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS)
%-sh.cc: $(srcdir)/%.sh tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-sh $<
%-sh.cc: $(srcdir)/%.sh $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-sh $<
%-txt.cc %-txt.h: $(srcdir)/%.txt tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-txt $<
%-txt.cc: $(srcdir)/%.txt $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-txt $<
%-md.cc %-md.h: $(srcdir)/%.md tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-md $<
%-md.cc: $(srcdir)/%.md $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-md $<
%-sql.cc %-sql.h: $(srcdir)/%.sql tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-sql $<
%-sql.cc: $(srcdir)/%.sql $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-sql $<
%-lnav.cc %-lnav.h: $(srcdir)/%.lnav tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-lnav $<
%-lnav.cc: $(srcdir)/%.lnav $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-lnav $<
%-json.cc %-json.h: $(srcdir)/%.json tools/bin2c$(BUILD_EXEEXT)
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-json $<
%-json.cc: $(srcdir)/%.json $(BIN2C_PATH)
$(BIN2C_V)$(BIN2C_PATH) $(*)-json $<
include time_formats.am
@ -77,44 +79,21 @@ if HAVE_RE2C
$(REC2_V)test $@ -ef $(srcdir)/$*.cc || cp $@ $(srcdir)/$*.cc
endif
lnav_config.$(OBJEXT): default-config.h
log_format_loader.$(OBJEXT): \
builtin-scripts.h \
builtin-sh-scripts.h \
default-formats.h
styling.$(OBJEXT): ansi-palette-json.h xterm-palette-json.h
view_helpers.$(OBJEXT): help-txt.h help-md.h
md4cpp.$(OBJEXT): xml-entities-json.h emojis-json.h
LNAV_BUILT_FILES = \
ansi-palette-json.h \
ansi-palette-json.cc \
builtin-scripts.h \
builtin-scripts.cc \
builtin-sh-scripts.h \
builtin-sh-scripts.cc \
default-config.h \
default-config.cc \
default-formats.h \
default-formats.cc \
emojis-json.h \
emojis-json.cc \
help-txt.h \
help-txt.cc \
help-md.h \
help-md.cc \
init-sql.h \
init-sql.cc \
time_fmts.cc \
xml-entities-json.h \
xml-entities-json.cc \
xterm-palette-json.h \
xterm-palette-json.cc
BUILT_SOURCES = $(LNAV_BUILT_FILES)
AM_LIBS = $(CODE_COVERAGE_LIBS)
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) $(USER_CXXFLAGS)
@ -287,6 +266,7 @@ noinst_HEADERS = \
spectro_impls.hh \
spectro_source.hh \
sqlitepp.hh \
sqlitepp.client.hh \
sql_help.hh \
sql_util.hh \
sqlite-extension-func.hh \
@ -474,8 +454,6 @@ libdiag_a_SOURCES = \
PLUGIN_SRCS = \
file_vtab.cc
lnav.$(OBJEXT): help-txt.h init-sql.h
lnav_SOURCES = \
lnav.cc \
lnav.events.cc \
@ -516,11 +494,13 @@ uncrusty:
$(HEADERS))
if !DISABLE_DOCUMENTATION
all-local: lnav
all-local: $(LNAV_BUILT_FILES) lnav
if test -w $(srcdir)/internals; then \
env DUMP_INTERNALS_DIR=$(srcdir)/internals DUMP_CRASH=1 ./lnav Makefile; \
mv $(srcdir)/internals/*.schema.json $(top_srcdir)/docs/schemas; \
fi
else
all-local: $(LNAV_BUILT_FILES)
endif
install-exec-hook:

View File

@ -109,13 +109,30 @@ VALUES (0, null, '2017-02-03T04:05:06.100', '2017-02-03T04:05:06.100', 0,
CREATE TABLE lnav_user_notifications
(
id TEXT NOT NULL DEFAULT 'org.lnav.unknown',
-- A unique identifier for the notification.
id TEXT NOT NULL DEFAULT 'org.lnav.user' PRIMARY KEY,
-- The priority of this message relative to others, the highest priority
-- message will be shown in the top-right corner.
priority INTEGER NOT NULL DEFAULT 0,
-- The time when this notification was created.
created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- The time when this notification is no longer applicable.
expiration DATETIME DEFAULT NULL,
message TEXT
-- A JSON array with the names of the views where this notification is
-- applicable. Use NULL to show it in all views.
views JSON,
-- The message to display, can be null to clear the message.
message TEXT,
CHECK (views IS NULL OR json_type(views) = 'array')
);
INSERT INTO lnav_user_notifications (id, priority, expiration, message)
VALUES ('org.lnav.breadcrumb.help.focus', -1, datetime('now', '+1 minute'),
VALUES ('org.lnav.breadcrumb.focus', -1, datetime('now', '+1 minute'),
'Press ENTER to focus on the breadcrumb bar');
CREATE TABLE lnav_views_echo AS
SELECT name, top, "left", height, inner_height, top_time, search
FROM lnav_views;
CREATE UNIQUE INDEX lnav_views_echo_index ON lnav_views_echo (name);

View File

@ -67,7 +67,7 @@ null_or_default(sqlite3_context* context, int argc, sqlite3_value* argv[])
}
struct contains_userdata {
util::variant<const char*, sqlite3_int64, bool> cu_match_value{false};
util::variant<string_fragment, sqlite3_int64, bool> cu_match_value{false};
size_t cu_depth{0};
bool cu_result{false};
};
@ -75,12 +75,10 @@ struct contains_userdata {
static int
contains_string(void* ctx, const unsigned char* str, size_t len)
{
auto sf = string_fragment{(const char*) str, 0, (int) len};
auto& cu = *((contains_userdata*) ctx);
if (cu.cu_depth <= 1
&& strncmp((const char*) str, cu.cu_match_value.get<const char*>(), len)
== 0)
{
if (cu.cu_depth <= 1 && cu.cu_match_value.get<string_fragment>() == sf) {
cu.cu_result = true;
}
@ -158,7 +156,11 @@ json_contains(vtab_types::nullable<const char> nullable_json_in,
switch (sqlite3_value_type(value)) {
case SQLITE3_TEXT:
cb.yajl_string = contains_string;
cu.cu_match_value = (const char*) sqlite3_value_text(value);
cu.cu_match_value = string_fragment{
(const char*) sqlite3_value_text(value),
0,
sqlite3_value_bytes(value),
};
break;
case SQLITE_INTEGER:
cb.yajl_integer = contains_integer;

View File

@ -96,7 +96,6 @@
#include "filter_sub_source.hh"
#include "fstat_vtab.hh"
#include "grep_proc.hh"
#include "help-txt.h"
#include "hist_source.hh"
#include "init-sql.h"
#include "listview_curses.hh"
@ -124,6 +123,7 @@
#include "sql_help.hh"
#include "sql_util.hh"
#include "sqlite-extension-func.hh"
#include "sqlitepp.client.hh"
#include "tailer/tailer.looper.hh"
#include "term_extra.hh"
#include "termios_guard.hh"
@ -1329,6 +1329,19 @@ looper()
auto next_status_update_time = next_rebuild_time;
auto next_rescan_time = next_rebuild_time;
auto echo_views_stmt = prepare_stmt(lnav_data.ld_db, R"(
UPDATE lnav_views_echo
SET top = orig.top,
left = orig.left,
height = orig.height,
inner_height = orig.inner_height,
top_time = orig.top_time,
search = orig.search
FROM (SELECT * FROM lnav_views) AS orig
WHERE orig.name = lnav_views_echo.name
)")
.unwrap();
while (lnav_data.ld_looping) {
auto loop_deadline
= ui_clock::now() + (session_stage == 0 ? 3s : 50ms);
@ -1447,6 +1460,7 @@ looper()
lnav_data.ld_spectro_details_view.do_update();
lnav_data.ld_user_message_view.do_update();
if (ui_clock::now() >= next_status_update_time) {
echo_views_stmt.execute();
lnav_data.ld_top_source.update_user_msg();
for (auto& sc : lnav_data.ld_status) {
sc.do_update();

View File

@ -2214,8 +2214,8 @@ com_create_search_table(exec_context& ec,
}
auto re = re_res.unwrap();
auto lst = std::make_shared<log_search_table>(
re, intern_string::lookup(args[1]));
auto tab_name = intern_string::lookup(args[1]);
auto lst = std::make_shared<log_search_table>(re, tab_name);
if (ec.ec_dry_run) {
textview_curses* tc = &lnav_data.ld_views[LNV_LOG];
auto& hm = tc->get_highlights();
@ -2238,6 +2238,11 @@ com_create_search_table(exec_context& ec,
return Ok(std::string());
}
auto tab_iter = custom_search_tables.find(args[1]);
if (tab_iter != custom_search_tables.end()) {
lnav_data.ld_vtab_manager->unregister_vtab(tab_name);
}
std::string errmsg;
errmsg = lnav_data.ld_vtab_manager->register_vtab(lst);
@ -2268,7 +2273,8 @@ com_delete_search_table(exec_context& ec,
if (args.empty()) {
args.emplace_back("search-table");
} else if (args.size() == 2) {
if (custom_search_tables.find(args[1]) == custom_search_tables.end()) {
auto tab_iter = custom_search_tables.find(args[1]);
if (tab_iter == custom_search_tables.end()) {
return ec.make_error("unknown search table -- {}", args[1]);
}
@ -2276,7 +2282,8 @@ com_delete_search_table(exec_context& ec,
return Ok(std::string());
}
std::string rc = lnav_data.ld_vtab_manager->unregister_vtab(
custom_search_tables.erase(tab_iter);
auto rc = lnav_data.ld_vtab_manager->unregister_vtab(
intern_string::lookup(args[1]));
if (rc.empty()) {

View File

@ -127,21 +127,20 @@ log_search_table::next(log_cursor& lc, logfile_sub_source& lss)
return true;
}
lc.lc_curr_line += 1_vl;
lc.lc_sub_index = 0;
this->lst_match_index = -1;
return false;
}
this->lst_match_index = -1;
while (!lc.is_eof() && !this->is_valid(lc, lss)) {
lc.lc_curr_line += 1_vl;
lc.lc_sub_index = 0;
}
if (lc.is_eof()) {
return true;
}
if (!this->is_valid(lc, lss)) {
return false;
}
auto cl = lss.at(lc.lc_curr_line);
auto* lf = lss.find_file_ptr(cl);
@ -177,7 +176,9 @@ log_search_table::extract(logfile* lf,
shared_buffer_ref& line,
std::vector<logline_value>& values)
{
values = this->lst_line_values_cache;
if (this->lst_format != nullptr) {
values = this->lst_line_values_cache;
}
values.emplace_back(this->lst_column_metas[this->lst_format_column_count],
this->lst_match_index);
for (int lpc = 0; lpc < this->lst_regex.get_capture_count(); lpc++) {

View File

@ -2023,7 +2023,7 @@ log_vtab_manager::unregister_vtab(intern_string_t name)
std::string retval;
if (this->vm_impls.find(name) == this->vm_impls.end()) {
retval = fmt::format(FMT_STRING("unknown log line table -- {}"), name);
retval = fmt::format(FMT_STRING("unknown table -- {}"), name);
} else {
auto_mem<char, sqlite3_free> sql;
__attribute((unused)) int rc;

View File

@ -520,7 +520,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
void
rl_search(readline_curses* rc)
{
textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode);
auto* tc = get_textview_for_mode(lnav_data.ld_mode);
rl_search_internal(rc, lnav_data.ld_mode);
tc->set_follow_search_for(0, {});
@ -878,6 +878,8 @@ rl_focus(readline_curses* rc)
.get_overlay_source();
fos->fos_contexts.emplace("", false, true);
get_textview_for_mode(lnav_data.ld_mode)->save_current_search();
}
void

View File

@ -67,6 +67,7 @@
#include "spookyhash/SpookyV2.h"
static int got_line = 0;
static int got_abort = 0;
static bool alt_done = 0;
static sig_atomic_t got_timeout = 0;
static sig_atomic_t got_winch = 0;
@ -499,6 +500,8 @@ rubout_char_or_abort(int count, int key)
{
if (rl_line_buffer[0] == '\0') {
rl_done = true;
got_abort = 1;
got_line = 0;
return 0;
} else {
return rl_rubout(count, '\b');
@ -885,6 +888,7 @@ readline_curses::start()
= this->rc_contexts.find(context))
!= this->rc_contexts.end())
{
got_abort = 0;
current_context->second->load();
rl_callback_handler_install(&msg[prompt_start],
line_ready_tramp);
@ -1027,7 +1031,7 @@ readline_curses::line_ready(const char* line)
const char* cmd_ch = alt_done ? "D" : "d";
alt_done = false;
if (line == nullptr) {
if (got_abort || line == nullptr) {
snprintf(msg, sizeof(msg), "a");
if (sendstring(this->rc_command_pipe[RCF_SLAVE], msg, strlen(msg))

View File

@ -52,6 +52,7 @@
#include "logfile.hh"
#include "service_tags.hh"
#include "sql_util.hh"
#include "sqlitepp.client.hh"
#include "tailer/tailer.looper.hh"
#include "vtab_module.hh"
#include "yajlpp/yajlpp.hh"
@ -131,143 +132,6 @@ static const size_t MAX_SESSION_FILE_COUNT = 256;
static std::vector<content_line_t> marked_session_lines;
static std::vector<content_line_t> offset_session_lines;
int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const struct timeval& tv)
{
char timestamp[64];
sql_strftime(timestamp, sizeof(timestamp), tv, 'T');
return sqlite3_bind_text(stmt, index, timestamp, -1, SQLITE_TRANSIENT);
}
int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const char* str)
{
return sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT);
}
int
bind_to_sqlite(sqlite3_stmt* stmt, int index, intern_string_t ist)
{
return sqlite3_bind_text(
stmt, index, ist.get(), ist.size(), SQLITE_TRANSIENT);
}
int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const std::string& str)
{
return sqlite3_bind_text(
stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT);
}
int
bind_to_sqlite(sqlite3_stmt* stmt, int index, int64_t i)
{
return sqlite3_bind_int64(stmt, index, i);
}
template<typename... Args, std::size_t... Idx>
int
bind_values_helper(sqlite3_stmt* stmt,
std::index_sequence<Idx...> idxs,
Args... args)
{
int rcs[] = {bind_to_sqlite(stmt, Idx + 1, args)...};
for (size_t lpc = 0; lpc < idxs.size(); lpc++) {
if (rcs[lpc] != SQLITE_OK) {
log_error("Failed to bind column %d in statement: %s",
lpc,
sqlite3_sql(stmt));
return rcs[lpc];
}
}
return SQLITE_OK;
}
template<typename... Args>
int
bind_values(sqlite3_stmt* stmt, Args... args)
{
return bind_values_helper(
stmt, std::make_index_sequence<sizeof...(Args)>(), args...);
}
struct prepared_stmt {
prepared_stmt(auto_mem<sqlite3_stmt> stmt) : ps_stmt(std::move(stmt)) {}
Result<void, std::string> execute()
{
auto rc = sqlite3_step(this->ps_stmt.in());
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
return Ok();
}
auto msg = std::string(
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())));
return Err(msg);
}
struct end_of_rows {};
struct fetch_error {
std::string fe_msg;
};
template<typename T>
using fetch_result = mapbox::util::variant<T, end_of_rows, fetch_error>;
template<typename T>
fetch_result<T> fetch_row()
{
auto rc = sqlite3_step(this->ps_stmt.in());
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
return end_of_rows{};
}
if (rc == SQLITE_ROW) {
const auto argc = sqlite3_column_count(this->ps_stmt.in());
sqlite3_value* argv[argc];
for (int lpc = 0; lpc < argc; lpc++) {
argv[lpc] = sqlite3_column_value(this->ps_stmt.in(), lpc);
}
return from_sqlite<T>()(argc, argv, 0);
}
return fetch_error{
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())),
};
}
auto_mem<sqlite3_stmt> ps_stmt;
};
template<typename... Args>
static Result<prepared_stmt, std::string>
prepare_stmt(sqlite3* db, const char* sql, Args... args)
{
auto_mem<sqlite3_stmt> retval(sqlite3_finalize);
if (sqlite3_prepare_v2(db, sql, -1, retval.out(), nullptr) != SQLITE_OK) {
return Err(
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
sqlite3_errmsg(db)));
}
if (bind_values(retval.in(), args...) != SQLITE_OK) {
return Err(
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
sqlite3_errmsg(db)));
}
return Ok(prepared_stmt{
std::move(retval),
});
}
static bool
bind_line(sqlite3* db,
sqlite3_stmt* stmt,

186
src/sqlitepp.client.hh Normal file
View File

@ -0,0 +1,186 @@
/**
* Copyright (c) 2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lnav_sqlitepp_client_hh
#define lnav_sqlitepp_client_hh
#include <sqlite3.h>
#include "base/auto_mem.hh"
#include "base/intern_string.hh"
#include "base/lnav_log.hh"
#include "sql_util.hh"
#include "vtab_module.hh"
inline int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const struct timeval& tv)
{
char timestamp[64];
sql_strftime(timestamp, sizeof(timestamp), tv, 'T');
return sqlite3_bind_text(stmt, index, timestamp, -1, SQLITE_TRANSIENT);
}
inline int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const char* str)
{
return sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT);
}
inline int
bind_to_sqlite(sqlite3_stmt* stmt, int index, intern_string_t ist)
{
return sqlite3_bind_text(
stmt, index, ist.get(), ist.size(), SQLITE_TRANSIENT);
}
inline int
bind_to_sqlite(sqlite3_stmt* stmt, int index, const std::string& str)
{
return sqlite3_bind_text(
stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT);
}
inline int
bind_to_sqlite(sqlite3_stmt* stmt, int index, int64_t i)
{
return sqlite3_bind_int64(stmt, index, i);
}
template<typename... Args, std::size_t... Idx>
int
bind_values_helper(sqlite3_stmt* stmt,
std::index_sequence<Idx...> idxs,
Args... args)
{
int rcs[] = {bind_to_sqlite(stmt, Idx + 1, args)...};
for (size_t lpc = 0; lpc < idxs.size(); lpc++) {
if (rcs[lpc] != SQLITE_OK) {
log_error("Failed to bind column %d in statement: %s",
lpc,
sqlite3_sql(stmt));
return rcs[lpc];
}
}
return SQLITE_OK;
}
template<typename... Args>
int
bind_values(sqlite3_stmt* stmt, Args... args)
{
return bind_values_helper(
stmt, std::make_index_sequence<sizeof...(Args)>(), args...);
}
struct prepared_stmt {
prepared_stmt(auto_mem<sqlite3_stmt> stmt) : ps_stmt(std::move(stmt)) {}
Result<void, std::string> execute()
{
auto rc = sqlite3_reset(this->ps_stmt.in());
if (rc != SQLITE_OK) {
return Err(std::string(
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in()))));
}
rc = sqlite3_step(this->ps_stmt.in());
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
return Ok();
}
auto msg = std::string(
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())));
return Err(msg);
}
struct end_of_rows {};
struct fetch_error {
std::string fe_msg;
};
void reset() { sqlite3_reset(this->ps_stmt.in()); }
template<typename T>
using fetch_result = mapbox::util::variant<T, end_of_rows, fetch_error>;
template<typename T>
fetch_result<T> fetch_row()
{
auto rc = sqlite3_step(this->ps_stmt.in());
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
return end_of_rows{};
}
if (rc == SQLITE_ROW) {
const auto argc = sqlite3_column_count(this->ps_stmt.in());
sqlite3_value* argv[argc];
for (int lpc = 0; lpc < argc; lpc++) {
argv[lpc] = sqlite3_column_value(this->ps_stmt.in(), lpc);
}
return from_sqlite<T>()(argc, argv, 0);
}
return fetch_error{
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())),
};
}
auto_mem<sqlite3_stmt> ps_stmt;
};
template<typename... Args>
static Result<prepared_stmt, std::string>
prepare_stmt(sqlite3* db, const char* sql, Args... args)
{
auto_mem<sqlite3_stmt> retval(sqlite3_finalize);
if (sqlite3_prepare_v2(db, sql, -1, retval.out(), nullptr) != SQLITE_OK) {
return Err(
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
sqlite3_errmsg(db)));
}
if (bind_values(retval.in(), args...) != SQLITE_OK) {
return Err(
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
sqlite3_errmsg(db)));
}
return Ok(prepared_stmt{
std::move(retval),
});
}
#endif

View File

@ -46,7 +46,7 @@ libtailerpp_a_CPPFLAGS = \
libtailerpp_a_SOURCES = \
tailerpp.cc
tailerbin.h tailerbin.cc: tailer tailer.ape ../tools/bin2c$(BUILD_EXEEXT)
tailerbin.cc: tailer tailer.ape ../tools/bin2c$(BUILD_EXEEXT)
../tools/bin2c$(BUILD_EXEEXT) -n tailer_bin tailerbin $(srcdir)/tailer.ape
libtailerservice_a_CPPFLAGS = \

View File

@ -570,7 +570,6 @@ textview_curses::execute_search(const std::string& regex_orig)
const char* errptr;
int eoff;
this->tc_previous_search = this->tc_current_search;
this->match_reset();
this->tc_search_child.reset();

View File

@ -676,15 +676,14 @@ public:
}
}
std::string get_current_search() const
std::string get_current_search() const { return this->tc_current_search; }
void save_current_search()
{
return this->tc_current_search;
this->tc_previous_search = this->tc_current_search;
}
void revert_search()
{
this->execute_search(this->tc_previous_search);
}
void revert_search() { this->execute_search(this->tc_previous_search); }
void invoke_scroll()
{

View File

@ -4,21 +4,9 @@
#include <stddef.h> /* size_t */
#if defined(_WIN32) || defined(__CYGWIN__)
#define BASE64_SYMBOL_IMPORT __declspec(dllimport)
#define BASE64_SYMBOL_EXPORT __declspec(dllexport)
#define BASE64_SYMBOL_PRIVATE
#elif __GNUC__ >= 4
#define BASE64_SYMBOL_IMPORT __attribute__ ((visibility ("default")))
#define BASE64_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
#define BASE64_SYMBOL_PRIVATE __attribute__ ((visibility ("hidden")))
#else
#define BASE64_SYMBOL_IMPORT
#define BASE64_SYMBOL_EXPORT
#define BASE64_SYMBOL_PRIVATE
#endif
#if defined(BASE64_STATIC_DEFINE)
#define BASE64_EXPORT

View File

@ -37,6 +37,7 @@
#include "lnav_config.hh"
#include "logfile_sub_source.hh"
#include "sql_util.hh"
#include "sqlitepp.client.hh"
top_status_source::top_status_source()
{
@ -70,76 +71,48 @@ top_status_source::update_time()
this->update_time(tv);
}
struct user_msg_stmt {
user_msg_stmt()
{
static const char* MSG_QUERY = R"(
static const char* MSG_QUERY = R"(
SELECT message FROM lnav_user_notifications
WHERE expiration IS NULL OR expiration > datetime('now')
WHERE message IS NOT NULL AND
(expiration IS NULL OR expiration > datetime('now')) AND
(views IS NULL OR
json_contains(views, (SELECT name FROM lnav_top_view)))
ORDER BY priority DESC
LIMIT 1
)";
auto& lnav_db = injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
auto retcode = sqlite3_prepare_v2(
lnav_db, MSG_QUERY, -1, this->ums_stmt.out(), nullptr);
ensure(retcode == SQLITE_OK);
struct user_msg_stmt {
user_msg_stmt()
: ums_stmt(
prepare_stmt(injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>()
.in(),
MSG_QUERY)
.unwrap())
{
}
auto_mem<sqlite3_stmt> ums_stmt{sqlite3_finalize};
prepared_stmt ums_stmt;
};
void
top_status_source::update_user_msg()
{
static user_msg_stmt um_stmt;
static auto& lnav_db
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
auto& al = this->tss_fields[TSF_USER_MSG].get_value();
al.clear();
auto* stmt = um_stmt.ums_stmt.in();
sqlite3_reset(stmt);
auto count = sqlite3_bind_parameter_count(stmt);
for (int lpc = 0; lpc < count; lpc++) {
const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
if (name[0] == '$') {
const char* env_value;
if ((env_value = getenv(&name[1])) != nullptr) {
sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
}
continue;
}
}
auto step_res = sqlite3_step(stmt);
switch (step_res) {
case SQLITE_OK:
case SQLITE_DONE:
break;
case SQLITE_ROW: {
int ncols = sqlite3_column_count(stmt);
for (int lpc = 0; lpc < ncols; lpc++) {
const auto* text = (const char*) sqlite3_column_text(stmt, lpc);
al.with_ansi_string(text);
al.append(" ");
}
break;
}
default: {
um_stmt.ums_stmt.reset();
auto fetch_res = um_stmt.ums_stmt.fetch_row<std::string>();
fetch_res.match(
[&al](const std::string& value) {
al.with_ansi_string(value);
al.append(" ");
},
[](const prepared_stmt::end_of_rows&) {},
[](const prepared_stmt::fetch_error& fe) {
log_error("failed to execute user-message expression: %s",
sqlite3_errmsg(lnav_db));
break;
}
}
fe.fe_msg.c_str());
});
}

View File

@ -35,7 +35,6 @@
#include "document.sections.hh"
#include "environ_vtab.hh"
#include "help-md.h"
#include "help-txt.h"
#include "intervaltree/IntervalTree.h"
#include "lnav.hh"
#include "lnav.indexing.hh"

View File

@ -243,7 +243,7 @@ CREATE TABLE lnav_views (
sql_strftime(timestamp,
sizeof(timestamp),
top_time_opt.value(),
'T');
' ');
sqlite3_result_text(
ctx, timestamp, -1, SQLITE_TRANSIENT);
} else {
@ -304,7 +304,7 @@ CREATE TABLE lnav_views (
sql_strftime(timestamp,
sizeof(timestamp),
top_time_opt.value(),
'T');
' ');
tlm.tlm_time = timestamp;
}
}

View File

@ -92,7 +92,6 @@ TEXT2C_OBJS = \
../src/builtin-scripts.$(OBJEXT) \
../src/builtin-sh-scripts.$(OBJEXT) \
../src/default-formats.$(OBJEXT) \
../src/help-txt.$(OBJEXT) \
../src/time_fmts.$(OBJEXT)
LDADD = \

View File

@ -662,6 +662,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql_json_func.sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.out \
$(srcdir)/%reldir%/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.err \
$(srcdir)/%reldir%/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.out \
$(srcdir)/%reldir%/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.err \
$(srcdir)/%reldir%/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.out \
$(srcdir)/%reldir%/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.err \
$(srcdir)/%reldir%/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.out \
$(srcdir)/%reldir%/test_sql_json_func.sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.err \

View File

@ -0,0 +1,2 @@
Row 0:
Column json_contains('"hi"', 'hi there'): 0

View File

@ -24,6 +24,10 @@ run_cap_test env TEST_COMMENT='contains1' ./drive_sql <<EOF
select json_contains('"hi"', 'hi')
EOF
run_cap_test env TEST_COMMENT='contains1.5' ./drive_sql <<EOF
select json_contains('"hi"', 'hi there')
EOF
run_cap_test env TEST_COMMENT='contains2' ./drive_sql <<EOF
select json_contains('["hi", "bye"]', 'hola') as res
EOF

View File

@ -136,16 +136,17 @@ main(int argc, char** argv)
}
const char* out_base_name = argv[0];
char hname[PATH_MAX], cname[PATH_MAX];
char hname[PATH_MAX], hname_tmp[PATH_MAX], cname[PATH_MAX];
argc -= 1;
argv += 1;
snprintf(hname, sizeof(hname), "%s.h", out_base_name);
snprintf(hname_tmp, sizeof(hname_tmp), "%s.tmp", hname);
FILE* hfile = fopen(hname, "wb");
FILE* hfile = fopen(hname_tmp, "w+b");
if (hfile == NULL) {
fprintf(stderr, "cannot open %s for writing\n", hname);
fprintf(stderr, "cannot open %s for writing\n", hname_tmp);
exit(1);
}
@ -174,7 +175,44 @@ main(int argc, char** argv)
trailer[0] = '\0';
}
fprintf(hfile, HEADER_FMT, sym, sym, sym, trailer);
fflush(hfile);
rewind(hfile);
int same = 1;
{
FILE* orig_hfile = fopen(hname, "rb");
if (orig_hfile == NULL) {
same = 0;
} else {
while (1) {
char orig_line[1024], new_line[1024];
char* orig_res
= fgets(orig_line, sizeof(orig_line), orig_hfile);
char* new_res = fgets(new_line, sizeof(new_line), hfile);
if (orig_res == NULL && new_res == NULL) {
break;
}
if (orig_res == NULL || new_res == NULL) {
same = 0;
break;
}
if (strcmp(orig_line, new_line) != 0) {
same = 0;
break;
}
}
}
}
fclose(hfile);
if (!same) {
rename(hname_tmp, hname);
} else {
remove(hname_tmp);
}
fprintf(cfile, "#include \"bin2c.hh\"\n");
fprintf(cfile, "\n");