mirror of
https://github.com/tstack/lnav.git
synced 2024-08-17 08:50:41 +03:00
[sqlite] implement .dump SQL command
This commit is contained in:
parent
6f4791ec35
commit
c0ed59e61e
1
NEWS
1
NEWS
@ -53,6 +53,7 @@ lnav v0.9.1:
|
|||||||
* The "generate_series()" SQLite extension is now included by default.
|
* The "generate_series()" SQLite extension is now included by default.
|
||||||
One change from the standard implementation is that both the start and
|
One change from the standard implementation is that both the start and
|
||||||
stop are required parameters.
|
stop are required parameters.
|
||||||
|
* Added the ";.read" SQL command for executing a plain SQL file.
|
||||||
|
|
||||||
Interface Changes:
|
Interface Changes:
|
||||||
* When copying log lines, the file name and time offset will be included
|
* When copying log lines, the file name and time offset will be included
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
# aminclude_static.am generated automatically by Autoconf
|
# aminclude_static.am generated automatically by Autoconf
|
||||||
# from AX_AM_MACROS_STATIC on Tue Mar 23 22:11:31 PDT 2021
|
# from AX_AM_MACROS_STATIC on Sun Mar 28 13:05:16 PDT 2021
|
||||||
|
|
||||||
|
|
||||||
# Code coverage
|
# Code coverage
|
||||||
|
@ -5,3 +5,10 @@ hunter_config(
|
|||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
EXTRA_FLAGS=--enable-unicode-properties --enable-jit --enable-utf
|
EXTRA_FLAGS=--enable-unicode-properties --enable-jit --enable-utf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
hunter_config(
|
||||||
|
readline
|
||||||
|
VERSION 6.3
|
||||||
|
CMAKE_ARGS
|
||||||
|
EXTRA_FLAGS=CFLAGS=-Wno-implicit-function-declaration
|
||||||
|
)
|
||||||
|
@ -90,8 +90,8 @@ AC_CHECK_SIZEOF(size_t)
|
|||||||
AC_STRUCT_TIMEZONE
|
AC_STRUCT_TIMEZONE
|
||||||
|
|
||||||
AC_ARG_ENABLE([static],
|
AC_ARG_ENABLE([static],
|
||||||
AS_HELP_STRING([--disable-static],
|
AS_HELP_STRING([--enable-static],
|
||||||
[Disable static linking]))
|
[Enable static linking]))
|
||||||
|
|
||||||
AC_SEARCH_LIBS(openpty, util)
|
AC_SEARCH_LIBS(openpty, util)
|
||||||
AC_SEARCH_LIBS(gzseek, z, [], [AC_MSG_ERROR([libz required to build])])
|
AC_SEARCH_LIBS(gzseek, z, [], [AC_MSG_ERROR([libz required to build])])
|
||||||
@ -101,7 +101,7 @@ AC_SEARCH_LIBS(BZ2_bzopen, bz2,
|
|||||||
AC_SUBST(BZIP2_SUPPORT)
|
AC_SUBST(BZIP2_SUPPORT)
|
||||||
AC_SEARCH_LIBS(dlopen, dl)
|
AC_SEARCH_LIBS(dlopen, dl)
|
||||||
AC_SEARCH_LIBS(backtrace, execinfo)
|
AC_SEARCH_LIBS(backtrace, execinfo)
|
||||||
LIBCURL_CHECK_CONFIG([], [7.23.0], [], [], [test x"${enable_static}" != x"no"])
|
LIBCURL_CHECK_CONFIG([], [7.23.0], [], [])
|
||||||
|
|
||||||
# Sometimes, curses depends on these libraries being linked in...
|
# Sometimes, curses depends on these libraries being linked in...
|
||||||
AC_ARG_ENABLE([tinfo],
|
AC_ARG_ENABLE([tinfo],
|
||||||
@ -193,7 +193,7 @@ AS_VAR_SET(static_lib_list,
|
|||||||
AS_VAR_SET(static_lib_list,
|
AS_VAR_SET(static_lib_list,
|
||||||
["$static_lib_list libarchive.a"])
|
["$static_lib_list libarchive.a"])
|
||||||
|
|
||||||
if test x"${enable_static}" != x"no"; then
|
if test x"${enable_static}" = x"yes"; then
|
||||||
case "$host_os" in
|
case "$host_os" in
|
||||||
darwin*)
|
darwin*)
|
||||||
STATIC_LDFLAGS="$STATIC_LDFLAGS -Wl,-search_paths_first"
|
STATIC_LDFLAGS="$STATIC_LDFLAGS -Wl,-search_paths_first"
|
||||||
|
@ -266,6 +266,7 @@ add_library(diag STATIC
|
|||||||
hist_source.cc
|
hist_source.cc
|
||||||
hotkeys.cc
|
hotkeys.cc
|
||||||
base/humanize.cc
|
base/humanize.cc
|
||||||
|
base/humanize.time.cc
|
||||||
input_dispatcher.cc
|
input_dispatcher.cc
|
||||||
base/intern_string.cc
|
base/intern_string.cc
|
||||||
base/is_utf8.cc
|
base/is_utf8.cc
|
||||||
@ -314,6 +315,7 @@ add_library(diag STATIC
|
|||||||
pcrepp/pcrepp.cc
|
pcrepp/pcrepp.cc
|
||||||
piper_proc.cc
|
piper_proc.cc
|
||||||
spectro_source.cc
|
spectro_source.cc
|
||||||
|
sql_commands.cc
|
||||||
sql_util.cc
|
sql_util.cc
|
||||||
state-extension-functions.cc
|
state-extension-functions.cc
|
||||||
styling.cc
|
styling.cc
|
||||||
@ -358,6 +360,7 @@ add_library(diag STATIC
|
|||||||
spookyhash/SpookyV2.cpp
|
spookyhash/SpookyV2.cpp
|
||||||
|
|
||||||
third-party/sqlite/ext/series.c
|
third-party/sqlite/ext/series.c
|
||||||
|
third-party/sqlite/ext/dbdump.c
|
||||||
|
|
||||||
all_logs_vtab.hh
|
all_logs_vtab.hh
|
||||||
archive_manager.hh
|
archive_manager.hh
|
||||||
@ -394,6 +397,7 @@ add_library(diag STATIC
|
|||||||
highlighter.hh
|
highlighter.hh
|
||||||
hotkeys.hh
|
hotkeys.hh
|
||||||
base/humanize.hh
|
base/humanize.hh
|
||||||
|
base/humanize.time.hh
|
||||||
input_dispatcher.hh
|
input_dispatcher.hh
|
||||||
base/injector.hh
|
base/injector.hh
|
||||||
base/injector.bind.hh
|
base/injector.bind.hh
|
||||||
@ -440,6 +444,7 @@ add_library(diag STATIC
|
|||||||
shlex.hh
|
shlex.hh
|
||||||
simdutf8check.h
|
simdutf8check.h
|
||||||
spectro_source.hh
|
spectro_source.hh
|
||||||
|
sql_util.hh
|
||||||
strong_int.hh
|
strong_int.hh
|
||||||
string_attr_type.hh
|
string_attr_type.hh
|
||||||
sysclip.hh
|
sysclip.hh
|
||||||
|
@ -248,9 +248,9 @@ noinst_HEADERS = \
|
|||||||
shlex.hh \
|
shlex.hh \
|
||||||
simdutf8check.h \
|
simdutf8check.h \
|
||||||
spectro_source.hh \
|
spectro_source.hh \
|
||||||
styling.hh \
|
|
||||||
sql_util.hh \
|
sql_util.hh \
|
||||||
sqlite-extension-func.hh \
|
sqlite-extension-func.hh \
|
||||||
|
styling.hh \
|
||||||
statusview_curses.hh \
|
statusview_curses.hh \
|
||||||
string_attr_type.hh \
|
string_attr_type.hh \
|
||||||
strnatcmp.h \
|
strnatcmp.h \
|
||||||
@ -293,6 +293,7 @@ nodist_libdiag_a_SOURCES = \
|
|||||||
$(LNAV_BUILT_FILES)
|
$(LNAV_BUILT_FILES)
|
||||||
|
|
||||||
THIRD_PARTY_SRCS = \
|
THIRD_PARTY_SRCS = \
|
||||||
|
third-party/sqlite/ext/dbdump.c \
|
||||||
third-party/sqlite/ext/series.c
|
third-party/sqlite/ext/series.c
|
||||||
|
|
||||||
libdiag_a_SOURCES = \
|
libdiag_a_SOURCES = \
|
||||||
@ -372,6 +373,7 @@ libdiag_a_SOURCES = \
|
|||||||
textfile_sub_source.cc \
|
textfile_sub_source.cc \
|
||||||
timer.cc \
|
timer.cc \
|
||||||
piper_proc.cc \
|
piper_proc.cc \
|
||||||
|
sql_commands.cc \
|
||||||
sql_util.cc \
|
sql_util.cc \
|
||||||
state-extension-functions.cc \
|
state-extension-functions.cc \
|
||||||
strnatcmp.c \
|
strnatcmp.c \
|
||||||
|
@ -20,6 +20,7 @@ noinst_HEADERS = \
|
|||||||
func_util.hh \
|
func_util.hh \
|
||||||
future_util.hh \
|
future_util.hh \
|
||||||
humanize.hh \
|
humanize.hh \
|
||||||
|
humanize.time.hh \
|
||||||
injector.hh \
|
injector.hh \
|
||||||
injector.bind.hh \
|
injector.bind.hh \
|
||||||
intern_string.hh \
|
intern_string.hh \
|
||||||
@ -37,6 +38,7 @@ noinst_HEADERS = \
|
|||||||
libbase_a_SOURCES = \
|
libbase_a_SOURCES = \
|
||||||
date_time_scanner.cc \
|
date_time_scanner.cc \
|
||||||
humanize.cc \
|
humanize.cc \
|
||||||
|
humanize.time.cc \
|
||||||
intern_string.cc \
|
intern_string.cc \
|
||||||
is_utf8.cc \
|
is_utf8.cc \
|
||||||
isc.cc \
|
isc.cc \
|
||||||
|
121
src/base/humanize.time.cc
Normal file
121
src/base/humanize.time.cc
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021, 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 "time_util.hh"
|
||||||
|
#include "humanize.time.hh"
|
||||||
|
|
||||||
|
namespace humanize {
|
||||||
|
namespace time {
|
||||||
|
|
||||||
|
std::string time_ago(time_t last_time, bool convert_local)
|
||||||
|
{
|
||||||
|
time_t delta, current_time = ::time(nullptr);
|
||||||
|
const char *fmt;
|
||||||
|
char buffer[64];
|
||||||
|
int amount;
|
||||||
|
|
||||||
|
if (convert_local) {
|
||||||
|
current_time = convert_log_time_to_local(current_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = current_time - last_time;
|
||||||
|
if (delta < 0) {
|
||||||
|
return "in the future";
|
||||||
|
} else if (delta < 60) {
|
||||||
|
return "just now";
|
||||||
|
} else if (delta < (60 * 2)) {
|
||||||
|
return "one minute ago";
|
||||||
|
} else if (delta < (60 * 60)) {
|
||||||
|
fmt = "%d minutes ago";
|
||||||
|
amount = delta / 60;
|
||||||
|
} else if (delta < (2 * 60 * 60)) {
|
||||||
|
return "one hour ago";
|
||||||
|
} else if (delta < (24 * 60 * 60)) {
|
||||||
|
fmt = "%d hours ago";
|
||||||
|
amount = delta / (60 * 60);
|
||||||
|
} else if (delta < (2 * 24 * 60 * 60)) {
|
||||||
|
return "one day ago";
|
||||||
|
} else if (delta < (365 * 24 * 60 * 60)) {
|
||||||
|
fmt = "%d days ago";
|
||||||
|
amount = delta / (24 * 60 * 60);
|
||||||
|
} else if (delta < (2 * 365 * 24 * 60 * 60)) {
|
||||||
|
return "over a year ago";
|
||||||
|
} else {
|
||||||
|
fmt = "over %d years ago";
|
||||||
|
amount = delta / (365 * 24 * 60 * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), fmt, amount);
|
||||||
|
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string precise_time_ago(const struct timeval &tv, bool convert_local)
|
||||||
|
{
|
||||||
|
struct timeval now, diff;
|
||||||
|
|
||||||
|
gettimeofday(&now, nullptr);
|
||||||
|
if (convert_local) {
|
||||||
|
now.tv_sec = convert_log_time_to_local(now.tv_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
timersub(&now, &tv, &diff);
|
||||||
|
if (diff.tv_sec < 0) {
|
||||||
|
return time_ago(tv.tv_sec);
|
||||||
|
} else if (diff.tv_sec <= 1) {
|
||||||
|
return "a second ago";
|
||||||
|
} else if (diff.tv_sec < (10 * 60)) {
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
if (diff.tv_sec < 60) {
|
||||||
|
snprintf(buf, sizeof(buf),
|
||||||
|
"%2ld seconds ago",
|
||||||
|
diff.tv_sec);
|
||||||
|
} else {
|
||||||
|
time_t seconds = diff.tv_sec % 60;
|
||||||
|
time_t minutes = diff.tv_sec / 60;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf),
|
||||||
|
"%2ld minute%s and %2ld second%s ago",
|
||||||
|
minutes,
|
||||||
|
minutes > 1 ? "s" : "",
|
||||||
|
seconds,
|
||||||
|
seconds == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(buf);
|
||||||
|
} else {
|
||||||
|
return time_ago(tv.tv_sec, convert_local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
17
src/base/humanize.time.hh
Normal file
17
src/base/humanize.time.hh
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#ifndef lnav_humanize_time_hh
|
||||||
|
#define lnav_humanize_time_hh
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace humanize {
|
||||||
|
namespace time {
|
||||||
|
|
||||||
|
std::string time_ago(time_t last_time, bool convert_local = false);
|
||||||
|
|
||||||
|
std::string precise_time_ago(const struct timeval &tv, bool convert_local = false);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -118,9 +118,6 @@ struct bind_multiple : multiple_storage<T> {
|
|||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
|
|
||||||
namespace injector {
|
namespace injector {
|
||||||
|
|
||||||
|
template<typename Annotation>
|
||||||
|
void force_linking(Annotation anno);
|
||||||
|
|
||||||
template<class ...>
|
template<class ...>
|
||||||
using void_t = void;
|
using void_t = void;
|
||||||
|
|
||||||
@ -56,11 +59,15 @@ struct has_injectable<T, void_t<typename T::injectable>> : std::true_type
|
|||||||
template<typename T, typename...Annotations>
|
template<typename T, typename...Annotations>
|
||||||
struct singleton_storage {
|
struct singleton_storage {
|
||||||
static T *get() {
|
static T *get() {
|
||||||
|
static int _[] = {0, (force_linking(Annotations{}), 0)...};
|
||||||
|
(void)_;
|
||||||
assert(ss_data != nullptr);
|
assert(ss_data != nullptr);
|
||||||
return ss_data;
|
return ss_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::shared_ptr<T> create() {
|
static std::shared_ptr<T> create() {
|
||||||
|
static int _[] = {0, (force_linking(Annotations{}), 0)...};
|
||||||
|
(void)_;
|
||||||
return ss_factory();
|
return ss_factory();
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
|
@ -45,6 +45,18 @@ struct tm *secs2tm(time_t *tim_p, struct tm *res);
|
|||||||
time_t tm2sec(const struct tm *t);
|
time_t tm2sec(const struct tm *t);
|
||||||
void secs2wday(const struct timeval &tv, struct tm *res);
|
void secs2wday(const struct timeval &tv, struct tm *res);
|
||||||
|
|
||||||
|
inline
|
||||||
|
time_t convert_log_time_to_local(time_t value) {
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
localtime_r(&value, &tm);
|
||||||
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||||
|
tm.tm_zone = NULL;
|
||||||
|
#endif
|
||||||
|
tm.tm_isdst = 0;
|
||||||
|
return tm2sec(&tm);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr time_t MAX_TIME_T = 4000000000LL;
|
constexpr time_t MAX_TIME_T = 4000000000LL;
|
||||||
|
|
||||||
enum exttm_bits_t {
|
enum exttm_bits_t {
|
||||||
|
@ -34,4 +34,6 @@
|
|||||||
|
|
||||||
struct last_relative_time_tag {};
|
struct last_relative_time_tag {};
|
||||||
|
|
||||||
|
struct sql_cmd_map_tag {};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "sql_util.hh"
|
#include "sql_util.hh"
|
||||||
#include "lnav_config.hh"
|
#include "lnav_config.hh"
|
||||||
#include "service_tags.hh"
|
#include "service_tags.hh"
|
||||||
|
#include "bound_tags.hh"
|
||||||
|
|
||||||
#include "command_executor.hh"
|
#include "command_executor.hh"
|
||||||
#include "db_sub_source.hh"
|
#include "db_sub_source.hh"
|
||||||
@ -138,15 +139,20 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
|
|||||||
|
|
||||||
lnav_data.ld_bottom_source.grep_error("");
|
lnav_data.ld_bottom_source.grep_error("");
|
||||||
|
|
||||||
if (stmt_str == ".schema") {
|
if (startswith(stmt_str, ".")) {
|
||||||
alt_msg = "";
|
vector<string> args;
|
||||||
|
split_ws(stmt_str, args);
|
||||||
|
|
||||||
ensure_view(&lnav_data.ld_views[LNV_SCHEMA]);
|
auto sql_cmd_map = injector::get<
|
||||||
|
readline_context::command_map_t *, sql_cmd_map_tag>();
|
||||||
|
auto cmd_iter = sql_cmd_map->find(args[0]);
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_PAGING;
|
if (cmd_iter != sql_cmd_map->end()) {
|
||||||
return Ok(string());
|
return cmd_iter->second->c_func(ec, stmt_str, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (stmt_str == ".msgformats") {
|
|
||||||
|
if (stmt_str == ".msgformats") {
|
||||||
stmt_str = MSG_FORMAT_STMT;
|
stmt_str = MSG_FORMAT_STMT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "base/humanize.time.hh"
|
||||||
#include "lnav_util.hh"
|
#include "lnav_util.hh"
|
||||||
#include "ansi_scrubber.hh"
|
#include "ansi_scrubber.hh"
|
||||||
#include "vtab_module.hh"
|
#include "vtab_module.hh"
|
||||||
@ -75,7 +76,8 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
|
|||||||
|
|
||||||
first_line = lss.find_line(lss.at(vis_line_t(0)));
|
first_line = lss.find_line(lss.at(vis_line_t(0)));
|
||||||
last_line = lss.find_line(lss.at(lv.get_bottom()));
|
last_line = lss.find_line(lss.at(lv.get_bottom()));
|
||||||
last_time = "Last message: " ANSI_BOLD_START + precise_time_ago(
|
last_time = "Last message: " ANSI_BOLD_START +
|
||||||
|
humanize::time::precise_time_ago(
|
||||||
last_line->get_timeval(), true) + ANSI_NORM;
|
last_line->get_timeval(), true) + ANSI_NORM;
|
||||||
duration2str(last_line->get_time_in_millis() -
|
duration2str(last_line->get_time_in_millis() -
|
||||||
first_line->get_time_in_millis(),
|
first_line->get_time_in_millis(),
|
||||||
@ -283,7 +285,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
|
|||||||
time_line.with_attr(string_attr(time_lr, &view_curses::VC_STYLE, A_BOLD));
|
time_line.with_attr(string_attr(time_lr, &view_curses::VC_STYLE, A_BOLD));
|
||||||
time_str.append(" -- ");
|
time_str.append(" -- ");
|
||||||
time_lr.lr_start = time_str.length();
|
time_lr.lr_start = time_str.length();
|
||||||
time_str.append(precise_time_ago(ll->get_timeval(), true));
|
time_str.append(humanize::time::precise_time_ago(ll->get_timeval(), true));
|
||||||
time_lr.lr_end = time_str.length();
|
time_lr.lr_end = time_str.length();
|
||||||
time_line.with_attr(string_attr(time_lr, &view_curses::VC_STYLE, A_BOLD));
|
time_line.with_attr(string_attr(time_lr, &view_curses::VC_STYLE, A_BOLD));
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ enum class help_context_t {
|
|||||||
HC_PARAMETER,
|
HC_PARAMETER,
|
||||||
HC_RESULT,
|
HC_RESULT,
|
||||||
HC_COMMAND,
|
HC_COMMAND,
|
||||||
|
HC_SQL_COMMAND,
|
||||||
HC_SQL_KEYWORD,
|
HC_SQL_KEYWORD,
|
||||||
HC_SQL_INFIX,
|
HC_SQL_INFIX,
|
||||||
HC_SQL_FUNCTION,
|
HC_SQL_FUNCTION,
|
||||||
@ -122,6 +123,11 @@ struct help_text {
|
|||||||
return *this;
|
return *this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
help_text &sql_command() noexcept {
|
||||||
|
this->ht_context = help_context_t::HC_SQL_COMMAND;
|
||||||
|
return *this;
|
||||||
|
};
|
||||||
|
|
||||||
help_text &sql_keyword() noexcept {
|
help_text &sql_keyword() noexcept {
|
||||||
this->ht_context = help_context_t::HC_SQL_KEYWORD;
|
this->ht_context = help_context_t::HC_SQL_KEYWORD;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -174,6 +174,38 @@ void format_help_text_for_term(const help_text &ht, size_t width, attr_line_t &o
|
|||||||
.append("\n");
|
.append("\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case help_context_t::HC_SQL_COMMAND: {
|
||||||
|
out.append("Synopsis", &view_curses::VC_STYLE, A_UNDERLINE)
|
||||||
|
.append("\n")
|
||||||
|
.append(body_indent, ' ')
|
||||||
|
.append(";")
|
||||||
|
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
|
||||||
|
for (auto ¶m : ht.ht_parameters) {
|
||||||
|
out.append(" ");
|
||||||
|
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
|
||||||
|
out.append("[");
|
||||||
|
}
|
||||||
|
out.append(param.ht_name, &view_curses::VC_STYLE, A_UNDERLINE);
|
||||||
|
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
|
||||||
|
out.append("]");
|
||||||
|
}
|
||||||
|
if (param.ht_nargs == help_nargs_t::HN_ONE_OR_MORE) {
|
||||||
|
out.append("1", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||||
|
out.append(" [");
|
||||||
|
out.append("...", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||||
|
out.append(" ");
|
||||||
|
out.append(param.ht_name, &view_curses::VC_STYLE,
|
||||||
|
A_UNDERLINE);
|
||||||
|
out.append("N", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||||
|
out.append("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.append(" - ")
|
||||||
|
.append(attr_line_t::from_ansi_str(ht.ht_summary),
|
||||||
|
&tws.with_indent(body_indent + 2))
|
||||||
|
.append("\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case help_context_t::HC_SQL_INFIX:
|
case help_context_t::HC_SQL_INFIX:
|
||||||
case help_context_t::HC_SQL_KEYWORD: {
|
case help_context_t::HC_SQL_KEYWORD: {
|
||||||
size_t line_start = body_indent;
|
size_t line_start = body_indent;
|
||||||
@ -501,6 +533,9 @@ void format_help_text_for_rst(const help_text &ht,
|
|||||||
case help_context_t::HC_COMMAND:
|
case help_context_t::HC_COMMAND:
|
||||||
prefix = ":";
|
prefix = ":";
|
||||||
break;
|
break;
|
||||||
|
case help_context_t::HC_SQL_COMMAND:
|
||||||
|
prefix = ";";
|
||||||
|
break;
|
||||||
case help_context_t::HC_SQL_FUNCTION:
|
case help_context_t::HC_SQL_FUNCTION:
|
||||||
case help_context_t::HC_SQL_TABLE_VALUED_FUNCTION:
|
case help_context_t::HC_SQL_TABLE_VALUED_FUNCTION:
|
||||||
is_sql = is_sql_func = true;
|
is_sql = is_sql_func = true;
|
||||||
|
27
src/lnav.cc
27
src/lnav.cc
@ -80,6 +80,7 @@
|
|||||||
#include "init-sql.h"
|
#include "init-sql.h"
|
||||||
#include "logfile.hh"
|
#include "logfile.hh"
|
||||||
#include "base/func_util.hh"
|
#include "base/func_util.hh"
|
||||||
|
#include "base/humanize.time.hh"
|
||||||
#include "base/injector.bind.hh"
|
#include "base/injector.bind.hh"
|
||||||
#include "base/isc.hh"
|
#include "base/isc.hh"
|
||||||
#include "base/string_util.hh"
|
#include "base/string_util.hh"
|
||||||
@ -252,6 +253,16 @@ static auto bound_curl =
|
|||||||
injector::bind_multiple<isc::service>()
|
injector::bind_multiple<isc::service>()
|
||||||
.add_singleton<curl_looper, services::curl_streamer_t>();
|
.add_singleton<curl_looper, services::curl_streamer_t>();
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void injector::force_linking(last_relative_time_tag anno)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void injector::force_linking(services::curl_streamer_t anno)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool setup_logline_table(exec_context &ec)
|
bool setup_logline_table(exec_context &ec)
|
||||||
{
|
{
|
||||||
// Hidden columns don't show up in the table_info pragma.
|
// Hidden columns don't show up in the table_info pragma.
|
||||||
@ -264,13 +275,6 @@ bool setup_logline_table(exec_context &ec)
|
|||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *commands[] = {
|
|
||||||
".schema",
|
|
||||||
".msgformats",
|
|
||||||
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
|
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
bool update_possibilities = (
|
bool update_possibilities = (
|
||||||
@ -326,7 +330,6 @@ bool setup_logline_table(exec_context &ec)
|
|||||||
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_function_names);
|
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_function_names);
|
||||||
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*",
|
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*",
|
||||||
hidden_table_columns);
|
hidden_table_columns);
|
||||||
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", commands);
|
|
||||||
|
|
||||||
for (int lpc = 0; sqlite_registration_funcs[lpc]; lpc++) {
|
for (int lpc = 0; sqlite_registration_funcs[lpc]; lpc++) {
|
||||||
struct FuncDef *basic_funcs;
|
struct FuncDef *basic_funcs;
|
||||||
@ -1133,7 +1136,9 @@ static void wait_for_pipers()
|
|||||||
static void looper()
|
static void looper()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
exec_context &ec = lnav_data.ld_exec_context;
|
auto sql_cmd_map = injector::get<
|
||||||
|
readline_context::command_map_t*, sql_cmd_map_tag>();
|
||||||
|
auto& ec = lnav_data.ld_exec_context;
|
||||||
|
|
||||||
readline_context command_context("cmd", &lnav_commands);
|
readline_context command_context("cmd", &lnav_commands);
|
||||||
|
|
||||||
@ -1141,7 +1146,7 @@ static void looper()
|
|||||||
readline_context search_filters_context("search-filters", nullptr, false);
|
readline_context search_filters_context("search-filters", nullptr, false);
|
||||||
readline_context search_files_context("search-files", nullptr, false);
|
readline_context search_files_context("search-files", nullptr, false);
|
||||||
readline_context index_context("capture");
|
readline_context index_context("capture");
|
||||||
readline_context sql_context("sql", nullptr, false);
|
readline_context sql_context("sql", sql_cmd_map, false);
|
||||||
readline_context exec_context("exec");
|
readline_context exec_context("exec");
|
||||||
readline_context user_context("user");
|
readline_context user_context("user");
|
||||||
readline_curses rlc;
|
readline_curses rlc;
|
||||||
@ -1430,7 +1435,7 @@ static void looper()
|
|||||||
if (session_data.sd_save_time) {
|
if (session_data.sd_save_time) {
|
||||||
std::string ago;
|
std::string ago;
|
||||||
|
|
||||||
ago = time_ago(session_data.sd_save_time);
|
ago = humanize::time::time_ago(session_data.sd_save_time);
|
||||||
lnav_data.ld_rl_view->set_value(
|
lnav_data.ld_rl_view->set_value(
|
||||||
("restored session from " ANSI_BOLD_START) +
|
("restored session from " ANSI_BOLD_START) +
|
||||||
ago +
|
ago +
|
||||||
|
@ -979,11 +979,15 @@ Result<config_file_type, std::string>
|
|||||||
detect_config_file_type(const ghc::filesystem::path &path)
|
detect_config_file_type(const ghc::filesystem::path &path)
|
||||||
{
|
{
|
||||||
static const char *id_path[] = {"$schema", nullptr};
|
static const char *id_path[] = {"$schema", nullptr};
|
||||||
string content;
|
|
||||||
|
|
||||||
if (!read_file(path.string(), content)) {
|
auto read_res = read_file(path);
|
||||||
return Err(fmt::format("unable to open file: {}", path.string()));
|
|
||||||
|
if (read_res.isErr()) {
|
||||||
|
return Err(fmt::format("unable to open file: {} -- {}",
|
||||||
|
path.string(), read_res.unwrapErr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto content = read_res.unwrap();
|
||||||
if (startswith(content, "#")) {
|
if (startswith(content, "#")) {
|
||||||
content.insert(0, "//");
|
content.insert(0, "//");
|
||||||
}
|
}
|
||||||
|
116
src/lnav_util.cc
116
src/lnav_util.cc
@ -45,101 +45,6 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
std::string time_ago(time_t last_time, bool convert_local)
|
|
||||||
{
|
|
||||||
time_t delta, current_time = time(nullptr);
|
|
||||||
const char *fmt;
|
|
||||||
char buffer[64];
|
|
||||||
int amount;
|
|
||||||
|
|
||||||
if (convert_local) {
|
|
||||||
current_time = convert_log_time_to_local(current_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
delta = current_time - last_time;
|
|
||||||
if (delta < 0) {
|
|
||||||
return "in the future";
|
|
||||||
}
|
|
||||||
else if (delta < 60) {
|
|
||||||
return "just now";
|
|
||||||
}
|
|
||||||
else if (delta < (60 * 2)) {
|
|
||||||
return "one minute ago";
|
|
||||||
}
|
|
||||||
else if (delta < (60 * 60)) {
|
|
||||||
fmt = "%d minutes ago";
|
|
||||||
amount = delta / 60;
|
|
||||||
}
|
|
||||||
else if (delta < (2 * 60 * 60)) {
|
|
||||||
return "one hour ago";
|
|
||||||
}
|
|
||||||
else if (delta < (24 * 60 * 60)) {
|
|
||||||
fmt = "%d hours ago";
|
|
||||||
amount = delta / (60 * 60);
|
|
||||||
}
|
|
||||||
else if (delta < (2 * 24 * 60 * 60)) {
|
|
||||||
return "one day ago";
|
|
||||||
}
|
|
||||||
else if (delta < (365 * 24 * 60 * 60)) {
|
|
||||||
fmt = "%d days ago";
|
|
||||||
amount = delta / (24 * 60 * 60);
|
|
||||||
}
|
|
||||||
else if (delta < (2 * 365 * 24 * 60 * 60)) {
|
|
||||||
return "over a year ago";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fmt = "over %d years ago";
|
|
||||||
amount = delta / (365 * 24 * 60 * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buffer, sizeof(buffer), fmt, amount);
|
|
||||||
|
|
||||||
return std::string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string precise_time_ago(const struct timeval &tv, bool convert_local)
|
|
||||||
{
|
|
||||||
struct timeval now, diff;
|
|
||||||
|
|
||||||
gettimeofday(&now, nullptr);
|
|
||||||
if (convert_local) {
|
|
||||||
now.tv_sec = convert_log_time_to_local(now.tv_sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
timersub(&now, &tv, &diff);
|
|
||||||
if (diff.tv_sec < 0) {
|
|
||||||
return time_ago(tv.tv_sec);
|
|
||||||
}
|
|
||||||
else if (diff.tv_sec <= 1) {
|
|
||||||
return "a second ago";
|
|
||||||
}
|
|
||||||
else if (diff.tv_sec < (10 * 60)) {
|
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
if (diff.tv_sec < 60) {
|
|
||||||
snprintf(buf, sizeof(buf),
|
|
||||||
"%2ld seconds ago",
|
|
||||||
diff.tv_sec);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
time_t seconds = diff.tv_sec % 60;
|
|
||||||
time_t minutes = diff.tv_sec / 60;
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf),
|
|
||||||
"%2ld minute%s and %2ld second%s ago",
|
|
||||||
minutes,
|
|
||||||
minutes > 1 ? "s" : "",
|
|
||||||
seconds,
|
|
||||||
seconds == 1 ? "" : "s");
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(buf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return time_ago(tv.tv_sec, convert_local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool change_to_parent_dir()
|
bool change_to_parent_dir()
|
||||||
{
|
{
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
@ -211,17 +116,22 @@ string build_path(const vector<ghc::filesystem::path> &paths)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_file(const ghc::filesystem::path &filename, string &out)
|
Result<std::string, std::string> read_file(const ghc::filesystem::path &path)
|
||||||
{
|
{
|
||||||
std::ifstream sql_file(filename.string());
|
try {
|
||||||
|
ghc::filesystem::ifstream file_stream(path);
|
||||||
|
|
||||||
if (sql_file) {
|
if (!file_stream) {
|
||||||
out.assign((std::istreambuf_iterator<char>(sql_file)),
|
return Err(std::string(strerror(errno)));
|
||||||
std::istreambuf_iterator<char>());
|
}
|
||||||
return true;
|
|
||||||
|
std::string retval;
|
||||||
|
retval.assign((std::istreambuf_iterator<char>(file_stream)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
return Ok(retval);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return Err(std::string(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<std::pair<ghc::filesystem::path, int>, std::string>
|
Result<std::pair<ghc::filesystem::path, int>, std::string>
|
||||||
|
@ -57,10 +57,6 @@
|
|||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "ghc/filesystem.hpp"
|
#include "ghc/filesystem.hpp"
|
||||||
|
|
||||||
std::string time_ago(time_t last_time, bool convert_local = false);
|
|
||||||
|
|
||||||
std::string precise_time_ago(const struct timeval &tv, bool convert_local = false);
|
|
||||||
|
|
||||||
#if SIZEOF_OFF_T == 8
|
#if SIZEOF_OFF_T == 8
|
||||||
#define FORMAT_OFF_T "%qd"
|
#define FORMAT_OFF_T "%qd"
|
||||||
#elif SIZEOF_OFF_T == 4
|
#elif SIZEOF_OFF_T == 4
|
||||||
@ -111,28 +107,6 @@ private:
|
|||||||
SpookyHash h_context;
|
SpookyHash h_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename UnaryFunction, typename Member>
|
|
||||||
struct object_field_t {
|
|
||||||
object_field_t(UnaryFunction &func, Member &mem)
|
|
||||||
: of_func(func), of_mem(mem) {};
|
|
||||||
|
|
||||||
template<typename Object>
|
|
||||||
void operator()(Object obj)
|
|
||||||
{
|
|
||||||
this->of_func(obj.*(this->of_mem));
|
|
||||||
};
|
|
||||||
|
|
||||||
UnaryFunction &of_func;
|
|
||||||
Member of_mem;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename UnaryFunction, typename Member>
|
|
||||||
object_field_t<UnaryFunction, Member> object_field(UnaryFunction &func,
|
|
||||||
Member mem)
|
|
||||||
{
|
|
||||||
return object_field_t<UnaryFunction, Member>(func, mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool change_to_parent_dir();
|
bool change_to_parent_dir();
|
||||||
|
|
||||||
bool next_format(const char * const fmt[], int &index, int &locked_index);
|
bool next_format(const char * const fmt[], int &index, int &locked_index);
|
||||||
@ -151,19 +125,7 @@ inline bool is_glob(const char *fn)
|
|||||||
|
|
||||||
std::string build_path(const std::vector<ghc::filesystem::path> &paths);
|
std::string build_path(const std::vector<ghc::filesystem::path> &paths);
|
||||||
|
|
||||||
bool read_file(const ghc::filesystem::path &path, std::string &out);
|
Result<std::string, std::string> read_file(const ghc::filesystem::path &path);
|
||||||
|
|
||||||
inline
|
|
||||||
time_t convert_log_time_to_local(time_t value) {
|
|
||||||
struct tm tm;
|
|
||||||
|
|
||||||
localtime_r(&value, &tm);
|
|
||||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
|
||||||
tm.tm_zone = NULL;
|
|
||||||
#endif
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
return tm2sec(&tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
size_t strtonum(T &num_out, const char *data, size_t len);
|
size_t strtonum(T &num_out, const char *data, size_t len);
|
||||||
|
@ -1058,19 +1058,19 @@ static void exec_sql_in_path(sqlite3 *db, const ghc::filesystem::path &path, std
|
|||||||
log_info("executing SQL files in path: %s", format_path.c_str());
|
log_info("executing SQL files in path: %s", format_path.c_str());
|
||||||
if (glob(format_path.c_str(), 0, nullptr, gl.inout()) == 0) {
|
if (glob(format_path.c_str(), 0, nullptr, gl.inout()) == 0) {
|
||||||
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
|
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
|
||||||
string filename(gl->gl_pathv[lpc]);
|
auto filename = ghc::filesystem::path(gl->gl_pathv[lpc]);
|
||||||
string content;
|
auto read_res = read_file(filename);
|
||||||
|
|
||||||
if (read_file(filename, content)) {
|
if (read_res.isOk()) {
|
||||||
log_info("Executing SQL file: %s", filename.c_str());
|
log_info("Executing SQL file: %s", filename.c_str());
|
||||||
|
auto content = read_res.unwrap();
|
||||||
|
|
||||||
sql_execute_script(db, filename.c_str(), content.c_str(), errors);
|
sql_execute_script(db, filename.c_str(), content.c_str(), errors);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
errors.push_back(
|
errors.push_back(fmt::format(
|
||||||
"error:unable to read file '" +
|
"error:unable to read file '{}' -- {}",
|
||||||
filename +
|
filename.string(), read_res.unwrapErr()));
|
||||||
"' -- " +
|
|
||||||
string(strerror(errno)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,10 +183,8 @@ bool rl_sql_help(readline_curses *rc)
|
|||||||
lnav_data.ld_doc_source.replace_with(doc_al);
|
lnav_data.ld_doc_source.replace_with(doc_al);
|
||||||
dtc.reload_data();
|
dtc.reload_data();
|
||||||
|
|
||||||
if (!ex_al.empty()) {
|
lnav_data.ld_example_source.replace_with(ex_al);
|
||||||
lnav_data.ld_example_source.replace_with(ex_al);
|
etc.reload_data();
|
||||||
etc.reload_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
has_doc = true;
|
has_doc = true;
|
||||||
}
|
}
|
||||||
|
@ -502,6 +502,8 @@ readline_curses::readline_curses()
|
|||||||
|
|
||||||
readline_curses::~readline_curses()
|
readline_curses::~readline_curses()
|
||||||
{
|
{
|
||||||
|
this->rc_pty[RCF_MASTER].reset();
|
||||||
|
this->rc_command_pipe[RCF_MASTER].reset();
|
||||||
if (this->rc_child == 0) {
|
if (this->rc_child == 0) {
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
@ -509,7 +511,7 @@ readline_curses::~readline_curses()
|
|||||||
int status;
|
int status;
|
||||||
|
|
||||||
log_debug("term child %d", this->rc_child);
|
log_debug("term child %d", this->rc_child);
|
||||||
kill(this->rc_child, SIGTERM);
|
log_perror(kill(this->rc_child, SIGTERM));
|
||||||
this->rc_child = -1;
|
this->rc_child = -1;
|
||||||
|
|
||||||
while (wait(&status) < 0 && (errno == EINTR)) {
|
while (wait(&status) < 0 && (errno == EINTR)) {
|
||||||
|
@ -86,7 +86,7 @@ public:
|
|||||||
|
|
||||||
_command_t(const char *name,
|
_command_t(const char *name,
|
||||||
command_func_t func,
|
command_func_t func,
|
||||||
help_text help,
|
help_text help = {},
|
||||||
prompt_func_t prompt = nullptr) noexcept
|
prompt_func_t prompt = nullptr) noexcept
|
||||||
: c_name(name), c_func(func), c_help(std::move(help)), c_prompt(prompt) {};
|
: c_name(name), c_func(func), c_help(std::move(help)), c_prompt(prompt) {};
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ void readline_command_highlighter(attr_line_t &al, int x)
|
|||||||
|
|
||||||
static void readline_sqlite_highlighter_int(attr_line_t &al, int x, int skip)
|
static void readline_sqlite_highlighter_int(attr_line_t &al, int x, int skip)
|
||||||
{
|
{
|
||||||
static string keyword_re_str = sql_keyword_re() + "|\\.schema|\\.msgformats";
|
static string keyword_re_str = sql_keyword_re();
|
||||||
static pcrepp keyword_pcre(keyword_re_str.c_str(), PCRE_CASELESS);
|
static pcrepp keyword_pcre(keyword_re_str.c_str(), PCRE_CASELESS);
|
||||||
static pcrepp string_literal_pcre("'[^']*('(?:'[^']*')*|$)");
|
static pcrepp string_literal_pcre("'[^']*('(?:'[^']*')*|$)");
|
||||||
static pcrepp ident_pcre("(?:\\$|:)?(\\b[a-z_]\\w*)|\"([^\"]+)\"|\\[([^\\]]+)]", PCRE_CASELESS);
|
static pcrepp ident_pcre("(?:\\$|:)?(\\b[a-z_]\\w*)|\"([^\"]+)\"|\\[([^\\]]+)]", PCRE_CASELESS);
|
||||||
@ -451,7 +451,7 @@ static void readline_sqlite_highlighter_int(attr_line_t &al, int x, int skip)
|
|||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
||||||
view_colors &vc = view_colors::singleton();
|
auto &vc = view_colors::singleton();
|
||||||
|
|
||||||
int keyword_attrs = vc.attrs_for_role(view_colors::VCR_KEYWORD);
|
int keyword_attrs = vc.attrs_for_role(view_colors::VCR_KEYWORD);
|
||||||
int symbol_attrs = vc.attrs_for_role(view_colors::VCR_SYMBOL);
|
int symbol_attrs = vc.attrs_for_role(view_colors::VCR_SYMBOL);
|
||||||
@ -460,7 +460,18 @@ static void readline_sqlite_highlighter_int(attr_line_t &al, int x, int skip)
|
|||||||
|
|
||||||
pcre_context_static<30> pc;
|
pcre_context_static<30> pc;
|
||||||
pcre_input pi(al.get_string(), skip);
|
pcre_input pi(al.get_string(), skip);
|
||||||
string &line = al.get_string();
|
auto &line = al.get_string();
|
||||||
|
|
||||||
|
if (startswith(line, ";.")) {
|
||||||
|
auto space = line.find(' ');
|
||||||
|
struct line_range lr{2, -1};
|
||||||
|
|
||||||
|
if (space != std::string::npos) {
|
||||||
|
lr.lr_end = space;
|
||||||
|
}
|
||||||
|
al.get_attrs().emplace_back(lr, &view_curses::VC_STYLE, keyword_attrs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (ident_pcre.match(pc, pi)) {
|
while (ident_pcre.match(pc, pi)) {
|
||||||
pcre_context::capture_t *cap = pc.first_valid();
|
pcre_context::capture_t *cap = pc.first_valid();
|
||||||
|
257
src/sql_commands.cc
Normal file
257
src/sql_commands.cc
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021, 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 "base/lnav_log.hh"
|
||||||
|
#include "lnav.hh"
|
||||||
|
#include "lnav_util.hh"
|
||||||
|
#include "bound_tags.hh"
|
||||||
|
#include "base/injector.bind.hh"
|
||||||
|
#include "readline_curses.hh"
|
||||||
|
#include "sqlite-extension-func.hh"
|
||||||
|
|
||||||
|
static
|
||||||
|
Result<std::string, std::string> sql_cmd_dump(
|
||||||
|
exec_context &ec, std::string cmdline, std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
|
||||||
|
if (args.empty()) {
|
||||||
|
args.emplace_back("filename");
|
||||||
|
args.emplace_back("tables");
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size() < 2) {
|
||||||
|
return ec.make_error("expecting a file name to write to");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_mem<FILE> file(fclose);
|
||||||
|
|
||||||
|
if ((file = fopen(args[1].c_str(), "w+")) == nullptr) {
|
||||||
|
return ec.make_error("unable to open '{}' for writing: {}",
|
||||||
|
args[1], strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t lpc = 2; lpc < args.size(); lpc++) {
|
||||||
|
sqlite3_db_dump(lnav_data.ld_db.in(),
|
||||||
|
"main",
|
||||||
|
args[lpc].c_str(),
|
||||||
|
(int (*)(const char *, void*)) fputs,
|
||||||
|
file.in());
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = "generated";
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Result<std::string, std::string> sql_cmd_read(
|
||||||
|
exec_context &ec, std::string cmdline, std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
|
||||||
|
if (args.empty()) {
|
||||||
|
args.emplace_back("filename");
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split_args;
|
||||||
|
shlex lexer(cmdline);
|
||||||
|
|
||||||
|
if (!lexer.split(split_args, ec.create_resolver())) {
|
||||||
|
return ec.make_error("unable to parse arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
|
||||||
|
auto read_res = read_file(split_args[lpc]);
|
||||||
|
|
||||||
|
if (read_res.isErr()) {
|
||||||
|
return ec.make_error("unable to read script file: {} -- {}",
|
||||||
|
split_args[lpc],
|
||||||
|
read_res.unwrapErr());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto script = read_res.unwrap();
|
||||||
|
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
||||||
|
const char *start = script.c_str();
|
||||||
|
|
||||||
|
do {
|
||||||
|
const char *tail;
|
||||||
|
auto rc = sqlite3_prepare_v2(lnav_data.ld_db.in(),
|
||||||
|
start,
|
||||||
|
-1,
|
||||||
|
stmt.out(),
|
||||||
|
&tail);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
||||||
|
|
||||||
|
return ec.make_error("{}", errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt.in() != nullptr) {
|
||||||
|
std::string alt_msg;
|
||||||
|
auto exec_res = execute_sql(ec,
|
||||||
|
std::string(start, tail - start),
|
||||||
|
alt_msg);
|
||||||
|
if (exec_res.isErr()) {
|
||||||
|
return exec_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = tail;
|
||||||
|
} while (start[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||||
|
if (ec.ec_local_vars.size() == 1) {
|
||||||
|
ensure_view(&lnav_data.ld_views[LNV_DB]);
|
||||||
|
}
|
||||||
|
} else if (lnav_data.ld_db_row_source.dls_rows.size() > 1) {
|
||||||
|
ensure_view(&lnav_data.ld_views[LNV_DB]);
|
||||||
|
}
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Result<std::string, std::string> sql_cmd_schema(
|
||||||
|
exec_context &ec, std::string cmdline, std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
|
||||||
|
if (args.empty()) {
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_view(&lnav_data.ld_views[LNV_SCHEMA]);
|
||||||
|
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Result<std::string, std::string> sql_cmd_generic(
|
||||||
|
exec_context &ec, std::string cmdline, std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
|
||||||
|
if (args.empty()) {
|
||||||
|
args.emplace_back("*");
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readline_context::command_t sql_commands[] = {
|
||||||
|
{
|
||||||
|
".dump",
|
||||||
|
sql_cmd_dump,
|
||||||
|
help_text(".dump",
|
||||||
|
"Dump the contents of the database")
|
||||||
|
.sql_command()
|
||||||
|
.with_parameter({"path", "The path to the file to write"})
|
||||||
|
.with_tags({"io",}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
".msgformats",
|
||||||
|
sql_cmd_schema,
|
||||||
|
help_text(".msgformats", "df")
|
||||||
|
.sql_command(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
".read",
|
||||||
|
sql_cmd_read,
|
||||||
|
help_text(".read",
|
||||||
|
"Switch to the SCHEMA view that contains a dump of the "
|
||||||
|
"current database schema")
|
||||||
|
.sql_command(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
".schema",
|
||||||
|
sql_cmd_schema,
|
||||||
|
help_text(".schema",
|
||||||
|
"Switch to the SCHEMA view that contains a dump of the "
|
||||||
|
"current database schema")
|
||||||
|
.sql_command(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ATTACH",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CREATE",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DELETE",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DETACH",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DROP",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"INSERT",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SELECT",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UPDATE",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"WITH",
|
||||||
|
sql_cmd_generic,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static readline_context::command_map_t sql_cmd_map;
|
||||||
|
|
||||||
|
static auto bound_sql_cmd_map = injector::bind<
|
||||||
|
readline_context::command_map_t, sql_cmd_map_tag>::to_instance(+[]() {
|
||||||
|
for (auto& cmd : sql_commands) {
|
||||||
|
sql_cmd_map[cmd.c_name] = &cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sql_cmd_map;
|
||||||
|
});
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void injector::force_linking(sql_cmd_map_tag anno)
|
||||||
|
{
|
||||||
|
}
|
@ -41,10 +41,13 @@
|
|||||||
|
|
||||||
#include "auto_mem.hh"
|
#include "auto_mem.hh"
|
||||||
#include "sql_util.hh"
|
#include "sql_util.hh"
|
||||||
|
#include "base/injector.hh"
|
||||||
#include "base/string_util.hh"
|
#include "base/string_util.hh"
|
||||||
#include "base/lnav_log.hh"
|
#include "base/lnav_log.hh"
|
||||||
#include "base/time_util.hh"
|
#include "base/time_util.hh"
|
||||||
#include "pcrepp/pcrepp.hh"
|
#include "pcrepp/pcrepp.hh"
|
||||||
|
#include "readline_curses.hh"
|
||||||
|
#include "bound_tags.hh"
|
||||||
#include "sqlite-extension-func.hh"
|
#include "sqlite-extension-func.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -886,6 +889,7 @@ string sql_keyword_re(void)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_attr_type SQL_COMMAND_ATTR("sql_command");
|
||||||
string_attr_type SQL_KEYWORD_ATTR("sql_keyword");
|
string_attr_type SQL_KEYWORD_ATTR("sql_keyword");
|
||||||
string_attr_type SQL_IDENTIFIER_ATTR("sql_ident");
|
string_attr_type SQL_IDENTIFIER_ATTR("sql_ident");
|
||||||
string_attr_type SQL_FUNCTION_ATTR("sql_func");
|
string_attr_type SQL_FUNCTION_ATTR("sql_func");
|
||||||
@ -897,16 +901,16 @@ string_attr_type SQL_GARBAGE_ATTR("sql_garbage");
|
|||||||
|
|
||||||
void annotate_sql_statement(attr_line_t &al)
|
void annotate_sql_statement(attr_line_t &al)
|
||||||
{
|
{
|
||||||
static string keyword_re_str =
|
static string keyword_re_str = R"(\A)" + sql_keyword_re();
|
||||||
R"(\A)" + sql_keyword_re() + R"(|\.schema|\.msgformats)";
|
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
pcrepp re;
|
pcrepp re;
|
||||||
string_attr_type_t type;
|
string_attr_type_t type;
|
||||||
} PATTERNS[] = {
|
} PATTERNS[] = {
|
||||||
|
{ pcrepp{R"(^(\.\w+))"}, &SQL_COMMAND_ATTR },
|
||||||
{ pcrepp{R"(\A,)"}, &SQL_COMMA_ATTR },
|
{ pcrepp{R"(\A,)"}, &SQL_COMMA_ATTR },
|
||||||
{ pcrepp{R"(\A\(|\A\))"}, &SQL_PAREN_ATTR },
|
{ pcrepp{R"(\A\(|\A\))"}, &SQL_PAREN_ATTR },
|
||||||
{ pcrepp{keyword_re_str.c_str(), PCRE_CASELESS}, &SQL_KEYWORD_ATTR },
|
{ pcrepp{keyword_re_str, PCRE_CASELESS}, &SQL_KEYWORD_ATTR },
|
||||||
{ pcrepp{R"(\A'[^']*('(?:'[^']*')*|$))"}, &SQL_STRING_ATTR },
|
{ pcrepp{R"(\A'[^']*('(?:'[^']*')*|$))"}, &SQL_STRING_ATTR },
|
||||||
{ pcrepp{R"(\A(\$?\b[a-z_]\w*)|\"([^\"]+)\"|\[([^\]]+)])", PCRE_CASELESS}, &SQL_IDENTIFIER_ATTR },
|
{ pcrepp{R"(\A(\$?\b[a-z_]\w*)|\"([^\"]+)\"|\[([^\]]+)])", PCRE_CASELESS}, &SQL_IDENTIFIER_ATTR },
|
||||||
{ pcrepp{R"(\A(\*|<|>|=|!|\-|\+|\|\|))"}, &SQL_OPERATOR_ATTR },
|
{ pcrepp{R"(\A(\*|<|>|=|!|\-|\+|\|\|))"}, &SQL_OPERATOR_ATTR },
|
||||||
@ -917,14 +921,14 @@ void annotate_sql_statement(attr_line_t &al)
|
|||||||
|
|
||||||
pcre_context_static<30> pc;
|
pcre_context_static<30> pc;
|
||||||
pcre_input pi(al.get_string());
|
pcre_input pi(al.get_string());
|
||||||
string &line = al.get_string();
|
auto &line = al.get_string();
|
||||||
string_attrs_t &sa = al.get_attrs();
|
auto &sa = al.get_attrs();
|
||||||
|
|
||||||
while (pi.pi_next_offset < line.length()) {
|
while (pi.pi_next_offset < line.length()) {
|
||||||
if (ws_pattern.match(pc, pi, PCRE_ANCHORED)) {
|
if (ws_pattern.match(pc, pi, PCRE_ANCHORED)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (auto &pat : PATTERNS) {
|
for (const auto &pat : PATTERNS) {
|
||||||
if (pat.re.match(pc, pi, PCRE_ANCHORED)) {
|
if (pat.re.match(pc, pi, PCRE_ANCHORED)) {
|
||||||
pcre_context::capture_t *cap = pc.all();
|
pcre_context::capture_t *cap = pc.all();
|
||||||
struct line_range lr(cap->c_begin, cap->c_end);
|
struct line_range lr(cap->c_begin, cap->c_end);
|
||||||
@ -990,8 +994,24 @@ vector<const help_text *> find_sql_help_for_line(const attr_line_t &al, size_t x
|
|||||||
|
|
||||||
x = al.nearest_text(x);
|
x = al.nearest_text(x);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sa_opt = get_string_attr(al.get_attrs(), &SQL_COMMAND_ATTR);
|
||||||
|
|
||||||
|
if (sa_opt) {
|
||||||
|
auto sql_cmd_map = injector::get<
|
||||||
|
readline_context::command_map_t*, sql_cmd_map_tag>();
|
||||||
|
auto cmd_name = al.get_substring((*sa_opt)->sa_range);
|
||||||
|
auto cmd_iter = sql_cmd_map->find(cmd_name);
|
||||||
|
|
||||||
|
if (cmd_iter != sql_cmd_map->end()) {
|
||||||
|
return {&cmd_iter->second->c_help};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vector<string> kw;
|
vector<string> kw;
|
||||||
auto iter = rfind_string_attr_if(sa, x, [&al, &name, &kw, x](auto sa) {
|
auto iter = rfind_string_attr_if(sa, x, [&al, &name, &kw, x](auto sa) {
|
||||||
|
|
||||||
if (sa.sa_type != &SQL_FUNCTION_ATTR &&
|
if (sa.sa_type != &SQL_FUNCTION_ATTR &&
|
||||||
sa.sa_type != &SQL_KEYWORD_ATTR) {
|
sa.sa_type != &SQL_KEYWORD_ATTR) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -108,6 +108,7 @@ int sqlite_authorizer(void* pUserData, int action_code, const char *detail1,
|
|||||||
const char *detail2, const char *detail3,
|
const char *detail2, const char *detail3,
|
||||||
const char *detail4);
|
const char *detail4);
|
||||||
|
|
||||||
|
extern string_attr_type SQL_COMMAND_ATTR;
|
||||||
extern string_attr_type SQL_KEYWORD_ATTR;
|
extern string_attr_type SQL_KEYWORD_ATTR;
|
||||||
extern string_attr_type SQL_IDENTIFIER_ATTR;
|
extern string_attr_type SQL_IDENTIFIER_ATTR;
|
||||||
extern string_attr_type SQL_FUNCTION_ATTR;
|
extern string_attr_type SQL_FUNCTION_ATTR;
|
||||||
|
@ -91,4 +91,14 @@ extern sqlite_registration_func_t sqlite_registration_funcs[];
|
|||||||
|
|
||||||
int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs);
|
int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs);
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
int sqlite3_db_dump(
|
||||||
|
sqlite3 *db, /* The database connection */
|
||||||
|
const char *zSchema, /* Which schema to dump. Usually "main". */
|
||||||
|
const char *zTable, /* Which table to dump. NULL means everything. */
|
||||||
|
int (*xCallback)(const char*,void*), /* Output sent to this callback */
|
||||||
|
void *pArg /* Second argument of the callback */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
726
src/third-party/sqlite/ext/dbdump.c
vendored
Normal file
726
src/third-party/sqlite/ext/dbdump.c
vendored
Normal file
@ -0,0 +1,726 @@
|
|||||||
|
/*
|
||||||
|
** 2016-03-13
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
******************************************************************************
|
||||||
|
**
|
||||||
|
** This file implements a C-language subroutine that converts the content
|
||||||
|
** of an SQLite database into UTF-8 text SQL statements that can be used
|
||||||
|
** to exactly recreate the original database. ROWID values are preserved.
|
||||||
|
**
|
||||||
|
** A prototype of the implemented subroutine is this:
|
||||||
|
**
|
||||||
|
** int sqlite3_db_dump(
|
||||||
|
** sqlite3 *db,
|
||||||
|
** const char *zSchema,
|
||||||
|
** const char *zTable,
|
||||||
|
** void (*xCallback)(void*, const char*),
|
||||||
|
** void *pArg
|
||||||
|
** );
|
||||||
|
**
|
||||||
|
** The db parameter is the database connection. zSchema is the schema within
|
||||||
|
** that database which is to be dumped. Usually the zSchema is "main" but
|
||||||
|
** can also be "temp" or any ATTACH-ed database. If zTable is not NULL, then
|
||||||
|
** only the content of that one table is dumped. If zTable is NULL, then all
|
||||||
|
** tables are dumped.
|
||||||
|
**
|
||||||
|
** The generate text is passed to xCallback() in multiple calls. The second
|
||||||
|
** argument to xCallback() is a copy of the pArg parameter. The first
|
||||||
|
** argument is some of the output text that this routine generates. The
|
||||||
|
** signature to xCallback() is designed to make it compatible with fputs().
|
||||||
|
**
|
||||||
|
** The sqlite3_db_dump() subroutine returns SQLITE_OK on success or some error
|
||||||
|
** code if it encounters a problem.
|
||||||
|
**
|
||||||
|
** If this file is compiled with -DDBDUMP_STANDALONE then a "main()" routine
|
||||||
|
** is included so that this routine becomes a command-line utility. The
|
||||||
|
** command-line utility takes two or three arguments which are the name
|
||||||
|
** of the database file, the schema, and optionally the table, forming the
|
||||||
|
** first three arguments of a single call to the library routine.
|
||||||
|
*/
|
||||||
|
#include "sqlite3.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The state of the dump process.
|
||||||
|
*/
|
||||||
|
typedef struct DState DState;
|
||||||
|
struct DState {
|
||||||
|
sqlite3 *db; /* The database connection */
|
||||||
|
int nErr; /* Number of errors seen so far */
|
||||||
|
int rc; /* Error code */
|
||||||
|
int writableSchema; /* True if in writable_schema mode */
|
||||||
|
int (*xCallback)(const char*,void*); /* Send output here */
|
||||||
|
void *pArg; /* Argument to xCallback() */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A variable length string to which one can append text.
|
||||||
|
*/
|
||||||
|
typedef struct DText DText;
|
||||||
|
struct DText {
|
||||||
|
char *z; /* The text */
|
||||||
|
int n; /* Number of bytes of content in z[] */
|
||||||
|
int nAlloc; /* Number of bytes allocated to z[] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Initialize and destroy a DText object
|
||||||
|
*/
|
||||||
|
static void initText(DText *p){
|
||||||
|
memset(p, 0, sizeof(*p));
|
||||||
|
}
|
||||||
|
static void freeText(DText *p){
|
||||||
|
sqlite3_free(p->z);
|
||||||
|
initText(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zIn is either a pointer to a NULL-terminated string in memory obtained
|
||||||
|
** from malloc(), or a NULL pointer. The string pointed to by zAppend is
|
||||||
|
** added to zIn, and the result returned in memory obtained from malloc().
|
||||||
|
** zIn, if it was not NULL, is freed.
|
||||||
|
**
|
||||||
|
** If the third argument, quote, is not '\0', then it is used as a
|
||||||
|
** quote character for zAppend.
|
||||||
|
*/
|
||||||
|
static void appendText(DText *p, char const *zAppend, char quote){
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
int nAppend = (int)(strlen(zAppend) & 0x3fffffff);
|
||||||
|
|
||||||
|
len = nAppend+p->n+1;
|
||||||
|
if( quote ){
|
||||||
|
len += 2;
|
||||||
|
for(i=0; i<nAppend; i++){
|
||||||
|
if( zAppend[i]==quote ) len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( p->n+len>=p->nAlloc ){
|
||||||
|
char *zNew;
|
||||||
|
p->nAlloc = p->nAlloc*2 + len + 20;
|
||||||
|
zNew = sqlite3_realloc(p->z, p->nAlloc);
|
||||||
|
if( zNew==0 ){
|
||||||
|
freeText(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p->z = zNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( quote ){
|
||||||
|
char *zCsr = p->z+p->n;
|
||||||
|
*zCsr++ = quote;
|
||||||
|
for(i=0; i<nAppend; i++){
|
||||||
|
*zCsr++ = zAppend[i];
|
||||||
|
if( zAppend[i]==quote ) *zCsr++ = quote;
|
||||||
|
}
|
||||||
|
*zCsr++ = quote;
|
||||||
|
p->n = (int)(zCsr - p->z);
|
||||||
|
*zCsr = '\0';
|
||||||
|
}else{
|
||||||
|
memcpy(p->z+p->n, zAppend, nAppend);
|
||||||
|
p->n += nAppend;
|
||||||
|
p->z[p->n] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Attempt to determine if identifier zName needs to be quoted, either
|
||||||
|
** because it contains non-alphanumeric characters, or because it is an
|
||||||
|
** SQLite keyword. Be conservative in this estimate: When in doubt assume
|
||||||
|
** that quoting is required.
|
||||||
|
**
|
||||||
|
** Return '"' if quoting is required. Return 0 if no quoting is required.
|
||||||
|
*/
|
||||||
|
static char quoteChar(const char *zName){
|
||||||
|
int i;
|
||||||
|
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
|
||||||
|
for(i=0; zName[i]; i++){
|
||||||
|
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
|
||||||
|
}
|
||||||
|
return sqlite3_keyword_check(zName, i) ? '"' : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Release memory previously allocated by tableColumnList().
|
||||||
|
*/
|
||||||
|
static void freeColumnList(char **azCol){
|
||||||
|
int i;
|
||||||
|
for(i=1; azCol[i]; i++){
|
||||||
|
sqlite3_free(azCol[i]);
|
||||||
|
}
|
||||||
|
/* azCol[0] is a static string */
|
||||||
|
sqlite3_free(azCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return a list of pointers to strings which are the names of all
|
||||||
|
** columns in table zTab. The memory to hold the names is dynamically
|
||||||
|
** allocated and must be released by the caller using a subsequent call
|
||||||
|
** to freeColumnList().
|
||||||
|
**
|
||||||
|
** The azCol[0] entry is usually NULL. However, if zTab contains a rowid
|
||||||
|
** value that needs to be preserved, then azCol[0] is filled in with the
|
||||||
|
** name of the rowid column.
|
||||||
|
**
|
||||||
|
** The first regular column in the table is azCol[1]. The list is terminated
|
||||||
|
** by an entry with azCol[i]==0.
|
||||||
|
*/
|
||||||
|
static char **tableColumnList(DState *p, const char *zTab){
|
||||||
|
char **azCol = 0;
|
||||||
|
sqlite3_stmt *pStmt = 0;
|
||||||
|
char *zSql;
|
||||||
|
int nCol = 0;
|
||||||
|
int nAlloc = 0;
|
||||||
|
int nPK = 0; /* Number of PRIMARY KEY columns seen */
|
||||||
|
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
|
||||||
|
int preserveRowid = 1;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
|
||||||
|
if( zSql==0 ) return 0;
|
||||||
|
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||||
|
sqlite3_free(zSql);
|
||||||
|
if( rc ) return 0;
|
||||||
|
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||||
|
if( nCol>=nAlloc-2 ){
|
||||||
|
char **azNew;
|
||||||
|
nAlloc = nAlloc*2 + nCol + 10;
|
||||||
|
azNew = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
|
||||||
|
if( azNew==0 ) goto col_oom;
|
||||||
|
azCol = azNew;
|
||||||
|
azCol[0] = 0;
|
||||||
|
}
|
||||||
|
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
||||||
|
if( azCol[nCol]==0 ) goto col_oom;
|
||||||
|
if( sqlite3_column_int(pStmt, 5) ){
|
||||||
|
nPK++;
|
||||||
|
if( nPK==1
|
||||||
|
&& sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2),
|
||||||
|
"INTEGER")==0
|
||||||
|
){
|
||||||
|
isIPK = 1;
|
||||||
|
}else{
|
||||||
|
isIPK = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(pStmt);
|
||||||
|
pStmt = 0;
|
||||||
|
azCol[nCol+1] = 0;
|
||||||
|
|
||||||
|
/* The decision of whether or not a rowid really needs to be preserved
|
||||||
|
** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table
|
||||||
|
** or a table with an INTEGER PRIMARY KEY. We are unable to preserve
|
||||||
|
** rowids on tables where the rowid is inaccessible because there are other
|
||||||
|
** columns in the table named "rowid", "_rowid_", and "oid".
|
||||||
|
*/
|
||||||
|
if( isIPK ){
|
||||||
|
/* If a single PRIMARY KEY column with type INTEGER was seen, then it
|
||||||
|
** might be an alise for the ROWID. But it might also be a WITHOUT ROWID
|
||||||
|
** table or a INTEGER PRIMARY KEY DESC column, neither of which are
|
||||||
|
** ROWID aliases. To distinguish these cases, check to see if
|
||||||
|
** there is a "pk" entry in "PRAGMA index_list". There will be
|
||||||
|
** no "pk" index if the PRIMARY KEY really is an alias for the ROWID.
|
||||||
|
*/
|
||||||
|
zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)"
|
||||||
|
" WHERE origin='pk'", zTab);
|
||||||
|
if( zSql==0 ) goto col_oom;
|
||||||
|
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||||
|
sqlite3_free(zSql);
|
||||||
|
if( rc ){
|
||||||
|
freeColumnList(azCol);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(pStmt);
|
||||||
|
sqlite3_finalize(pStmt);
|
||||||
|
pStmt = 0;
|
||||||
|
preserveRowid = rc==SQLITE_ROW;
|
||||||
|
}
|
||||||
|
if( preserveRowid ){
|
||||||
|
/* Only preserve the rowid if we can find a name to use for the
|
||||||
|
** rowid */
|
||||||
|
static char *azRowid[] = { "rowid", "_rowid_", "oid" };
|
||||||
|
int i, j;
|
||||||
|
for(j=0; j<3; j++){
|
||||||
|
for(i=1; i<=nCol; i++){
|
||||||
|
if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break;
|
||||||
|
}
|
||||||
|
if( i>nCol ){
|
||||||
|
/* At this point, we know that azRowid[j] is not the name of any
|
||||||
|
** ordinary column in the table. Verify that azRowid[j] is a valid
|
||||||
|
** name for the rowid before adding it to azCol[0]. WITHOUT ROWID
|
||||||
|
** tables will fail this last check */
|
||||||
|
rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0);
|
||||||
|
if( rc==SQLITE_OK ) azCol[0] = azRowid[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return azCol;
|
||||||
|
|
||||||
|
col_oom:
|
||||||
|
sqlite3_finalize(pStmt);
|
||||||
|
freeColumnList(azCol);
|
||||||
|
p->nErr++;
|
||||||
|
p->rc = SQLITE_NOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Send mprintf-formatted content to the output callback.
|
||||||
|
*/
|
||||||
|
static void output_formatted(DState *p, const char *zFormat, ...){
|
||||||
|
va_list ap;
|
||||||
|
char *z;
|
||||||
|
va_start(ap, zFormat);
|
||||||
|
z = sqlite3_vmprintf(zFormat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
p->xCallback(z, p->pArg);
|
||||||
|
sqlite3_free(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find a string that is not found anywhere in z[]. Return a pointer
|
||||||
|
** to that string.
|
||||||
|
**
|
||||||
|
** Try to use zA and zB first. If both of those are already found in z[]
|
||||||
|
** then make up some string and store it in the buffer zBuf.
|
||||||
|
*/
|
||||||
|
static const char *unused_string(
|
||||||
|
const char *z, /* Result must not appear anywhere in z */
|
||||||
|
const char *zA, const char *zB, /* Try these first */
|
||||||
|
char *zBuf /* Space to store a generated string */
|
||||||
|
){
|
||||||
|
unsigned i = 0;
|
||||||
|
if( strstr(z, zA)==0 ) return zA;
|
||||||
|
if( strstr(z, zB)==0 ) return zB;
|
||||||
|
do{
|
||||||
|
sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
|
||||||
|
}while( strstr(z,zBuf)!=0 );
|
||||||
|
return zBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Output the given string as a quoted string using SQL quoting conventions.
|
||||||
|
** Additionallly , escape the "\n" and "\r" characters so that they do not
|
||||||
|
** get corrupted by end-of-line translation facilities in some operating
|
||||||
|
** systems.
|
||||||
|
*/
|
||||||
|
static void output_quoted_escaped_string(DState *p, const char *z){
|
||||||
|
int i;
|
||||||
|
char c;
|
||||||
|
for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
|
||||||
|
if( c==0 ){
|
||||||
|
output_formatted(p,"'%s'",z);
|
||||||
|
}else{
|
||||||
|
const char *zNL = 0;
|
||||||
|
const char *zCR = 0;
|
||||||
|
int nNL = 0;
|
||||||
|
int nCR = 0;
|
||||||
|
char zBuf1[20], zBuf2[20];
|
||||||
|
for(i=0; z[i]; i++){
|
||||||
|
if( z[i]=='\n' ) nNL++;
|
||||||
|
if( z[i]=='\r' ) nCR++;
|
||||||
|
}
|
||||||
|
if( nNL ){
|
||||||
|
p->xCallback("replace(", p->pArg);
|
||||||
|
zNL = unused_string(z, "\\n", "\\012", zBuf1);
|
||||||
|
}
|
||||||
|
if( nCR ){
|
||||||
|
p->xCallback("replace(", p->pArg);
|
||||||
|
zCR = unused_string(z, "\\r", "\\015", zBuf2);
|
||||||
|
}
|
||||||
|
p->xCallback("'", p->pArg);
|
||||||
|
while( *z ){
|
||||||
|
for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
|
||||||
|
if( c=='\'' ) i++;
|
||||||
|
if( i ){
|
||||||
|
output_formatted(p, "%.*s", i, z);
|
||||||
|
z += i;
|
||||||
|
}
|
||||||
|
if( c=='\'' ){
|
||||||
|
p->xCallback("'", p->pArg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( c==0 ){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
z++;
|
||||||
|
if( c=='\n' ){
|
||||||
|
p->xCallback(zNL, p->pArg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p->xCallback(zCR, p->pArg);
|
||||||
|
}
|
||||||
|
p->xCallback("'", p->pArg);
|
||||||
|
if( nCR ){
|
||||||
|
output_formatted(p, ",'%s',char(13))", zCR);
|
||||||
|
}
|
||||||
|
if( nNL ){
|
||||||
|
output_formatted(p, ",'%s',char(10))", zNL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This is an sqlite3_exec callback routine used for dumping the database.
|
||||||
|
** Each row received by this callback consists of a table name,
|
||||||
|
** the table type ("index" or "table") and SQL to create the table.
|
||||||
|
** This routine should print text sufficient to recreate the table.
|
||||||
|
*/
|
||||||
|
static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||||
|
int rc;
|
||||||
|
const char *zTable;
|
||||||
|
const char *zType;
|
||||||
|
const char *zSql;
|
||||||
|
DState *p = (DState*)pArg;
|
||||||
|
sqlite3_stmt *pStmt;
|
||||||
|
|
||||||
|
(void)azCol;
|
||||||
|
if( nArg!=3 ) return 1;
|
||||||
|
zTable = azArg[0];
|
||||||
|
zType = azArg[1];
|
||||||
|
zSql = azArg[2];
|
||||||
|
|
||||||
|
if( strcmp(zTable, "sqlite_sequence")==0 ){
|
||||||
|
p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg);
|
||||||
|
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
|
||||||
|
p->xCallback("ANALYZE sqlite_schema;\n", p->pArg);
|
||||||
|
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
|
||||||
|
return 0;
|
||||||
|
}else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
|
||||||
|
#if 0
|
||||||
|
if( !p->writableSchema ){
|
||||||
|
p->xCallback("PRAGMA writable_schema=ON;\n", p->pArg);
|
||||||
|
p->writableSchema = 1;
|
||||||
|
}
|
||||||
|
output_formatted(p,
|
||||||
|
"INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)"
|
||||||
|
"VALUES('table','%q','%q',0,'%q');",
|
||||||
|
zTable, zTable, zSql);
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}else{
|
||||||
|
if( sqlite3_strglob("CREATE TABLE ['\"]*", zSql)==0 ){
|
||||||
|
p->xCallback("CREATE TABLE IF NOT EXISTS ", p->pArg);
|
||||||
|
p->xCallback(zSql+13, p->pArg);
|
||||||
|
}else{
|
||||||
|
p->xCallback(zSql, p->pArg);
|
||||||
|
}
|
||||||
|
p->xCallback(";\n", p->pArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( strcmp(zType, "table")==0 ){
|
||||||
|
DText sSelect;
|
||||||
|
DText sTable;
|
||||||
|
char **azTCol;
|
||||||
|
int i;
|
||||||
|
int nCol;
|
||||||
|
|
||||||
|
azTCol = tableColumnList(p, zTable);
|
||||||
|
if( azTCol==0 ) return 0;
|
||||||
|
|
||||||
|
initText(&sTable);
|
||||||
|
appendText(&sTable, "INSERT INTO ", 0);
|
||||||
|
|
||||||
|
/* Always quote the table name, even if it appears to be pure ascii,
|
||||||
|
** in case it is a keyword. Ex: INSERT INTO "table" ... */
|
||||||
|
appendText(&sTable, zTable, quoteChar(zTable));
|
||||||
|
|
||||||
|
/* If preserving the rowid, add a column list after the table name.
|
||||||
|
** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)"
|
||||||
|
** instead of the usual "INSERT INTO tab VALUES(...)".
|
||||||
|
*/
|
||||||
|
if( azTCol[0] ){
|
||||||
|
appendText(&sTable, "(", 0);
|
||||||
|
appendText(&sTable, azTCol[0], 0);
|
||||||
|
for(i=1; azTCol[i]; i++){
|
||||||
|
appendText(&sTable, ",", 0);
|
||||||
|
appendText(&sTable, azTCol[i], quoteChar(azTCol[i]));
|
||||||
|
}
|
||||||
|
appendText(&sTable, ")", 0);
|
||||||
|
}
|
||||||
|
appendText(&sTable, " VALUES(", 0);
|
||||||
|
|
||||||
|
/* Build an appropriate SELECT statement */
|
||||||
|
initText(&sSelect);
|
||||||
|
appendText(&sSelect, "SELECT ", 0);
|
||||||
|
if( azTCol[0] ){
|
||||||
|
appendText(&sSelect, azTCol[0], 0);
|
||||||
|
appendText(&sSelect, ",", 0);
|
||||||
|
}
|
||||||
|
for(i=1; azTCol[i]; i++){
|
||||||
|
appendText(&sSelect, azTCol[i], quoteChar(azTCol[i]));
|
||||||
|
if( azTCol[i+1] ){
|
||||||
|
appendText(&sSelect, ",", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nCol = i;
|
||||||
|
if( azTCol[0]==0 ) nCol--;
|
||||||
|
freeColumnList(azTCol);
|
||||||
|
appendText(&sSelect, " FROM ", 0);
|
||||||
|
appendText(&sSelect, zTable, quoteChar(zTable));
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(p->db, sSelect.z, -1, &pStmt, 0);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
p->nErr++;
|
||||||
|
if( p->rc==SQLITE_OK ) p->rc = rc;
|
||||||
|
}else{
|
||||||
|
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||||
|
p->xCallback(sTable.z, p->pArg);
|
||||||
|
for(i=0; i<nCol; i++){
|
||||||
|
if( i ) p->xCallback(",", p->pArg);
|
||||||
|
switch( sqlite3_column_type(pStmt,i) ){
|
||||||
|
case SQLITE_INTEGER: {
|
||||||
|
output_formatted(p, "%lld", sqlite3_column_int64(pStmt,i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLITE_FLOAT: {
|
||||||
|
double r = sqlite3_column_double(pStmt,i);
|
||||||
|
sqlite3_uint64 ur;
|
||||||
|
memcpy(&ur,&r,sizeof(r));
|
||||||
|
if( ur==0x7ff0000000000000LL ){
|
||||||
|
p->xCallback("1e999", p->pArg);
|
||||||
|
}else if( ur==0xfff0000000000000LL ){
|
||||||
|
p->xCallback("-1e999", p->pArg);
|
||||||
|
}else{
|
||||||
|
output_formatted(p, "%!.20g", r);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLITE_NULL: {
|
||||||
|
p->xCallback("NULL", p->pArg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLITE_TEXT: {
|
||||||
|
output_quoted_escaped_string(p,
|
||||||
|
(const char*)sqlite3_column_text(pStmt,i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLITE_BLOB: {
|
||||||
|
int nByte = sqlite3_column_bytes(pStmt,i);
|
||||||
|
unsigned char *a = (unsigned char*)sqlite3_column_blob(pStmt,i);
|
||||||
|
int j;
|
||||||
|
p->xCallback("x'", p->pArg);
|
||||||
|
for(j=0; j<nByte; j++){
|
||||||
|
char zWord[3];
|
||||||
|
zWord[0] = "0123456789abcdef"[(a[j]>>4)&15];
|
||||||
|
zWord[1] = "0123456789abcdef"[a[j]&15];
|
||||||
|
zWord[2] = 0;
|
||||||
|
p->xCallback(zWord, p->pArg);
|
||||||
|
}
|
||||||
|
p->xCallback("'", p->pArg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p->xCallback(");\n", p->pArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(pStmt);
|
||||||
|
freeText(&sTable);
|
||||||
|
freeText(&sSelect);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Execute a query statement that will generate SQL output. Print
|
||||||
|
** the result columns, comma-separated, on a line and then add a
|
||||||
|
** semicolon terminator to the end of that line.
|
||||||
|
**
|
||||||
|
** If the number of columns is 1 and that column contains text "--"
|
||||||
|
** then write the semicolon on a separate line. That way, if a
|
||||||
|
** "--" comment occurs at the end of the statement, the comment
|
||||||
|
** won't consume the semicolon terminator.
|
||||||
|
*/
|
||||||
|
static void output_sql_from_query(
|
||||||
|
DState *p, /* Query context */
|
||||||
|
const char *zSelect, /* SELECT statement to extract content */
|
||||||
|
...
|
||||||
|
){
|
||||||
|
sqlite3_stmt *pSelect;
|
||||||
|
int rc;
|
||||||
|
int nResult;
|
||||||
|
int i;
|
||||||
|
const char *z;
|
||||||
|
char *zSql;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, zSelect);
|
||||||
|
zSql = sqlite3_vmprintf(zSelect, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if( zSql==0 ){
|
||||||
|
p->rc = SQLITE_NOMEM;
|
||||||
|
p->nErr++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pSelect, 0);
|
||||||
|
sqlite3_free(zSql);
|
||||||
|
if( rc!=SQLITE_OK || !pSelect ){
|
||||||
|
output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
|
||||||
|
sqlite3_errmsg(p->db));
|
||||||
|
p->nErr++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(pSelect);
|
||||||
|
nResult = sqlite3_column_count(pSelect);
|
||||||
|
while( rc==SQLITE_ROW ){
|
||||||
|
z = (const char*)sqlite3_column_text(pSelect, 0);
|
||||||
|
p->xCallback(z, p->pArg);
|
||||||
|
for(i=1; i<nResult; i++){
|
||||||
|
p->xCallback(",", p->pArg);
|
||||||
|
p->xCallback((const char*)sqlite3_column_text(pSelect,i), p->pArg);
|
||||||
|
}
|
||||||
|
if( z==0 ) z = "";
|
||||||
|
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
|
||||||
|
if( z[0] ){
|
||||||
|
p->xCallback("\n;\n", p->pArg);
|
||||||
|
}else{
|
||||||
|
p->xCallback(";\n", p->pArg);
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(pSelect);
|
||||||
|
}
|
||||||
|
rc = sqlite3_finalize(pSelect);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
|
||||||
|
sqlite3_errmsg(p->db));
|
||||||
|
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Run zQuery. Use dump_callback() as the callback routine so that
|
||||||
|
** the contents of the query are output as SQL statements.
|
||||||
|
**
|
||||||
|
** If we get a SQLITE_CORRUPT error, rerun the query after appending
|
||||||
|
** "ORDER BY rowid DESC" to the end.
|
||||||
|
*/
|
||||||
|
static void run_schema_dump_query(
|
||||||
|
DState *p,
|
||||||
|
const char *zQuery,
|
||||||
|
...
|
||||||
|
){
|
||||||
|
char *zErr = 0;
|
||||||
|
char *z;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, zQuery);
|
||||||
|
z = sqlite3_vmprintf(zQuery, ap);
|
||||||
|
va_end(ap);
|
||||||
|
sqlite3_exec(p->db, z, dump_callback, p, &zErr);
|
||||||
|
sqlite3_free(z);
|
||||||
|
if( zErr ){
|
||||||
|
output_formatted(p, "/****** %s ******/\n", zErr);
|
||||||
|
sqlite3_free(zErr);
|
||||||
|
p->nErr++;
|
||||||
|
zErr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Convert an SQLite database into SQL statements that will recreate that
|
||||||
|
** database.
|
||||||
|
*/
|
||||||
|
int sqlite3_db_dump(
|
||||||
|
sqlite3 *db, /* The database connection */
|
||||||
|
const char *zSchema, /* Which schema to dump. Usually "main". */
|
||||||
|
const char *zTable, /* Which table to dump. NULL means everything. */
|
||||||
|
int (*xCallback)(const char*,void*), /* Output sent to this callback */
|
||||||
|
void *pArg /* Second argument of the callback */
|
||||||
|
){
|
||||||
|
DState x;
|
||||||
|
memset(&x, 0, sizeof(x));
|
||||||
|
x.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||||
|
if( x.rc ) return x.rc;
|
||||||
|
x.db = db;
|
||||||
|
x.xCallback = xCallback;
|
||||||
|
x.pArg = pArg;
|
||||||
|
xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg);
|
||||||
|
if( zTable==0 ){
|
||||||
|
run_schema_dump_query(&x,
|
||||||
|
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||||
|
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
|
||||||
|
zSchema
|
||||||
|
);
|
||||||
|
run_schema_dump_query(&x,
|
||||||
|
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||||
|
"WHERE name=='sqlite_sequence'", zSchema
|
||||||
|
);
|
||||||
|
output_sql_from_query(&x,
|
||||||
|
"SELECT sql FROM sqlite_schema "
|
||||||
|
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
run_schema_dump_query(&x,
|
||||||
|
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||||
|
"WHERE tbl_name=%Q COLLATE nocase AND type=='table'"
|
||||||
|
" AND sql NOT NULL",
|
||||||
|
zSchema, zTable
|
||||||
|
);
|
||||||
|
output_sql_from_query(&x,
|
||||||
|
"SELECT sql FROM \"%w\".sqlite_schema "
|
||||||
|
"WHERE sql NOT NULL"
|
||||||
|
" AND type IN ('index','trigger','view')"
|
||||||
|
" AND tbl_name=%Q COLLATE nocase",
|
||||||
|
zSchema, zTable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if( x.writableSchema ){
|
||||||
|
xCallback("PRAGMA writable_schema=OFF;\n", pArg);
|
||||||
|
}
|
||||||
|
xCallback(x.nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n", pArg);
|
||||||
|
sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||||
|
return x.rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* The generic subroutine is above. The code the follows implements
|
||||||
|
** the command-line interface.
|
||||||
|
*/
|
||||||
|
#ifdef DBDUMP_STANDALONE
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Command-line interface
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
sqlite3 *db;
|
||||||
|
const char *zDb;
|
||||||
|
const char *zSchema;
|
||||||
|
const char *zTable = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if( argc<2 || argc>4 ){
|
||||||
|
fprintf(stderr, "Usage: %s DATABASE ?SCHEMA? ?TABLE?\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
zDb = argv[1];
|
||||||
|
zSchema = argc>=3 ? argv[2] : "main";
|
||||||
|
zTable = argc==4 ? argv[3] : 0;
|
||||||
|
|
||||||
|
rc = sqlite3_open(zDb, &db);
|
||||||
|
if( rc ){
|
||||||
|
fprintf(stderr, "Cannot open \"%s\": %s\n", zDb, sqlite3_errmsg(db));
|
||||||
|
sqlite3_close(db);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
rc = sqlite3_db_dump(db, zSchema, zTable,
|
||||||
|
(int(*)(const char*,void*))fputs, (void*)stdout);
|
||||||
|
if( rc ){
|
||||||
|
fprintf(stderr, "Error: sqlite3_db_dump() returns %d\n", rc);
|
||||||
|
}
|
||||||
|
sqlite3_close(db);
|
||||||
|
return rc!=SQLITE_OK;
|
||||||
|
}
|
||||||
|
#endif /* DBDUMP_STANDALONE */
|
@ -3,10 +3,16 @@ enable_testing()
|
|||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
.
|
.
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
${CMAKE_SOURCE_DIR}/src/fmtlib
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/../src
|
${CMAKE_CURRENT_BINARY_DIR}/../src
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(testdummy STATIC
|
||||||
|
test_stubs.cc)
|
||||||
|
target_link_libraries(testdummy PkgConfig::libpcre)
|
||||||
|
|
||||||
add_executable(test_abbrev test_abbrev.cc)
|
add_executable(test_abbrev test_abbrev.cc)
|
||||||
target_link_libraries(test_abbrev diag PkgConfig::libpcre)
|
target_link_libraries(test_abbrev diag PkgConfig::libpcre)
|
||||||
add_test(NAME test_abbrev COMMAND test_abbrev)
|
add_test(NAME test_abbrev COMMAND test_abbrev)
|
||||||
@ -59,20 +65,20 @@ target_link_libraries(test_reltime diag PkgConfig::libpcre)
|
|||||||
add_test(NAME test_reltime COMMAND test_reltime)
|
add_test(NAME test_reltime COMMAND test_reltime)
|
||||||
|
|
||||||
add_executable(test_top_status test_top_status.cc)
|
add_executable(test_top_status test_top_status.cc)
|
||||||
target_link_libraries(test_top_status diag PkgConfig::libpcre)
|
target_link_libraries(test_top_status diag testdummy PkgConfig::libpcre)
|
||||||
add_test(NAME test_top_status COMMAND test_top_status)
|
add_test(NAME test_top_status COMMAND test_top_status)
|
||||||
|
|
||||||
add_executable(drive_view_colors drive_view_colors.cc)
|
add_executable(drive_view_colors drive_view_colors.cc)
|
||||||
target_link_libraries(drive_view_colors diag PkgConfig::ncursesw)
|
target_link_libraries(drive_view_colors diag testdummy PkgConfig::ncursesw)
|
||||||
|
|
||||||
add_executable(drive_vt52_curses drive_vt52_curses.cc)
|
add_executable(drive_vt52_curses drive_vt52_curses.cc)
|
||||||
target_link_libraries(drive_vt52_curses diag PkgConfig::ncursesw)
|
target_link_libraries(drive_vt52_curses diag PkgConfig::ncursesw)
|
||||||
|
|
||||||
add_executable(drive_sql_anno drive_sql_anno.cc)
|
add_executable(drive_sql_anno drive_sql_anno.cc)
|
||||||
target_link_libraries(drive_sql_anno diag PkgConfig::libpcre)
|
target_link_libraries(drive_sql_anno diag testdummy PkgConfig::libpcre)
|
||||||
|
|
||||||
add_executable(drive_data_scanner drive_data_scanner.cc)
|
add_executable(drive_data_scanner drive_data_scanner.cc)
|
||||||
target_link_libraries(drive_data_scanner diag PkgConfig::libpcre)
|
target_link_libraries(drive_data_scanner diag testdummy PkgConfig::libpcre)
|
||||||
|
|
||||||
add_executable(scripty scripty.cc)
|
add_executable(scripty scripty.cc)
|
||||||
target_link_libraries(scripty diag PkgConfig::ncursesw)
|
target_link_libraries(scripty diag PkgConfig::ncursesw)
|
||||||
|
@ -24,6 +24,11 @@ AM_CPPFLAGS = \
|
|||||||
# AM_CFLAGS = -fprofile-arcs -ftest-coverage
|
# AM_CFLAGS = -fprofile-arcs -ftest-coverage
|
||||||
# AM_CXXFLAGS = -fprofile-arcs -ftest-coverage
|
# AM_CXXFLAGS = -fprofile-arcs -ftest-coverage
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libtestdummy.a
|
||||||
|
|
||||||
|
libtestdummy_a_SOURCES = \
|
||||||
|
test_stubs.cc
|
||||||
|
|
||||||
check_PROGRAMS = \
|
check_PROGRAMS = \
|
||||||
drive_data_scanner \
|
drive_data_scanner \
|
||||||
drive_line_buffer \
|
drive_line_buffer \
|
||||||
@ -75,6 +80,7 @@ TEXT2C_OBJS = \
|
|||||||
|
|
||||||
LDADD = \
|
LDADD = \
|
||||||
-lz \
|
-lz \
|
||||||
|
libtestdummy.a \
|
||||||
$(CONFIG_OBJS) \
|
$(CONFIG_OBJS) \
|
||||||
$(TEXT2C_OBJS) \
|
$(TEXT2C_OBJS) \
|
||||||
$(top_builddir)/src/libdiag.a \
|
$(top_builddir)/src/libdiag.a \
|
||||||
|
@ -50,15 +50,6 @@ using namespace std;
|
|||||||
|
|
||||||
const char *TMP_NAME = "scanned.tmp";
|
const char *TMP_NAME = "scanned.tmp";
|
||||||
|
|
||||||
string execute_any(exec_context &ec, const string &cmdline_with_mode)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_global_vars(exec_context &ec)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c, retval = EXIT_SUCCESS;
|
int c, retval = EXIT_SUCCESS;
|
||||||
|
@ -58,15 +58,6 @@ time_t time(time_t *_unused)
|
|||||||
return 1194107018;
|
return 1194107018;
|
||||||
}
|
}
|
||||||
|
|
||||||
string execute_any(exec_context &ec, const string &cmdline_with_mode)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_global_vars(exec_context &ec)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c, retval = EXIT_SUCCESS;
|
int c, retval = EXIT_SUCCESS;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "lnav.hh"
|
#include "base/injector.hh"
|
||||||
#include "auto_mem.hh"
|
#include "auto_mem.hh"
|
||||||
#include "sqlite-extension-func.hh"
|
#include "sqlite-extension-func.hh"
|
||||||
#include "regexp_vtab.hh"
|
#include "regexp_vtab.hh"
|
||||||
@ -17,8 +17,6 @@ struct callback_state {
|
|||||||
int cs_row;
|
int cs_row;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lnav_data_t lnav_data;
|
|
||||||
|
|
||||||
static int sql_callback(void *ptr,
|
static int sql_callback(void *ptr,
|
||||||
int ncols,
|
int ncols,
|
||||||
char **colvalues,
|
char **colvalues,
|
||||||
@ -36,36 +34,6 @@ static int sql_callback(void *ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rebuild_hist()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setup_logline_table(exec_context &ec)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rescan_files(bool required)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_for_children()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void rebuild_indexes()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
textview_curses *get_textview_for_mode(ln_mode_t mode)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
readline_context::command_map_t lnav_commands;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int retval = EXIT_SUCCESS;
|
int retval = EXIT_SUCCESS;
|
||||||
|
@ -38,38 +38,6 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
struct lnav_data_t lnav_data;
|
|
||||||
|
|
||||||
void rebuild_hist()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setup_logline_table(exec_context &ec)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rescan_files(bool required)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_for_children()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void rebuild_indexes()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
textview_curses *get_textview_for_mode(ln_mode_t mode)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
readline_context::command_map_t lnav_commands;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int retval = EXIT_SUCCESS;
|
int retval = EXIT_SUCCESS;
|
||||||
|
4
test/file_for_dot_read.sql
Normal file
4
test/file_for_dot_read.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
INSERT INTO environ VALUES ('SEARCH_TERM', '%mount%');
|
||||||
|
|
||||||
|
SELECT log_line, log_body FROM syslog_log WHERE log_body LIKE $SEARCH_TERM
|
@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
lnav_test="${top_builddir}/src/lnav-test"
|
lnav_test="${top_builddir}/src/lnav-test"
|
||||||
|
|
||||||
|
|
||||||
|
run_test ${lnav_test} -n \
|
||||||
|
-c ";.read nonexistent-file" \
|
||||||
|
${test_dir}/logfile_empty.0
|
||||||
|
|
||||||
|
check_error_output "read worked with a nonexistent file?" <<EOF
|
||||||
|
command-option:1: error: unable to read script file: nonexistent-file -- No such file or directory
|
||||||
|
EOF
|
||||||
|
|
||||||
|
run_test ${lnav_test} -n \
|
||||||
|
-c ";.read ${test_dir}/file_for_dot_read.sql" \
|
||||||
|
-c ':write-csv-to -' \
|
||||||
|
${test_dir}/logfile_syslog.0
|
||||||
|
|
||||||
|
check_output ".read did not work?" <<EOF
|
||||||
|
log_line,log_body
|
||||||
|
1, attempting to mount entry /auto/opt
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
run_test ${lnav_test} -n \
|
run_test ${lnav_test} -n \
|
||||||
-c ";SELECT replicate('foobar', 120)" \
|
-c ";SELECT replicate('foobar', 120)" \
|
||||||
${test_dir}/logfile_empty.0
|
${test_dir}/logfile_empty.0
|
||||||
|
71
test/test_stubs.cc
Normal file
71
test/test_stubs.cc
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021, 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 "lnav.hh"
|
||||||
|
#include "base/injector.hh"
|
||||||
|
#include "service_tags.hh"
|
||||||
|
|
||||||
|
struct lnav_data_t lnav_data;
|
||||||
|
|
||||||
|
void rebuild_hist()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setup_logline_table(exec_context &ec)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rescan_files(bool required)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_for_children()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void rebuild_indexes()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
textview_curses *get_textview_for_mode(ln_mode_t mode)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
readline_context::command_map_t lnav_commands;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void injector::force_linking(services::curl_streamer_t anno)
|
||||||
|
{
|
||||||
|
}
|
@ -48,15 +48,6 @@ int gettimeofday(struct timeval * tp, void * tzp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<string, string> execute_any(exec_context &ec, const string &cmdline_with_mode)
|
|
||||||
{
|
|
||||||
return Ok(string());
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_global_vars(exec_context &ec)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int retval = EXIT_SUCCESS;
|
int retval = EXIT_SUCCESS;
|
||||||
|
Loading…
Reference in New Issue
Block a user