[fstat_vtab] add error column

pull/1205/head
Tim Stack 10 months ago
parent 63e5b81002
commit b561dd18c4

@ -41,6 +41,14 @@
namespace lnav { namespace lnav {
namespace filesystem { namespace filesystem {
inline bool
is_glob(const std::string& fn)
{
return (fn.find('*') != std::string::npos
|| fn.find('?') != std::string::npos
|| fn.find('[') != std::string::npos);
}
inline int inline int
statp(const ghc::filesystem::path& path, struct stat* buf) statp(const ghc::filesystem::path& path, struct stat* buf)
{ {

@ -64,6 +64,7 @@ enum {
FSTAT_COL_ATIME, FSTAT_COL_ATIME,
FSTAT_COL_MTIME, FSTAT_COL_MTIME,
FSTAT_COL_CTIME, FSTAT_COL_CTIME,
FSTAT_COL_ERROR,
FSTAT_COL_PATTERN, FSTAT_COL_PATTERN,
FSTAT_COL_DATA, FSTAT_COL_DATA,
}; };
@ -93,6 +94,7 @@ CREATE TABLE fstat (
st_atime DATETIME, st_atime DATETIME,
st_mtime DATETIME, st_mtime DATETIME,
st_ctime DATETIME, st_ctime DATETIME,
error TEXT,
pattern TEXT HIDDEN, pattern TEXT HIDDEN,
data BLOB HIDDEN data BLOB HIDDEN
); );
@ -104,20 +106,20 @@ CREATE TABLE fstat (
static_root_mem<glob_t, globfree> c_glob; static_root_mem<glob_t, globfree> c_glob;
size_t c_path_index{0}; size_t c_path_index{0};
struct stat c_stat; struct stat c_stat;
std::string c_error;
cursor(sqlite3_vtab* vt) : base({vt}) explicit cursor(sqlite3_vtab* vt) : base({vt})
{ {
memset(&this->c_stat, 0, sizeof(this->c_stat)); memset(&this->c_stat, 0, sizeof(this->c_stat));
} }
void load_stat() void load_stat()
{ {
while ((this->c_path_index < this->c_glob->gl_pathc) auto rc = lstat(this->c_glob->gl_pathv[this->c_path_index],
&& lstat(this->c_glob->gl_pathv[this->c_path_index], &this->c_stat);
&this->c_stat)
== -1) if (rc == -1) {
{ this->c_error = strerror(errno);
this->c_path_index += 1;
} }
} }
@ -173,13 +175,23 @@ CREATE TABLE fstat (
break; break;
} }
case FSTAT_COL_DEV: case FSTAT_COL_DEV:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_dev); sqlite3_result_int(ctx, vc.c_stat.st_dev);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_INO: case FSTAT_COL_INO:
if (vc.c_error.empty()) {
sqlite3_result_int64(ctx, vc.c_stat.st_ino); sqlite3_result_int64(ctx, vc.c_stat.st_ino);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_TYPE: case FSTAT_COL_TYPE:
if (S_ISREG(vc.c_stat.st_mode)) { if (!vc.c_error.empty()) {
sqlite3_result_null(ctx);
} else if (S_ISREG(vc.c_stat.st_mode)) {
sqlite3_result_text(ctx, "reg", 3, SQLITE_STATIC); sqlite3_result_text(ctx, "reg", 3, SQLITE_STATIC);
} else if (S_ISBLK(vc.c_stat.st_mode)) { } else if (S_ISBLK(vc.c_stat.st_mode)) {
sqlite3_result_text(ctx, "blk", 3, SQLITE_STATIC); sqlite3_result_text(ctx, "blk", 3, SQLITE_STATIC);
@ -196,60 +208,124 @@ CREATE TABLE fstat (
} }
break; break;
case FSTAT_COL_MODE: case FSTAT_COL_MODE:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_mode & 0777); sqlite3_result_int(ctx, vc.c_stat.st_mode & 0777);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_NLINK: case FSTAT_COL_NLINK:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_nlink); sqlite3_result_int(ctx, vc.c_stat.st_nlink);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_UID: case FSTAT_COL_UID:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_uid); sqlite3_result_int(ctx, vc.c_stat.st_uid);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_USER: { case FSTAT_COL_USER: {
if (vc.c_error.empty()) {
struct passwd* pw = getpwuid(vc.c_stat.st_uid); struct passwd* pw = getpwuid(vc.c_stat.st_uid);
if (pw != nullptr) { if (pw != nullptr) {
sqlite3_result_text(ctx, pw->pw_name, -1, SQLITE_TRANSIENT); sqlite3_result_text(
ctx, pw->pw_name, -1, SQLITE_TRANSIENT);
} else { } else {
sqlite3_result_int(ctx, vc.c_stat.st_uid); sqlite3_result_int(ctx, vc.c_stat.st_uid);
} }
} else {
sqlite3_result_null(ctx);
}
break; break;
} }
case FSTAT_COL_GID: case FSTAT_COL_GID:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_gid); sqlite3_result_int(ctx, vc.c_stat.st_gid);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_GROUP: { case FSTAT_COL_GROUP: {
if (vc.c_error.empty()) {
struct group* gr = getgrgid(vc.c_stat.st_gid); struct group* gr = getgrgid(vc.c_stat.st_gid);
if (gr != nullptr) { if (gr != nullptr) {
sqlite3_result_text(ctx, gr->gr_name, -1, SQLITE_TRANSIENT); sqlite3_result_text(
ctx, gr->gr_name, -1, SQLITE_TRANSIENT);
} else { } else {
sqlite3_result_int(ctx, vc.c_stat.st_gid); sqlite3_result_int(ctx, vc.c_stat.st_gid);
} }
} else {
sqlite3_result_null(ctx);
}
break; break;
} }
case FSTAT_COL_RDEV: case FSTAT_COL_RDEV:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_rdev); sqlite3_result_int(ctx, vc.c_stat.st_rdev);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_SIZE: case FSTAT_COL_SIZE:
if (vc.c_error.empty()) {
sqlite3_result_int64(ctx, vc.c_stat.st_size); sqlite3_result_int64(ctx, vc.c_stat.st_size);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_BLKSIZE: case FSTAT_COL_BLKSIZE:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_blksize); sqlite3_result_int(ctx, vc.c_stat.st_blksize);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_BLOCKS: case FSTAT_COL_BLOCKS:
if (vc.c_error.empty()) {
sqlite3_result_int(ctx, vc.c_stat.st_blocks); sqlite3_result_int(ctx, vc.c_stat.st_blocks);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_ATIME: case FSTAT_COL_ATIME:
sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_atime, 0); if (vc.c_error.empty()) {
sql_strftime(
time_buf, sizeof(time_buf), vc.c_stat.st_atime, 0);
sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_MTIME: case FSTAT_COL_MTIME:
sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_mtime, 0); if (vc.c_error.empty()) {
sql_strftime(
time_buf, sizeof(time_buf), vc.c_stat.st_mtime, 0);
sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT);
} else {
sqlite3_result_null(ctx);
}
break; break;
case FSTAT_COL_CTIME: case FSTAT_COL_CTIME:
sql_strftime(time_buf, sizeof(time_buf), vc.c_stat.st_ctime, 0); if (vc.c_error.empty()) {
sql_strftime(
time_buf, sizeof(time_buf), vc.c_stat.st_ctime, 0);
sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT); sqlite3_result_text(ctx, time_buf, -1, SQLITE_TRANSIENT);
} else {
sqlite3_result_null(ctx);
}
break;
case FSTAT_COL_ERROR:
if (vc.c_error.empty()) {
sqlite3_result_null(ctx);
} else {
to_sqlite(ctx, vc.c_error);
}
break; break;
case FSTAT_COL_PATTERN: case FSTAT_COL_PATTERN:
sqlite3_result_text(ctx, sqlite3_result_text(ctx,
@ -259,7 +335,9 @@ CREATE TABLE fstat (
break; break;
case FSTAT_COL_DATA: { case FSTAT_COL_DATA: {
auto fs_path = ghc::filesystem::path{path}; auto fs_path = ghc::filesystem::path{path};
if (S_ISREG(vc.c_stat.st_mode)) { if (!vc.c_error.empty()) {
sqlite3_result_null(ctx);
} else if (S_ISREG(vc.c_stat.st_mode)) {
auto open_res auto open_res
= lnav::filesystem::open_file(fs_path, O_RDONLY); = lnav::filesystem::open_file(fs_path, O_RDONLY);
@ -366,14 +444,17 @@ rcFilter(sqlite3_vtab_cursor* pVtabCursor,
const char* pattern = (const char*) sqlite3_value_text(argv[0]); const char* pattern = (const char*) sqlite3_value_text(argv[0]);
pCur->c_pattern = pattern; pCur->c_pattern = pattern;
switch (glob(pattern,
auto glob_flags = GLOB_ERR;
if (!lnav::filesystem::is_glob(pCur->c_pattern)) {
glob_flags |= GLOB_NOCHECK;
}
#ifdef GLOB_TILDE #ifdef GLOB_TILDE
GLOB_TILDE | glob_flags |= GLOB_TILDE;
#endif #endif
GLOB_ERR,
nullptr, switch (glob(pattern, glob_flags, nullptr, pCur->c_glob.inout())) {
pCur->c_glob.inout()))
{
case GLOB_NOSPACE: case GLOB_NOSPACE:
pVtabCursor->pVtab->zErrMsg pVtabCursor->pVtab->zErrMsg
= sqlite3_mprintf("No space to perform glob()"); = sqlite3_mprintf("No space to perform glob()");

@ -3127,13 +3127,14 @@ radians(*degrees*)
.. _raise_error: .. _raise_error:
raise_error(*msg*) raise_error(*msg*, *\[reason\]*)
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Raises an error with the given message when executed Raises an error with the given message when executed
**Parameters** **Parameters**
* **msg\*** --- The error message * **msg\*** --- The error message
* **reason** --- The reason the error occurred
---- ----

@ -2955,7 +2955,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
fmt::format(FMT_STRING(":open {}"), file_path)); fmt::format(FMT_STRING(":open {}"), file_path));
} }
#endif #endif
else if (is_glob(file_path)) else if (lnav::filesystem::is_glob(file_path))
{ {
lnav_data.ld_active_files.fc_file_names[file_path].with_tail( lnav_data.ld_active_files.fc_file_names[file_path].with_tail(
!(lnav_data.ld_flags & LNF_HEADLESS)); !(lnav_data.ld_flags & LNF_HEADLESS));

@ -2699,7 +2699,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
} }
retval = "info: watching -- " + fn; retval = "info: watching -- " + fn;
} else if (is_glob(fn.c_str())) { } else if (lnav::filesystem::is_glob(fn.c_str())) {
fc.fc_file_names.emplace(fn, loo); fc.fc_file_names.emplace(fn, loo);
files_to_front.emplace_back( files_to_front.emplace_back(
loo.loo_filename.empty() ? fn : loo.loo_filename, file_loc); loo.loo_filename.empty() ? fn : loo.loo_filename, file_loc);
@ -2827,7 +2827,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
} }
}); });
lnav_data.ld_preview_view.set_needs_update(); lnav_data.ld_preview_view.set_needs_update();
} else if (is_glob(fn.c_str())) { } else if (lnav::filesystem::is_glob(fn.c_str())) {
static_root_mem<glob_t, globfree> gl; static_root_mem<glob_t, globfree> gl;
if (glob(fn.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { if (glob(fn.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {

@ -81,14 +81,6 @@ to_string(const char* s)
} }
} // namespace std } // namespace std
inline bool
is_glob(const std::string& fn)
{
return (fn.find('*') != std::string::npos
|| fn.find('?') != std::string::npos
|| fn.find('[') != std::string::npos);
}
inline void inline void
rusagesub(const struct rusage& left, rusagesub(const struct rusage& left,
const struct rusage& right, const struct rusage& right,

@ -7,6 +7,8 @@
jget(url, '/path') AS docker_path jget(url, '/path') AS docker_path
FROM (SELECT parse_url($1) AS url) FROM (SELECT parse_url($1) AS url)
;SELECT substr($docker_path, 2) AS docker_relpath
;SELECT CASE ;SELECT CASE
$docker_hostname $docker_hostname
WHEN 'compose' THEN ( WHEN 'compose' THEN (
@ -18,8 +20,19 @@
), ),
char(10) char(10)
) AS cmds ) AS cmds
FROM fstat(substr($docker_path, 2)), FROM fstat($docker_relpath) AS st,
json_each(yaml_to_json(data), '$.services') as compose_services json_each(
yaml_to_json(
ifnull(
st.data,
raise_error(
'Cannot read compose configuration: ' || $docker_relpath,
st.error
)
)
),
'$.services'
) as compose_services
) )
ELSE CASE ELSE CASE
$docker_path $docker_path

@ -99,9 +99,14 @@ sql_lnav_version()
} }
static int64_t static int64_t
sql_error(const char* str) sql_error(const char* str, nonstd::optional<string_fragment> reason)
{ {
throw sqlite_func_error("{}", str); auto um = lnav::console::user_message::error(str);
if (reason) {
um.with_reason(reason->to_string());
}
throw um;
} }
static nonstd::optional<std::string> static nonstd::optional<std::string>
@ -155,7 +160,10 @@ state_extension_functions(struct FuncDef** basic_funcs,
help_text("raise_error", help_text("raise_error",
"Raises an error with the given message when executed") "Raises an error with the given message when executed")
.sql_function() .sql_function()
.with_parameter({"msg", "The error message"})) .with_parameter({"msg", "The error message"})
.with_parameter(
help_text("reason", "The reason the error occurred")
.optional()))
.with_flags(SQLITE_UTF8), .with_flags(SQLITE_UTF8),
sqlite_func_adapter<decltype(&sql_echoln), sql_echoln>::builder( sqlite_func_adapter<decltype(&sql_echoln), sql_echoln>::builder(

@ -736,6 +736,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql_fs_func.sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_18ddc138b263dd06f3fe81fec05bc4330caffef7.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_20a76db446a0a558dcbdf41033f97d4a22ca1bfa.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_2aa83fc90c850cdd11e3136a1a02b79c5879824b.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_2aa83fc90c850cdd11e3136a1a02b79c5879824b.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_2c3f66e78deb8721b1d1fe5a787e9958895401d7.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_34baa8050f8278d7b68c29e53bdd9f37da0f34c8.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_34baa8050f8278d7b68c29e53bdd9f37da0f34c8.err \
@ -756,6 +758,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql_fs_func.sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_7b5d7dd8d0003ab83e3e5cb0a5ce802fe9a0e3b3.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_917ffde411c1425e8a6addae0170900dcd553986.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_9234a453403934587bbbdde355281a956d1fbe5f.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_9234a453403934587bbbdde355281a956d1fbe5f.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.err \
$(srcdir)/%reldir%/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.out \ $(srcdir)/%reldir%/test_sql_fs_func.sh_9e2c0a90ce333365ff7354375f2c609bc27135c8.out \
$(srcdir)/%reldir%/test_sql_fs_func.sh_a247b137e71124e496f1beab56c7fe85717c4199.err \ $(srcdir)/%reldir%/test_sql_fs_func.sh_a247b137e71124e496f1beab56c7fe85717c4199.err \

@ -3759,11 +3759,12 @@ For support questions, email:
raise_error(msg) raise_error(msg, [reason])
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
Raises an error with the given message when executed Raises an error with the given message when executed
Parameter Parameters
msg The error message msg The error message
reason The reason the error occurred
random() random()

@ -1,4 +1,3 @@
✘ error: call to raise_error(msg) failed ✘ error: oops!
reason: oops!
 --> command-option:1  --> command-option:1
 | ;SELECT raise_error('oops!')   | ;SELECT raise_error('oops!') 

@ -1,5 +1,4 @@
✘ error: call to raise_error(msg) failed ✘ error: no data was redirected to lnav's standard-input
reason: no data was redirected to lnav's standard-input
 --> command-option:1  --> command-option:1
 | |rename-stdin foo   | |rename-stdin foo 
 --> ../test/.lnav/formats/default/rename-stdin.lnav:7  --> ../test/.lnav/formats/default/rename-stdin.lnav:7

@ -1,5 +1,4 @@
✘ error: call to raise_error(msg) failed ✘ error: expecting the new name for stdin as the first argument
reason: expecting the new name for stdin as the first argument
 --> command-option:1  --> command-option:1
 | |rename-stdin   | |rename-stdin 
 --> ../test/.lnav/formats/default/rename-stdin.lnav:6  --> ../test/.lnav/formats/default/rename-stdin.lnav:6

@ -1,5 +1,4 @@
✘ error: call to raise_error(msg) failed ✘ error: oops!
reason: oops!
 --> command-option:2  --> command-option:2
 | ;SELECT $inum, $nul, $fnum, $str, raise_error('oops!')  | ;SELECT $inum, $nul, $fnum, $str, raise_error('oops!')
 = note: the bound parameters are set as follows:  = note: the bound parameters are set as follows:

@ -0,0 +1,2 @@
st_parent st_name st_dev st_ino st_type st_mode st_nlink st_uid st_user st_gid st_group st_rdev st_size st_blksize st_blocks st_atime st_mtime st_ctime error
/ non-existent <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> <NULL> No such file or directory

@ -53,3 +53,7 @@ run_cap_test ./drive_sql "select joinpath('foo', 'bar', 'baz')"
run_cap_test ./drive_sql "select joinpath('foo', 'bar', 'baz', '/root')" run_cap_test ./drive_sql "select joinpath('foo', 'bar', 'baz', '/root')"
run_cap_test ${lnav_test} -Nn -c ";SELECT shell_exec('echo hi')" run_cap_test ${lnav_test} -Nn -c ";SELECT shell_exec('echo hi')"
run_cap_test ${lnav_test} -Nn -c ";SELECT * FROM fstat('/non-existent')"
run_cap_test ${lnav_test} -Nn -c ";SELECT * FROM fstat('/*.non-existent')"

Loading…
Cancel
Save