diff --git a/CMakeLists.txt b/CMakeLists.txt index 4062cee4..df71f186 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 2.6) -SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD 14) project (lnav) add_subdirectory(src) add_subdirectory(test) diff --git a/configure.ac b/configure.ac index e1be10cb..d017661f 100644 --- a/configure.ac +++ b/configure.ac @@ -8,7 +8,7 @@ AC_PREFIX_DEFAULT(/usr) AC_CANONICAL_HOST -AX_CXX_COMPILE_STDCXX_11([noext], [mandatory]) +AX_CXX_COMPILE_STDCXX_14([noext], [mandatory]) for defdir in /opt/local /usr/local /usr /; do if test -d "$defdir/include"; then diff --git a/m4/ax_cxx_compile_stdcxx_14.m4 b/m4/ax_cxx_compile_stdcxx_14.m4 new file mode 100644 index 00000000..094db0d0 --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_14.m4 @@ -0,0 +1,34 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_14.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_14([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++14 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++14. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 5 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [AX_CXX_COMPILE_STDCXX([14], [$1], [$2])]) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07a54189..81fb7561 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ set(diag_STAT_SRCS view_curses.cc views_vtab.cc vt52_curses.cc + vtab_module.cc log_vtab_impl.cc xterm_mouse.cc yajlpp.cc @@ -130,6 +131,7 @@ set(diag_STAT_SRCS top_status_source.hh url_loader.hh views_vtab.hh + vtab_module.hh yajl/api/yajl_common.h yajl/api/yajl_gen.h diff --git a/src/Makefile.am b/src/Makefile.am index ac04f64f..aee8a5f5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -206,6 +206,7 @@ noinst_HEADERS = \ view_curses.hh \ views_vtab.hh \ vt52_curses.hh \ + vtab_module.hh \ log_vtab_impl.hh \ log_format_impls.cc \ xterm_mouse.hh \ @@ -295,6 +296,7 @@ libdiag_a_SOURCES = \ view_curses.cc \ views_vtab.cc \ vt52_curses.cc \ + vtab_module.cc \ log_vtab_impl.cc \ xterm_mouse.cc \ yajlpp.cc \ diff --git a/src/file_vtab.cc b/src/file_vtab.cc index 2fc53ade..ec272882 100644 --- a/src/file_vtab.cc +++ b/src/file_vtab.cc @@ -29,7 +29,6 @@ #include "config.h" -#include #include #include #include @@ -39,231 +38,85 @@ #include "lnav_log.hh" #include "sql_util.hh" #include "file_vtab.hh" +#include "vtab_module.hh" using namespace std; -const char *LNAV_FILE_CREATE_STMT = "\ --- Access lnav's open file list through this table.\n\ -CREATE TABLE lnav_file (\n\ - device integer,\n\ - inode integer,\n\ - filepath text,\n\ - format text,\n\ - lines integer\n\ -);\n\ -"; - -struct vtab { - sqlite3_vtab base; - sqlite3 * db; -}; - -struct vtab_cursor { - sqlite3_vtab_cursor base; - int vc_index; -}; - -static int vt_destructor(sqlite3_vtab *p_svt); - -static int vt_create(sqlite3 *db, - void *pAux, - int argc, const char *const *argv, - sqlite3_vtab **pp_vt, - char **pzErr) -{ - vtab *p_vt; - - /* Allocate the sqlite3_vtab/vtab structure itself */ - p_vt = (vtab *)sqlite3_malloc(sizeof(*p_vt)); - - if (p_vt == NULL) { - return SQLITE_NOMEM; +struct lnav_file : public tvt_iterator_cursor { + using iterator = vector::iterator; + + static constexpr const char *CREATE_STMT = R"( +-- Access lnav's open file list through this table. +CREATE TABLE lnav_file ( + device integer, + inode integer, + filepath text, + format text, + lines integer +); +)"; + + struct vtab { + sqlite3_vtab base; + + operator sqlite3_vtab *() { + return &this->base; + }; + }; + + iterator begin() { + return lnav_data.ld_files.begin(); } - memset(&p_vt->base, 0, sizeof(sqlite3_vtab)); - p_vt->db = db; - - *pp_vt = &p_vt->base; - - int rc = sqlite3_declare_vtab(db, LNAV_FILE_CREATE_STMT); - - return rc; -} - - -static int vt_destructor(sqlite3_vtab *p_svt) -{ - vtab *p_vt = (vtab *)p_svt; - - /* Free the SQLite structure */ - sqlite3_free(p_vt); - - return SQLITE_OK; -} - -static int vt_connect(sqlite3 *db, void *p_aux, - int argc, const char *const *argv, - sqlite3_vtab **pp_vt, char **pzErr) -{ - return vt_create(db, p_aux, argc, argv, pp_vt, pzErr); -} - -static int vt_disconnect(sqlite3_vtab *pVtab) -{ - return vt_destructor(pVtab); -} - -static int vt_destroy(sqlite3_vtab *p_vt) -{ - return vt_destructor(p_vt); -} - -static int vt_next(sqlite3_vtab_cursor *cur); - -static int vt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor) -{ - vtab *p_vt = (vtab *)p_svt; - - p_vt->base.zErrMsg = NULL; - - vtab_cursor *p_cur = (vtab_cursor *)new vtab_cursor(); - - if (p_cur == NULL) { - return SQLITE_NOMEM; - } else { - *pp_cursor = (sqlite3_vtab_cursor *)p_cur; - - p_cur->base.pVtab = p_svt; - p_cur->vc_index = 0; + iterator end() { + return lnav_data.ld_files.end(); } - return SQLITE_OK; -} - -static int vt_close(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *p_cur = (vtab_cursor *)cur; - - /* Free cursor struct. */ - delete p_cur; - - return SQLITE_OK; -} - -static int vt_eof(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - - return vc->vc_index >= lnav_data.ld_files.size(); -} - -static int vt_next(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - - if (vc->vc_index < lnav_data.ld_files.size()) { - vc->vc_index += 1; + int get_column(const cursor &vc, sqlite3_context *ctx, int col) { + logfile *lf = *vc.iter; + const struct stat &st = lf->get_stat(); + const string &name = lf->get_filename(); + log_format *format = lf->get_format(); + const char *format_name = + format != nullptr ? format->get_name().get() : nullptr; + + switch (col) { + case 0: + sqlite3_result_int(ctx, st.st_dev); + break; + case 1: + sqlite3_result_int(ctx, st.st_ino); + break; + case 2: + sqlite3_result_text(ctx, name.c_str(), name.size(), + SQLITE_TRANSIENT); + break; + case 3: + if (format_name != nullptr) { + sqlite3_result_text(ctx, format_name, -1, SQLITE_STATIC); + } else { + sqlite3_result_null(ctx); + } + break; + case 4: + sqlite3_result_int(ctx, lf->size()); + break; + } + + return SQLITE_OK; } +}; - return SQLITE_OK; -} - -static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - logfile *lf = lnav_data.ld_files[vc->vc_index]; - const struct stat &st = lf->get_stat(); - const string &name = lf->get_filename(); - log_format *format = lf->get_format(); - const char *format_name = format != NULL ? format->get_name().get() : NULL; - - switch (col) { - case 0: - sqlite3_result_int(ctx, st.st_dev); - break; - case 1: - sqlite3_result_int(ctx, st.st_ino); - break; - case 2: - sqlite3_result_text(ctx, name.c_str(), name.size(), SQLITE_TRANSIENT); - break; - case 3: - if (format_name != NULL) { - sqlite3_result_text(ctx, format_name, -1, SQLITE_STATIC); - } else { - sqlite3_result_null(ctx); - } - break; - case 4: - sqlite3_result_int(ctx, lf->size()); - break; - } - - return SQLITE_OK; -} - -static int vt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) -{ - vtab_cursor *p_cur = (vtab_cursor *)cur; - - *p_rowid = (int64_t) p_cur->vc_index; - - return SQLITE_OK; -} - -static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) -{ - return SQLITE_OK; -} -static int vt_filter(sqlite3_vtab_cursor *p_vtc, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv) +int register_file_vtab(sqlite3 *db) { - return SQLITE_OK; -} + static vtab_module> LNAV_FILE_MODULE; -static int vt_update(sqlite3_vtab *tab, - int argc, - sqlite3_value **argv, - sqlite_int64 *rowid) -{ - return SQLITE_ERROR; -} + int rc; -static sqlite3_module vtab_module = { - 0, /* iVersion */ - vt_create, /* xCreate - create a vtable */ - vt_connect, /* xConnect - associate a vtable with a connection */ - vt_best_index, /* xBestIndex - best index */ - vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ - vt_destroy, /* xDestroy - destroy a vtable */ - vt_open, /* xOpen - open a cursor */ - vt_close, /* xClose - close a cursor */ - vt_filter, /* xFilter - configure scan constraints */ - vt_next, /* xNext - advance a cursor */ - vt_eof, /* xEof - inidicate end of result set*/ - vt_column, /* xColumn - read data */ - vt_rowid, /* xRowid - read data */ - vt_update, /* xUpdate - write data */ - NULL, /* xBegin - begin transaction */ - NULL, /* xSync - sync transaction */ - NULL, /* xCommit - commit transaction */ - NULL, /* xRollback - rollback transaction */ - NULL, /* xFindFunction - function overloading */ -}; + rc = LNAV_FILE_MODULE.create(db, "lnav_file"); -int register_file_vtab(sqlite3 *db) -{ - auto_mem errmsg; - int rc; + ensure(rc == SQLITE_OK); - rc = sqlite3_create_module(db, "lnav_file_vtab_impl", &vtab_module, NULL); - assert(rc == SQLITE_OK); - if ((rc = sqlite3_exec(db, - "CREATE VIRTUAL TABLE lnav_file USING lnav_file_vtab_impl()", - NULL, NULL, errmsg.out())) != SQLITE_OK) { - fprintf(stderr, "unable to create lnav_file table %s\n", errmsg.in()); - } return rc; } diff --git a/src/file_vtab.hh b/src/file_vtab.hh index c2428d4f..4f041a09 100644 --- a/src/file_vtab.hh +++ b/src/file_vtab.hh @@ -34,6 +34,4 @@ int register_file_vtab(sqlite3 *db); -extern const char *LNAV_FILE_CREATE_STMT; - #endif diff --git a/src/hotkeys.cc b/src/hotkeys.cc index 7f753e91..4df46f9d 100644 --- a/src/hotkeys.cc +++ b/src/hotkeys.cc @@ -494,9 +494,9 @@ void handle_paging_key(int ch) "----------------\n\n", (int) top, (int) bottom); log_perror(write(STDOUT_FILENO, line_break, strlen(line_break))); - for (; top <= bottom; ++top) { - attr_line_t al; - tc->listview_value_for_row(*tc, top, al); + vector rows(bottom - top); + tc->listview_value_for_rows(*tc, top, rows); + for (auto &al : rows) { struct line_range lr = find_string_attr_range( al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE); log_perror(write(STDOUT_FILENO, lr.substr(al.get_string()), diff --git a/src/listview_curses.cc b/src/listview_curses.cc index bcc07ac9..475a940c 100644 --- a/src/listview_curses.cc +++ b/src/listview_curses.cc @@ -206,6 +206,8 @@ void listview_curses::do_update(void) row_count = this->get_inner_height(); row = this->lv_top; bottom = y + height; + vector rows(min((size_t) height, row_count - (int) this->lv_top)); + this->lv_source->listview_value_for_rows(*this, row, rows); while (y < bottom) { lr.lr_start = this->lv_left; lr.lr_end = this->lv_left + wrap_width; @@ -219,9 +221,8 @@ void listview_curses::do_update(void) ++y; } else if (row < (int)row_count) { - attr_line_t al; + attr_line_t &al = rows[row - this->lv_top]; - this->lv_source->listview_value_for_row(*this, row, al); do { this->mvwattrline(this->lv_window, y, 0, al, lr); if (this->lv_word_wrap) { diff --git a/src/listview_curses.hh b/src/listview_curses.hh index 45b8bb58..9197505d 100644 --- a/src/listview_curses.hh +++ b/src/listview_curses.hh @@ -67,9 +67,9 @@ public: * @param row The row number. * @param value_out The destination for the string value. */ - virtual void listview_value_for_row(const listview_curses &lv, - vis_line_t row, - attr_line_t &value_out) = 0; + virtual void listview_value_for_rows(const listview_curses &lv, + vis_line_t start_row, + std::vector &rows_out) = 0; virtual size_t listview_size_for_row(const listview_curses &lv, vis_line_t row) = 0; diff --git a/src/lnav.cc b/src/lnav.cc index 68d48726..4bef89a4 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -725,9 +725,7 @@ static void open_schema_view(void) schema += "\n\n-- Virtual Table Definitions --\n\n"; schema += ENVIRON_CREATE_STMT; - schema += LNAV_VIEWS_CREATE_STMT; - schema += LNAV_VIEW_STACK_CREATE_STMT; - schema += LNAV_FILE_CREATE_STMT; + schema += vtab_module_schemas; for (log_vtab_manager::iterator vtab_iter = lnav_data.ld_vtab_manager->begin(); vtab_iter != lnav_data.ld_vtab_manager->end(); @@ -3182,8 +3180,6 @@ int main(int argc, char *argv[]) std::vector > msgs; std::vector >::iterator msg_iter; textview_curses *log_tc, *text_tc, *tc; - attr_line_t al; - const std::string &line = al.get_string(); bool found_error = false; lnav_data.ld_output_stack.push(stdout); @@ -3249,6 +3245,8 @@ int main(int argc, char *argv[]) for (vis_line_t vl = tc->get_top(); vl < tc->get_inner_height(); ++vl, ++y) { + attr_line_t al; + string &line = al.get_string(); while (los != NULL && los->list_value_for_overlay(*tc, y, al)) { if (write(STDOUT_FILENO, line.c_str(), @@ -3259,15 +3257,16 @@ int main(int argc, char *argv[]) ++y; } - tc->listview_value_for_row(*tc, vl, al); - if (suppress_empty_lines && line.empty()) { + vector rows(1); + tc->listview_value_for_rows(*tc, vl, rows); + if (suppress_empty_lines && rows[0].empty()) { continue; } struct line_range lr = find_string_attr_range( - al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE); - if (write(STDOUT_FILENO, lr.substr(al.get_string()), - lr.sublen(al.get_string())) == -1 || + rows[0].get_attrs(), &textview_curses::SA_ORIGINAL_LINE); + if (write(STDOUT_FILENO, lr.substr(rows[0].get_string()), + lr.sublen(rows[0].get_string())) == -1 || write(STDOUT_FILENO, "\n", 1) == -1) { perror("write to STDOUT"); } diff --git a/src/term_extra.hh b/src/term_extra.hh index 92c74351..00cd2d71 100644 --- a/src/term_extra.hh +++ b/src/term_extra.hh @@ -77,10 +77,10 @@ public: if (lc->get_inner_height() > 0) { string_attrs_t::const_iterator line_attr; - attr_line_t al; + std::vector rows(1); - lc->get_data_source()->listview_value_for_row(*lc, lc->get_top(), al); - string_attrs_t &sa = al.get_attrs(); + lc->get_data_source()->listview_value_for_rows(*lc, lc->get_top(), rows); + string_attrs_t &sa = rows[0].get_attrs(); line_attr = find_string_attr(sa, &logline::L_FILE); if (line_attr != sa.end()) { logfile *lf = (logfile *)line_attr->sa_value.sav_ptr; diff --git a/src/textview_curses.cc b/src/textview_curses.cc index 91ddee7e..a3195d94 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -116,8 +116,99 @@ void textview_curses::grep_match(grep_proc &gp, } } -void textview_curses::listview_value_for_row(const listview_curses &lv, - vis_line_t row, +void textview_curses::listview_value_for_rows(const listview_curses &lv, + vis_line_t row, + vector &rows_out) +{ + for (auto &al : rows_out) { + this->textview_value_for_row(row, al); + ++row; + } +} + +bool textview_curses::handle_mouse(mouse_event &me) +{ + unsigned long width; + vis_line_t height; + + if (this->tc_selection_start == -1 && + listview_curses::handle_mouse(me)) { + return true; + } + + if (this->tc_delegate != NULL && + this->tc_delegate->text_handle_mouse(*this, me)) { + return true; + } + + if (me.me_button != BUTTON_LEFT) { + return false; + } + + vis_line_t mouse_line(this->get_top() + me.me_y); + + if (mouse_line > this->get_bottom()) { + mouse_line = this->get_bottom(); + } + + this->get_dimensions(height, width); + + switch (me.me_state) { + case BUTTON_STATE_PRESSED: + this->tc_selection_start = mouse_line; + this->tc_selection_last = vis_line_t(-1); + this->tc_selection_cleared = false; + break; + case BUTTON_STATE_DRAGGED: + if (me.me_y <= 0) { + this->shift_top(vis_line_t(-1)); + me.me_y = 0; + mouse_line = this->get_top(); + } + if (me.me_y >= height && this->get_top() < this->get_top_for_last_row()) { + this->shift_top(vis_line_t(1)); + me.me_y = height; + mouse_line = this->get_bottom(); + } + + if (this->tc_selection_last == mouse_line) + break; + + if (this->tc_selection_last != -1) { + this->toggle_user_mark(&textview_curses::BM_USER, + this->tc_selection_start, + this->tc_selection_last); + } + if (this->tc_selection_start == mouse_line) { + this->tc_selection_last = vis_line_t(-1); + } + else { + if (!this->tc_selection_cleared) { + if (this->tc_sub_source != NULL) { + this->tc_sub_source->text_clear_marks(&BM_USER); + } + this->tc_bookmarks[&BM_USER].clear(); + + this->tc_selection_cleared = true; + } + this->toggle_user_mark(&BM_USER, + this->tc_selection_start, + mouse_line); + this->tc_selection_last = mouse_line; + } + this->reload_data(); + break; + case BUTTON_STATE_RELEASED: + this->tc_selection_start = vis_line_t(-1); + this->tc_selection_last = vis_line_t(-1); + this->tc_selection_cleared = false; + break; + } + + return true; +} + +void textview_curses::textview_value_for_row(vis_line_t row, attr_line_t &value_out) { view_colors &vc = view_colors::singleton(); @@ -290,85 +381,3 @@ void textview_curses::listview_value_for_row(const listview_curses &lv, line_range(0), &view_curses::VC_STYLE, A_UNDERLINE)); } } - -bool textview_curses::handle_mouse(mouse_event &me) -{ - unsigned long width; - vis_line_t height; - - if (this->tc_selection_start == -1 && - listview_curses::handle_mouse(me)) { - return true; - } - - if (this->tc_delegate != NULL && - this->tc_delegate->text_handle_mouse(*this, me)) { - return true; - } - - if (me.me_button != BUTTON_LEFT) { - return false; - } - - vis_line_t mouse_line(this->get_top() + me.me_y); - - if (mouse_line > this->get_bottom()) { - mouse_line = this->get_bottom(); - } - - this->get_dimensions(height, width); - - switch (me.me_state) { - case BUTTON_STATE_PRESSED: - this->tc_selection_start = mouse_line; - this->tc_selection_last = vis_line_t(-1); - this->tc_selection_cleared = false; - break; - case BUTTON_STATE_DRAGGED: - if (me.me_y <= 0) { - this->shift_top(vis_line_t(-1)); - me.me_y = 0; - mouse_line = this->get_top(); - } - if (me.me_y >= height && this->get_top() < this->get_top_for_last_row()) { - this->shift_top(vis_line_t(1)); - me.me_y = height; - mouse_line = this->get_bottom(); - } - - if (this->tc_selection_last == mouse_line) - break; - - if (this->tc_selection_last != -1) { - this->toggle_user_mark(&textview_curses::BM_USER, - this->tc_selection_start, - this->tc_selection_last); - } - if (this->tc_selection_start == mouse_line) { - this->tc_selection_last = vis_line_t(-1); - } - else { - if (!this->tc_selection_cleared) { - if (this->tc_sub_source != NULL) { - this->tc_sub_source->text_clear_marks(&BM_USER); - } - this->tc_bookmarks[&BM_USER].clear(); - - this->tc_selection_cleared = true; - } - this->toggle_user_mark(&BM_USER, - this->tc_selection_start, - mouse_line); - this->tc_selection_last = mouse_line; - } - this->reload_data(); - break; - case BUTTON_STATE_RELEASED: - this->tc_selection_start = vis_line_t(-1); - this->tc_selection_last = vis_line_t(-1); - this->tc_selection_cleared = false; - break; - } - - return true; -} diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 04f6fc59..d2731e43 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -573,12 +573,12 @@ public: int prev_hit = -1, next_hit = INT_MAX; for (; start < end; ++start) { - attr_line_t al; + std::vector rows(1); int off; - this->listview_value_for_row(*this, start, al); + this->listview_value_for_rows(*this, start, rows); - const std::string &str = al.get_string(); + const std::string &str = rows[0].get_string(); for (off = 0; off < (int)str.size(); ) { int rc, matches[128]; @@ -671,9 +671,11 @@ public: this->tc_sub_source->text_line_width(*this); }; - void listview_value_for_row(const listview_curses &lv, - vis_line_t line, - attr_line_t &value_out); + void listview_value_for_rows(const listview_curses &lv, + vis_line_t line, + std::vector &rows_out); + + void textview_value_for_row(vis_line_t line, attr_line_t &value_out); size_t listview_size_for_row(const listview_curses &lv, vis_line_t row) { return this->tc_sub_source->text_size_for_line(*this, row); diff --git a/src/top_status_source.hh b/src/top_status_source.hh index cd44a523..2041bf20 100644 --- a/src/top_status_source.hh +++ b/src/top_status_source.hh @@ -110,11 +110,11 @@ public: if (lc->get_inner_height() > 0) { string_attrs_t::const_iterator line_attr; - attr_line_t al; + std::vector rows(1); lc->get_data_source()-> - listview_value_for_row(*lc, lc->get_top(), al); - string_attrs_t &sa = al.get_attrs(); + listview_value_for_rows(*lc, lc->get_top(), rows); + string_attrs_t &sa = rows[0].get_attrs(); line_attr = find_string_attr(sa, &logline::L_FILE); if (line_attr != sa.end()) { logfile *lf = (logfile *)line_attr->sa_value.sav_ptr; diff --git a/src/view_curses.hh b/src/view_curses.hh index 54c00393..5348ce63 100644 --- a/src/view_curses.hh +++ b/src/view_curses.hh @@ -396,6 +396,10 @@ public: return retval; }; + bool empty() const { + return this->length() == 0; + }; + /** Clear the string and the attributes for the string. */ attr_line_t &clear() { diff --git a/src/views_vtab.cc b/src/views_vtab.cc index 80ce7061..82c2c210 100644 --- a/src/views_vtab.cc +++ b/src/views_vtab.cc @@ -34,8 +34,9 @@ #include #include +#include + #include "lnav.hh" -#include "auto_mem.hh" #include "lnav_log.hh" #include "sql_util.hh" #include "views_vtab.hh" @@ -43,408 +44,178 @@ using namespace std; -const char *LNAV_VIEWS_CREATE_STMT = "\ --- Access lnav's views through this table.\n\ -CREATE TABLE lnav_views (\n\ - -- The name of the view.\n\ - name text PRIMARY KEY,\n\ - -- The number of the line at the top of the view, starting from zero.\n\ - top integer,\n\ - -- The left position of the viewport.\n\ - left integer,\n\ - -- The height of the viewport.\n\ - height integer,\n\ - -- The number of lines in the view.\n\ - inner_height integer,\n\ - -- The time of the top line in the view, if the content is time-based.\n\ - top_time datetime\n\ -);\n\ -"; - -const char *LNAV_VIEW_STACK_CREATE_STMT = "\ --- Access lnav's view stack through this table.\n\ -CREATE TABLE lnav_view_stack (\n\ - name text\n\ -);\n\ -"; - -struct vtab { - sqlite3_vtab base; - sqlite3 * db; -}; - -struct vtab_cursor { - sqlite3_vtab_cursor base; - lnav_view_t vc_cursor; -}; - -struct stack_vtab_cursor { - sqlite3_vtab_cursor base; - int vc_index; -}; - -static int vt_destructor(sqlite3_vtab *p_svt); - -static int vt_create(sqlite3 *db, - void *pAux, - int argc, const char *const *argv, - sqlite3_vtab **pp_vt, - char **pzErr) -{ - vtab *p_vt; - - /* Allocate the sqlite3_vtab/vtab structure itself */ - p_vt = (vtab *)sqlite3_malloc(sizeof(*p_vt)); - - if (p_vt == NULL) { - return SQLITE_NOMEM; - } - - memset(&p_vt->base, 0, sizeof(sqlite3_vtab)); - p_vt->db = db; - - *pp_vt = &p_vt->base; - - int rc = sqlite3_declare_vtab(db, LNAV_VIEWS_CREATE_STMT); - - return rc; -} - - -static int vt_destructor(sqlite3_vtab *p_svt) -{ - vtab *p_vt = (vtab *)p_svt; - - /* Free the SQLite structure */ - sqlite3_free(p_vt); - - return SQLITE_OK; -} - -static int vt_connect(sqlite3 *db, void *p_aux, - int argc, const char *const *argv, - sqlite3_vtab **pp_vt, char **pzErr) -{ - return vt_create(db, p_aux, argc, argv, pp_vt, pzErr); -} - -static int vt_disconnect(sqlite3_vtab *pVtab) -{ - return vt_destructor(pVtab); -} - -static int vt_destroy(sqlite3_vtab *p_vt) -{ - return vt_destructor(p_vt); -} - -static int vt_next(sqlite3_vtab_cursor *cur); - -static int vt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor) -{ - vtab *p_vt = (vtab *)p_svt; - - p_vt->base.zErrMsg = NULL; - - vtab_cursor *p_cur = new vtab_cursor(); - - if (p_cur == NULL) { - return SQLITE_NOMEM; - } else { - *pp_cursor = (sqlite3_vtab_cursor *)p_cur; - - p_cur->base.pVtab = p_svt; - p_cur->vc_cursor = (lnav_view_t) -1; - - vt_next((sqlite3_vtab_cursor *)p_cur); +struct lnav_views : public tvt_iterator_cursor { + struct vtab { + sqlite3_vtab base; + + operator sqlite3_vtab *() { + return &this->base; + }; + }; + + static constexpr const char *CREATE_STMT = R"( +-- Access lnav's views through this table. +CREATE TABLE lnav_views ( + -- The name of the view. + name text PRIMARY KEY, + -- The number of the line at the top of the view, starting from zero. + top integer, + -- The left position of the viewport. + left integer, + -- The height of the viewport. + height integer, + -- The number of lines in the view. + inner_height integer, + -- The time of the top line in the view, if the content is time-based. + top_time datetime +); +)"; + + using iterator = textview_curses *; + + iterator begin() { + return std::begin(lnav_data.ld_views); } - return SQLITE_OK; -} - -static int vt_close(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *p_cur = (vtab_cursor *)cur; - - /* Free cursor struct. */ - delete p_cur; - - return SQLITE_OK; -} - -static int vt_eof(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - - return vc->vc_cursor == LNV__MAX; -} - -static int vt_next(sqlite3_vtab_cursor *cur) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - - if (vc->vc_cursor < LNV__MAX) { - vc->vc_cursor = (lnav_view_t) (vc->vc_cursor + 1); + iterator end() { + return std::end(lnav_data.ld_views); } - return SQLITE_OK; -} - -static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) -{ - vtab_cursor *vc = (vtab_cursor *)cur; - textview_curses &tc = lnav_data.ld_views[vc->vc_cursor]; - unsigned long width; - vis_line_t height; - - tc.get_dimensions(height, width); - switch (col) { - case 0: - sqlite3_result_text(ctx, - lnav_view_strings[vc->vc_cursor], -1, - SQLITE_STATIC); - break; - case 1: - sqlite3_result_int(ctx, (int) tc.get_top()); - break; - case 2: - sqlite3_result_int(ctx, tc.get_left()); - break; - case 3: - sqlite3_result_int(ctx, height); - break; - case 4: - sqlite3_result_int(ctx, tc.get_inner_height()); - break; - case 5: { - text_time_translator *time_source = dynamic_cast(tc.get_sub_source()); - - if (time_source != NULL && tc.get_inner_height() > 0) { - char timestamp[64]; - - sql_strftime(timestamp, sizeof(timestamp), time_source->time_for_row(tc.get_top()), 0, 'T'); - - sqlite3_result_text(ctx, timestamp, -1, SQLITE_TRANSIENT); - } else { - sqlite3_result_null(ctx); + int get_column(cursor &vc, sqlite3_context *ctx, int col) { + textview_curses &tc = *vc.iter; + unsigned long width; + vis_line_t height; + + tc.get_dimensions(height, width); + switch (col) { + case 0: + sqlite3_result_text(ctx, + lnav_view_strings[distance(std::begin(lnav_data.ld_views), vc.iter)], -1, + SQLITE_STATIC); + break; + case 1: + sqlite3_result_int(ctx, (int) tc.get_top()); + break; + case 2: + sqlite3_result_int(ctx, tc.get_left()); + break; + case 3: + sqlite3_result_int(ctx, height); + break; + case 4: + sqlite3_result_int(ctx, tc.get_inner_height()); + break; + case 5: { + text_time_translator *time_source = dynamic_cast(tc.get_sub_source()); + + if (time_source != nullptr && tc.get_inner_height() > 0) { + char timestamp[64]; + + sql_strftime(timestamp, sizeof(timestamp), time_source->time_for_row(tc.get_top()), 0, 'T'); + + sqlite3_result_text(ctx, timestamp, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; } - break; } - } - - return SQLITE_OK; -} - -static int vt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) -{ - vtab_cursor *p_cur = (vtab_cursor *)cur; - - *p_rowid = p_cur->vc_cursor; - - return SQLITE_OK; -} - -static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) -{ - return SQLITE_OK; -} - -static int vt_filter(sqlite3_vtab_cursor *p_vtc, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv) -{ - return SQLITE_OK; -} -static int vt_update(sqlite3_vtab *tab, - int argc, - sqlite3_value **argv, - sqlite_int64 *rowid) -{ - if (argc <= 1) { - tab->zErrMsg = sqlite3_mprintf( - "Rows cannot be deleted from the lnav_views table"); - return SQLITE_ERROR; + return SQLITE_OK; } - if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + int delete_row(sqlite3_vtab *tab, int64_t rowid) { tab->zErrMsg = sqlite3_mprintf( - "Rows cannot be inserted into the lnav_views table"); + "Rows cannot be deleted from the lnav_views table"); return SQLITE_ERROR; } - int64_t index = sqlite3_value_int64(argv[0]); - - if (index != sqlite3_value_int64(argv[1])) { + int insert_row(sqlite3_vtab *tab, int64_t &rowid_out) { tab->zErrMsg = sqlite3_mprintf( - "The rowids in the lnav_views table cannot be changed"); + "Rows cannot be inserted into the lnav_views table"); return SQLITE_ERROR; - } - - textview_curses &tc = lnav_data.ld_views[index]; - text_time_translator *time_source = dynamic_cast(tc.get_sub_source()); - int64_t top = sqlite3_value_int64(argv[3]); - int64_t left = sqlite3_value_int64(argv[4]); - const unsigned char *top_time = sqlite3_value_text(argv[7]); - - if (tc.get_top() != top) { - tc.set_top(vis_line_t(top)); - } else if (top_time != NULL && time_source != NULL) { - date_time_scanner dts; - struct timeval tv; - - if (dts.convert_to_timeval((const char *) top_time, -1, NULL, tv)) { - time_t last_time = time_source->time_for_row(tc.get_top()); - - if (tv.tv_sec != last_time) { - int row = time_source->row_for_time(tv.tv_sec); - - tc.set_top(vis_line_t(row)); + }; + + int update_row(sqlite3_vtab *tab, + int64_t &index, + const char *name, + int64_t top_row, + int64_t left, + int64_t height, + int64_t inner_height, + const char *top_time) { + textview_curses &tc = lnav_data.ld_views[index]; + text_time_translator *time_source = dynamic_cast(tc.get_sub_source()); + + if (tc.get_top() != top_row) { + tc.set_top(vis_line_t(top_row)); + } else if (top_time != nullptr && time_source != nullptr) { + date_time_scanner dts; + struct timeval tv; + + if (dts.convert_to_timeval(top_time, -1, nullptr, tv)) { + time_t last_time = time_source->time_for_row(tc.get_top()); + + if (tv.tv_sec != last_time) { + int row = time_source->row_for_time(tv.tv_sec); + + tc.set_top(vis_line_t(row)); + } + } else { + tab->zErrMsg = sqlite3_mprintf("Invalid time: %s", top_time); + return SQLITE_ERROR; } - } else { - tab->zErrMsg = sqlite3_mprintf("Invalid time: %s", top_time); - return SQLITE_ERROR; } - } - tc.set_left(left); - - return SQLITE_OK; -} - -static sqlite3_module views_vtab_module = { - 0, /* iVersion */ - vt_create, /* xCreate - create a vtable */ - vt_connect, /* xConnect - associate a vtable with a connection */ - vt_best_index, /* xBestIndex - best index */ - vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ - vt_destroy, /* xDestroy - destroy a vtable */ - vt_open, /* xOpen - open a cursor */ - vt_close, /* xClose - close a cursor */ - vt_filter, /* xFilter - configure scan constraints */ - vt_next, /* xNext - advance a cursor */ - vt_eof, /* xEof - inidicate end of result set*/ - vt_column, /* xColumn - read data */ - vt_rowid, /* xRowid - read data */ - vt_update, /* xUpdate - write data */ - NULL, /* xBegin - begin transaction */ - NULL, /* xSync - sync transaction */ - NULL, /* xCommit - commit transaction */ - NULL, /* xRollback - rollback transaction */ - NULL, /* xFindFunction - function overloading */ + tc.set_left(left); + return SQLITE_OK; + }; }; -static int vt_stack_create(sqlite3 *db, - void *pAux, - int argc, const char *const *argv, - sqlite3_vtab **pp_vt, - char **pzErr) -{ - vtab *p_vt; - - /* Allocate the sqlite3_vtab/vtab structure itself */ - p_vt = (vtab *)sqlite3_malloc(sizeof(*p_vt)); - - if (p_vt == NULL) { - return SQLITE_NOMEM; - } - - memset(&p_vt->base, 0, sizeof(sqlite3_vtab)); - p_vt->db = db; - - *pp_vt = &p_vt->base; - - int rc = sqlite3_declare_vtab(db, LNAV_VIEW_STACK_CREATE_STMT); - - return rc; -} - -static int vt_stack_next(sqlite3_vtab_cursor *cur); - -static int vt_stack_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor) -{ - vtab *p_vt = (vtab *)p_svt; - - p_vt->base.zErrMsg = NULL; - - stack_vtab_cursor *p_cur = new stack_vtab_cursor(); +struct lnav_view_stack : public tvt_iterator_cursor { + using iterator = vector::iterator; - if (p_cur == NULL) { - return SQLITE_NOMEM; - } else { - *pp_cursor = &p_cur->base; + static constexpr const char *CREATE_STMT = R"( +-- Access lnav's view stack through this table. +CREATE TABLE lnav_view_stack ( + name text +); +)"; - p_cur->base.pVtab = p_svt; - p_cur->vc_index = 0; - } - - return SQLITE_OK; -} - -static int vt_stack_close(sqlite3_vtab_cursor *cur) -{ - stack_vtab_cursor *p_cur = (stack_vtab_cursor *)cur; - - /* Free cursor struct. */ - delete p_cur; - - return SQLITE_OK; -} - -static int vt_stack_eof(sqlite3_vtab_cursor *cur) -{ - stack_vtab_cursor *vc = (stack_vtab_cursor *)cur; + struct vtab { + sqlite3_vtab base; - return vc->vc_index == lnav_data.ld_view_stack.size(); -} + operator sqlite3_vtab *() { + return &this->base; + }; + }; -static int vt_stack_next(sqlite3_vtab_cursor *cur) -{ - stack_vtab_cursor *vc = (stack_vtab_cursor *)cur; - - if (vc->vc_index < lnav_data.ld_view_stack.size()) { - vc->vc_index += 1; + iterator begin() { + return lnav_data.ld_view_stack.begin(); } - return SQLITE_OK; -} - -static int vt_stack_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) -{ - stack_vtab_cursor *vc = (stack_vtab_cursor *)cur; - textview_curses *tc = lnav_data.ld_view_stack[vc->vc_index]; - lnav_view_t view = lnav_view_t(tc - lnav_data.ld_views); - - switch (col) { - case 0: - sqlite3_result_text(ctx, - lnav_view_strings[view], -1, - SQLITE_STATIC); - break; + iterator end() { + return lnav_data.ld_view_stack.end(); } - return SQLITE_OK; -} + int get_column(cursor &vc, sqlite3_context *ctx, int col) { + textview_curses *tc = *vc.iter; + lnav_view_t view = lnav_view_t(tc - lnav_data.ld_views); -static int vt_stack_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) -{ - stack_vtab_cursor *p_cur = (stack_vtab_cursor *)cur; - - *p_rowid = p_cur->vc_index; - - return SQLITE_OK; -} + switch (col) { + case 0: + sqlite3_result_text(ctx, + lnav_view_strings[view], -1, + SQLITE_STATIC); + break; + } -static int vt_stack_update(sqlite3_vtab *tab, - int argc, - sqlite3_value **argv, - sqlite_int64 *rowid) -{ - if (argc <= 1) { - int64_t index = sqlite3_value_int64(argv[0]); + return SQLITE_OK; + }; - if (index != lnav_data.ld_view_stack.size() - 1) { + int delete_row(sqlite3_vtab *tab, int64_t rowid) { + if (rowid != lnav_data.ld_view_stack.size() - 1) { tab->zErrMsg = sqlite3_mprintf( - "Only the top view in the stack can be deleted"); + "Only the top view in the stack can be deleted"); return SQLITE_ERROR; } @@ -458,23 +229,23 @@ static int vt_stack_update(sqlite3_vtab *tab, } return SQLITE_OK; - } + }; - if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { - const unsigned char *name = sqlite3_value_text(argv[2]); - - if (name == NULL) { + int insert_row(sqlite3_vtab *tab, + int64_t &rowid_out, + const char *name) { + if (name == nullptr) { tab->zErrMsg = sqlite3_mprintf("'name' cannot be null"); return SQLITE_ERROR; } auto view_name_iter = find_if( - begin(lnav_view_strings), end(lnav_view_strings), + ::begin(lnav_view_strings), ::end(lnav_view_strings), [&](const char *v) { - return v != NULL && strcmp(v, (const char *) name) == 0; + return v != nullptr && strcmp(v, name) == 0; }); - if (view_name_iter == end(lnav_view_strings)) { + if (view_name_iter == ::end(lnav_view_strings)) { tab->zErrMsg = sqlite3_mprintf("Unknown view name: %s", name); return SQLITE_ERROR; } @@ -485,60 +256,30 @@ static int vt_stack_update(sqlite3_vtab *tab, tc->set_needs_update(); lnav_data.ld_view_stack_broadcaster.invoke(tc); - *rowid = lnav_data.ld_view_stack.size() - 1; + rowid_out = lnav_data.ld_view_stack.size() - 1; return SQLITE_OK; - } + }; - tab->zErrMsg = sqlite3_mprintf( - "The lnav_view_stack table cannot be updated"); - return SQLITE_ERROR; -} - -static sqlite3_module view_stack_vtab_module = { - 0, /* iVersion */ - vt_stack_create, /* xCreate - create a vtable */ - vt_connect, /* xConnect - associate a vtable with a connection */ - vt_best_index, /* xBestIndex - best index */ - vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ - vt_destroy, /* xDestroy - destroy a vtable */ - vt_stack_open, /* xOpen - open a cursor */ - vt_stack_close, /* xClose - close a cursor */ - vt_filter, /* xFilter - configure scan constraints */ - vt_stack_next, /* xNext - advance a cursor */ - vt_stack_eof, /* xEof - inidicate end of result set*/ - vt_stack_column, /* xColumn - read data */ - vt_stack_rowid, /* xRowid - read data */ - vt_stack_update, /* xUpdate - write data */ - NULL, /* xBegin - begin transaction */ - NULL, /* xSync - sync transaction */ - NULL, /* xCommit - commit transaction */ - NULL, /* xRollback - rollback transaction */ - NULL, /* xFindFunction - function overloading */ + int update_row(sqlite3_vtab *tab, int64_t &index) { + tab->zErrMsg = sqlite3_mprintf( + "The lnav_view_stack table cannot be updated"); + return SQLITE_ERROR; + }; }; int register_views_vtab(sqlite3 *db) { - auto_mem errmsg; + static vtab_module LNAV_VIEWS_MODULE; + static vtab_module LNAV_VIEW_STACK_MODULE; + int rc; - rc = sqlite3_create_module(db, "views_vtab_impl", &views_vtab_module, NULL); + rc = LNAV_VIEWS_MODULE.create(db, "lnav_views"); assert(rc == SQLITE_OK); - if ((rc = sqlite3_exec(db, - "CREATE VIRTUAL TABLE lnav_views USING views_vtab_impl()", - NULL, NULL, errmsg.out())) != SQLITE_OK) { - fprintf(stderr, "error: unable to create lnav_views %s\n", errmsg.in()); - } - rc = sqlite3_create_module(db, - "view_stack_vtab_impl", - &view_stack_vtab_module, - NULL); + rc = LNAV_VIEW_STACK_MODULE.create(db, "lnav_view_stack"); assert(rc == SQLITE_OK); - if ((rc = sqlite3_exec(db, - "CREATE VIRTUAL TABLE lnav_view_stack USING view_stack_vtab_impl()", - NULL, NULL, errmsg.out())) != SQLITE_OK) { - fprintf(stderr, "error: unable to create lnav_view_stack %s\n", errmsg.in()); - } + return rc; } diff --git a/src/views_vtab.hh b/src/views_vtab.hh index ba970307..91fb2a7b 100644 --- a/src/views_vtab.hh +++ b/src/views_vtab.hh @@ -32,9 +32,8 @@ #include -int register_views_vtab(sqlite3 *db); +#include "vtab_module.hh" -extern const char *LNAV_VIEWS_CREATE_STMT; -extern const char *LNAV_VIEW_STACK_CREATE_STMT; +int register_views_vtab(sqlite3 *db); #endif diff --git a/src/vtab_module.cc b/src/vtab_module.cc new file mode 100644 index 00000000..9613c2ee --- /dev/null +++ b/src/vtab_module.cc @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2017, 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. + */ + +#include "config.h" + +#include "vtab_module.hh" + +std::string vtab_module_schemas; diff --git a/src/vtab_module.hh b/src/vtab_module.hh new file mode 100644 index 00000000..6396ca39 --- /dev/null +++ b/src/vtab_module.hh @@ -0,0 +1,287 @@ +/** + * Copyright (c) 2017, 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 __vtab_module_hh +#define __vtab_module_hh + +#include + +#include +#include + +#include "lnav_log.hh" + +template +inline T from_sqlite(sqlite3_value *val) +{ + return T(); +} + +template<> +inline int64_t from_sqlite(sqlite3_value *val) +{ + return sqlite3_value_int64(val); +} + +template<> +inline const char *from_sqlite(sqlite3_value *val) +{ + return (const char *) sqlite3_value_text(val); +} + +template<> +inline double from_sqlite(sqlite3_value *val) +{ + return sqlite3_value_double(val); +} + +extern std::string vtab_module_schemas; + +template +struct vtab_module { + static int tvt_create(sqlite3 *db, + void *pAux, + int argc, const char *const *argv, + sqlite3_vtab **pp_vt, + char **pzErr) { + static typename T::vtab vt; + + *pp_vt = vt; + + return sqlite3_declare_vtab(db, T::CREATE_STMT); + }; + + template + static int apply_impl(T &obj, int (T::*func)(sqlite3_vtab *, int64_t &, Args...), sqlite3_vtab *tab, int64_t &rowid, sqlite3_value **argv, std::index_sequence) + { + return (obj.*func)(tab, rowid, from_sqlite(argv[Idx])...); + } + + template + static int apply(T &obj, + int (T::*func)(sqlite3_vtab *, int64_t &, Args...), + sqlite3_vtab *tab, + int64_t &rowid, + int argc, + sqlite3_value **argv) + { + require(sizeof...(Args) == 0 || argc == sizeof...(Args)); + + return apply_impl(obj, + func, + tab, + rowid, + argv, + std::make_index_sequence{}); + } + + static int tvt_destructor(sqlite3_vtab *p_svt) + { + return SQLITE_OK; + } + + static int tvt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor) + { + p_svt->zErrMsg = NULL; + + typename T::cursor *p_cur = new (typename T::cursor)(p_svt); + + if (p_cur == NULL) { + return SQLITE_NOMEM; + } else { + *pp_cursor = (sqlite3_vtab_cursor *) p_cur; + } + + return SQLITE_OK; + } + + static int tvt_next(sqlite3_vtab_cursor *cur) + { + typename T::cursor *p_cur = (typename T::cursor *) cur; + + return p_cur->next(); + } + + static int tvt_eof(sqlite3_vtab_cursor *cur) + { + typename T::cursor *p_cur = (typename T::cursor *) cur; + + return p_cur->eof(); + } + + static int tvt_close(sqlite3_vtab_cursor *cur) + { + typename T::cursor *p_cur = (typename T::cursor *) cur; + + delete p_cur; + + return SQLITE_OK; + } + + + static int tvt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) { + typename T::cursor *p_cur = (typename T::cursor *) cur; + + return p_cur->get_rowid(*p_rowid); + }; + + static int tvt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) { + typename T::cursor *p_cur = (typename T::cursor *) cur; + T handler; + + return handler.get_column(*p_cur, ctx, col); + }; + + static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) { + return SQLITE_OK; + }; + + static int vt_filter(sqlite3_vtab_cursor *p_vtc, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv) { + return SQLITE_OK; + } + + static int tvt_update(sqlite3_vtab *tab, + int argc, + sqlite3_value **argv, + sqlite_int64 *rowid) { + T handler; + + if (argc <= 1) { + int64_t rowid = sqlite3_value_int64(argv[0]); + + return handler.delete_row(tab, rowid); + } + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + int64_t *rowid2 = rowid; + return vtab_module::apply(handler, &T::insert_row, tab, *rowid2, argc - 2, argv + 2); + } + + int64_t index = sqlite3_value_int64(argv[0]); + + if (index != sqlite3_value_int64(argv[1])) { + tab->zErrMsg = sqlite3_mprintf( + "The rowids in the lnav_views table cannot be changed"); + return SQLITE_ERROR; + } + + return vtab_module::apply(handler, &T::update_row, tab, index, argc - 2, argv + 2); + }; + + vtab_module() noexcept { + memset(&this->vm_module, 0, sizeof(this->vm_module)); + this->vm_module.iVersion = 0; + this->vm_module.xCreate = tvt_create; + this->vm_module.xConnect = tvt_create; + this->vm_module.xOpen = tvt_open; + this->vm_module.xNext = tvt_next; + this->vm_module.xEof = tvt_eof; + this->vm_module.xClose = tvt_close; + this->vm_module.xDestroy = tvt_destructor; + this->vm_module.xRowid = tvt_rowid; + this->vm_module.xDisconnect = tvt_destructor; + this->vm_module.xBestIndex = vt_best_index; + this->vm_module.xFilter = vt_filter; + this->vm_module.xColumn = tvt_column; + this->vm_module.xUpdate = tvt_update; + }; + + int create(sqlite3 *db, const char *name) { + vtab_module_schemas += T::CREATE_STMT; + + return sqlite3_create_module(db, name, &this->vm_module, NULL); + }; + + sqlite3_module vm_module; +}; + +template +struct tvt_iterator_cursor { + struct cursor { + sqlite3_vtab_cursor base; + typename T::iterator iter; + + cursor(sqlite3_vtab *vt) { + T handler; + + this->base.pVtab = vt; + this->iter = handler.begin(); + }; + + int next() { + T handler; + + if (this->iter != handler.end()) { + ++this->iter; + } + + return SQLITE_OK; + }; + + int eof() { + T handler; + + return this->iter == handler.end(); + }; + + int get_rowid(sqlite_int64 &rowid_out) { + T handler; + + rowid_out = std::distance(handler.begin(), this->iter); + + return SQLITE_OK; + }; + }; +}; + +template +struct tvt_no_update : public T { + int delete_row(sqlite3_vtab *vt, int64_t rowid) { + vt->zErrMsg = sqlite3_mprintf( + "Rows cannot be deleted from this table"); + return SQLITE_ERROR; + }; + + int insert_row(sqlite3_vtab *tab, int64_t &rowid_out) { + tab->zErrMsg = sqlite3_mprintf( + "Rows cannot be inserted into this table"); + return SQLITE_ERROR; + }; + + int update_row(sqlite3_vtab *tab, int64_t &rowid_out) { + tab->zErrMsg = sqlite3_mprintf( + "Rows cannot be update in this table"); + return SQLITE_ERROR; + }; + +}; + +#endif diff --git a/test/drive_listview.cc b/test/drive_listview.cc index 81b9188e..4398c7fc 100644 --- a/test/drive_listview.cc +++ b/test/drive_listview.cc @@ -50,23 +50,23 @@ public: return this->ms_rows; }; - void listview_value_for_row(const listview_curses &lv, - vis_line_t row, - attr_line_t &value_out) { - if (row == 0) { - value_out = "Hello"; - } - else if (row == 1) { - value_out = "World!"; - } - else if (row < this->ms_rows) { - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%d", (int)row); - value_out = string(buffer); - } - else { - assert(0); + void listview_value_for_rows(const listview_curses &lv, + vis_line_t row, + vector &rows) { + for (auto &value_out : rows) { + if (row == 0) { + value_out = "Hello"; + } else if (row == 1) { + value_out = "World!"; + } else if (row < this->ms_rows) { + char buffer[32]; + + snprintf(buffer, sizeof(buffer), "%d", (int) row); + value_out = string(buffer); + } else { + assert(0); + } + ++row; } }; diff --git a/test/test_sql.sh b/test/test_sql.sh index cf86f44f..60ca4152 100644 --- a/test/test_sql.sh +++ b/test/test_sql.sh @@ -497,7 +497,7 @@ EOF schema_dump() { - ${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n11 + ${lnav_test} -n -c ';.schema' ${test_dir}/logfile_access_log.0 | head -n8 } run_test schema_dump @@ -505,9 +505,6 @@ run_test schema_dump check_output "schema view is not working" <