• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.78
/src/log_vtab_impl.cc
1
/**
2
 * Copyright (c) 2007-2012, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include "log_vtab_impl.hh"
31

32
#include "base/ansi_scrubber.hh"
33
#include "base/itertools.hh"
34
#include "base/lnav_log.hh"
35
#include "base/string_util.hh"
36
#include "bookmarks.json.hh"
37
#include "config.h"
38
#include "hasher.hh"
39
#include "lnav_util.hh"
40
#include "logfile_sub_source.hh"
41
#include "scn/ranges.h"
42
#include "sql_util.hh"
43
#include "vtab_module.hh"
44
#include "vtab_module_json.hh"
45
#include "yajlpp/json_op.hh"
46
#include "yajlpp/yajlpp_def.hh"
47

48
// #define DEBUG_INDEXING 1
49

50
using namespace lnav::roles::literals;
51

52
static auto intern_lifetime = intern_string::get_table_lifetime();
53

54
static log_cursor log_cursor_latest;
55

56
thread_local _log_vtab_data log_vtab_data;
57

58
const std::unordered_set<string_fragment, frag_hasher>
59
    log_vtab_impl::RESERVED_COLUMNS = {
60
        "log_line"_frag,        "log_time"_frag,        "log_level"_frag,
61
        "log_part"_frag,        "log_actual_time"_frag, "log_idle_msecs"_frag,
62
        "log_mark"_frag,        "log_comment"_frag,     "log_tags"_frag,
63
        "log_annotations"_frag, "log_filters"_frag,     "log_opid"_frag,
64
        "log_user_opid"_frag,   "log_format"_frag,      "log_format_regex"_frag,
65
        "log_time_msecs"_frag,  "log_path"_frag,        "log_unique_path"_frag,
66
        "log_text"_frag,        "log_body"_frag,        "log_raw_text"_frag,
67
        "log_line_hash"_frag,   "log_line_link"_frag,   "log_src_file"_frag,
68
        "log_src_line"_frag,
69
};
70

71
static const char* const LOG_COLUMNS = R"(  (
72
  log_line        INTEGER,                         -- The line number for the log message
73
  log_time        DATETIME,                        -- The adjusted timestamp for the log message
74
  log_level       TEXT     COLLATE loglevel,       -- The log message level
75
  -- BEGIN Format-specific fields:
76
)";
77

78
static const char* const LOG_FOOTER_COLUMNS = R"(
79
  -- END Format-specific fields
80
  log_part         TEXT     COLLATE naturalnocase,    -- The partition the message is in
81
  log_actual_time  DATETIME HIDDEN,                   -- The timestamp from the original log file for this message
82
  log_idle_msecs   INTEGER,                           -- The difference in time between this messages and the previous
83
  log_mark         BOOLEAN,                           -- True if the log message was marked
84
  log_comment      TEXT,                              -- The comment for this message
85
  log_tags         TEXT,                              -- A JSON list of tags for this message
86
  log_annotations  TEXT,                              -- A JSON object of annotations for this messages
87
  log_filters      TEXT,                              -- A JSON list of filter IDs that matched this message
88
  log_opid         TEXT HIDDEN,                       -- The message's OPID from the log message or user
89
  log_user_opid    TEXT HIDDEN,                       -- The message's OPID as set by the user
90
  log_format       TEXT HIDDEN,                       -- The name of the log file format
91
  log_format_regex TEXT HIDDEN,                       -- The name of the regex used to parse this log message
92
  log_time_msecs   INTEGER HIDDEN,                    -- The adjusted timestamp for the log message as the number of milliseconds from the epoch
93
  log_path         TEXT HIDDEN COLLATE naturalnocase, -- The path to the log file this message is from
94
  log_unique_path  TEXT HIDDEN COLLATE naturalnocase, -- The unique portion of the path this message is from
95
  log_text         TEXT HIDDEN,                       -- The full text of the log message
96
  log_body         TEXT HIDDEN,                       -- The body of the log message
97
  log_raw_text     TEXT HIDDEN,                       -- The raw text from the log file
98
  log_line_hash    TEXT HIDDEN,                       -- A hash of the first line of the log message
99
  log_line_link    TEXT HIDDEN,                       -- The permalink for the log message
100
  log_src_file     TEXT HIDDEN,                       -- The source file the log message came from
101
  log_src_line     TEXT HIDDEN                        -- The source line the log message came from
102
)";
103

104
enum class log_footer_columns : uint32_t {
105
    partition,
106
    actual_time,
107
    idle_msecs,
108
    mark,
109
    comment,
110
    tags,
111
    annotations,
112
    filters,
113
    opid,
114
    user_opid,
115
    format,
116
    format_regex,
117
    time_msecs,
118
    path,
119
    unique_path,
120
    text,
121
    body,
122
    raw_text,
123
    line_hash,
124
    line_link,
125
    src_file,
126
    src_line,
127
};
128

129
const std::string&
130
log_vtab_impl::get_table_statement()
48,550✔
131
{
132
    if (this->vi_table_statement.empty()) {
48,550✔
133
        std::vector<vtab_column> cols;
48,444✔
134
        std::ostringstream oss;
48,444✔
135
        size_t max_name_len = 15;
48,444✔
136

137
        oss << "CREATE TABLE " << this->get_name().to_string() << LOG_COLUMNS;
48,444✔
138
        this->get_columns(cols);
48,444✔
139
        this->vi_column_count = cols.size();
48,444✔
140
        for (const auto& col : cols) {
480,298✔
141
            max_name_len = std::max(max_name_len, col.vc_name.length());
431,854✔
142
        }
143
        for (const auto& col : cols) {
480,298✔
144
            std::string comment;
431,854✔
145

146
            require(!col.vc_name.empty());
431,854✔
147

148
            if (!col.vc_comment.empty()) {
431,854✔
149
                comment.append(" -- ").append(col.vc_comment);
46,675✔
150
            }
151

152
            auto colname = sql_quote_ident(col.vc_name.c_str());
431,854✔
153
            auto coldecl = lnav::sql::mprintf(
154
                "  %-*s %-7s %s COLLATE %-15Q,%s\n",
155
                max_name_len,
156
                colname.in(),
157
                sqlite3_type_to_string(col.vc_type),
431,854✔
158
                col.vc_hidden ? "hidden" : "",
431,854✔
159
                col.vc_collator.empty() ? "BINARY" : col.vc_collator.c_str(),
444,673✔
160
                comment.c_str());
876,527✔
161
            oss << coldecl;
431,854✔
162
        }
431,854✔
163
        oss << LOG_FOOTER_COLUMNS;
48,444✔
164

165
        {
166
            std::vector<std::string> primary_keys;
48,444✔
167

168
            this->get_primary_keys(primary_keys);
48,444✔
169
            if (!primary_keys.empty()) {
48,444✔
170
                auto first = true;
6,954✔
171

172
                oss << ", PRIMARY KEY (";
6,954✔
173
                for (const auto& pkey : primary_keys) {
20,862✔
174
                    if (!first) {
13,908✔
175
                        oss << ", ";
6,954✔
176
                    }
177
                    oss << pkey;
13,908✔
178
                    first = false;
13,908✔
179
                }
180
                oss << ")\n";
6,954✔
181
            } else {
182
                oss << ", PRIMARY KEY (log_line)\n";
41,490✔
183
            }
184
        }
48,444✔
185

186
        oss << ");\n";
48,444✔
187

188
        log_trace("log_vtab_impl.get_table_statement() -> %s",
48,444✔
189
                  oss.str().c_str());
190

191
        this->vi_table_statement = oss.str();
48,444✔
192
    }
48,444✔
193

194
    return this->vi_table_statement;
48,550✔
195
}
196

197
std::pair<int, unsigned int>
198
log_vtab_impl::logline_value_to_sqlite_type(value_kind_t kind)
406,662✔
199
{
200
    int type = 0;
406,662✔
201
    unsigned int subtype = 0;
406,662✔
202

203
    switch (kind) {
406,662✔
204
        case value_kind_t::VALUE_JSON:
10,048✔
205
            type = SQLITE3_TEXT;
10,048✔
206
            subtype = JSON_SUBTYPE;
10,048✔
207
            break;
10,048✔
208
        case value_kind_t::VALUE_NULL:
308,551✔
209
        case value_kind_t::VALUE_TEXT:
210
        case value_kind_t::VALUE_STRUCT:
211
        case value_kind_t::VALUE_QUOTED:
212
        case value_kind_t::VALUE_W3C_QUOTED:
213
        case value_kind_t::VALUE_TIMESTAMP:
214
        case value_kind_t::VALUE_XML:
215
            type = SQLITE3_TEXT;
308,551✔
216
            break;
308,551✔
217
        case value_kind_t::VALUE_FLOAT:
5,227✔
218
            type = SQLITE_FLOAT;
5,227✔
219
            break;
5,227✔
220
        case value_kind_t::VALUE_BOOLEAN:
82,836✔
221
        case value_kind_t::VALUE_INTEGER:
222
            type = SQLITE_INTEGER;
82,836✔
223
            break;
82,836✔
UNCOV
224
        case value_kind_t::VALUE_UNKNOWN:
×
225
        case value_kind_t::VALUE__MAX:
UNCOV
226
            ensure(0);
×
227
            break;
228
    }
229
    return std::make_pair(type, subtype);
813,324✔
230
}
231

232
void
233
log_vtab_impl::get_foreign_keys(
39,634✔
234
    std::unordered_set<std::string>& keys_inout) const
235
{
236
    keys_inout.emplace("id");
39,634✔
237
    keys_inout.emplace("parent");
39,634✔
238
    keys_inout.emplace("notused");
39,634✔
239

240
    keys_inout.emplace("log_line");
39,634✔
241
    keys_inout.emplace("min(log_line)");
39,634✔
242
    keys_inout.emplace("log_mark");
39,634✔
243
    keys_inout.emplace("log_time_msecs");
39,634✔
244
    keys_inout.emplace("log_top_line()");
39,634✔
245
    keys_inout.emplace("log_msg_line()");
39,634✔
246
    keys_inout.emplace("log_src_line");
39,634✔
247
}
39,634✔
248

249
void
250
log_vtab_impl::extract(logfile* lf,
29,504✔
251
                       uint64_t line_number,
252
                       string_attrs_t& sa,
253
                       logline_value_vector& values)
254
{
255
    const auto* format = lf->get_format_ptr();
29,504✔
256

257
    format->annotate(lf, line_number, sa, values, false);
29,504✔
258
}
29,504✔
259

260
bool
261
log_vtab_impl::is_valid(log_cursor& lc, logfile_sub_source& lss)
54,829✔
262
{
263
    if (lc.lc_curr_line < 0_vl) {
54,829✔
UNCOV
264
        return false;
×
265
    }
266

267
    content_line_t cl(lss.at(lc.lc_curr_line));
54,829✔
268
    auto* lf = lss.find_file_ptr(cl);
54,829✔
269
    auto lf_iter = lf->begin() + cl;
54,829✔
270

271
    if (!lf_iter->is_message()) {
54,829✔
272
        return false;
280✔
273
    }
274

275
    if (!lc.lc_format_name.empty()
54,549✔
276
        && lc.lc_format_name != lf->get_format_name())
54,549✔
277
    {
278
        return false;
3✔
279
    }
280

281
    if (!lc.lc_pattern_name.empty()
54,546✔
282
        && lc.lc_pattern_name != lf->get_format_ptr()->get_pattern_name(cl))
54,546✔
283
    {
UNCOV
284
        return false;
×
285
    }
286

287
    if (lc.lc_level_constraint
54,546✔
288
        && !lc.lc_level_constraint->matches(lf_iter->get_msg_level()))
54,546✔
289
    {
290
        return false;
206✔
291
    }
292

293
    if (!lc.lc_log_path.empty()) {
54,340✔
294
        if (lf == lc.lc_last_log_path_match) {
4✔
295
        } else if (lf == lc.lc_last_log_path_mismatch) {
2✔
UNCOV
296
            return false;
×
297
        } else {
298
            for (const auto& path_cons : lc.lc_log_path) {
4✔
299
                if (!path_cons.matches(lf->get_filename())) {
2✔
300
                    lc.lc_last_log_path_mismatch = lf;
×
UNCOV
301
                    return false;
×
302
                }
303
            }
304
            lc.lc_last_log_path_match = lf;
2✔
305
        }
306
    }
307

308
    if (!lc.lc_unique_path.empty()) {
54,340✔
UNCOV
309
        if (lf == lc.lc_last_unique_path_match) {
×
UNCOV
310
        } else if (lf == lc.lc_last_unique_path_mismatch) {
×
UNCOV
311
            return false;
×
312
        } else {
UNCOV
313
            for (const auto& path_cons : lc.lc_unique_path) {
×
UNCOV
314
                if (!path_cons.matches(lf->get_unique_path())) {
×
UNCOV
315
                    lc.lc_last_unique_path_mismatch = lf;
×
UNCOV
316
                    return false;
×
317
                }
318
            }
UNCOV
319
            lc.lc_last_unique_path_match = lf;
×
320
        }
321
    }
322

323
    if (lc.lc_opid && !lf_iter->match_opid_hash(lc.lc_opid.value())) {
54,340✔
324
        return false;
×
325
    }
326

327
    return true;
54,340✔
328
}
329

330
struct log_vtab {
331
    sqlite3_vtab base;
332
    sqlite3* db;
333
    textview_curses* tc{nullptr};
334
    logfile_sub_source* lss{nullptr};
335
    std::shared_ptr<log_vtab_impl> vi;
336

337
    size_t footer_index(log_footer_columns col) const
304✔
338
    {
339
        return VT_COL_MAX + this->vi->vi_column_count
304✔
340
            + lnav::enums::to_underlying(col);
304✔
341
    }
342
};
343

344
struct vtab_cursor {
345
    void cache_msg(logfile* lf, logfile::const_iterator ll)
33,098✔
346
    {
347
        if (this->log_msg_line == this->log_cursor.lc_curr_line) {
33,098✔
UNCOV
348
            return;
×
349
        }
350
        auto& sbr = this->line_values.lvv_sbr;
33,098✔
351
        lf->read_full_message(ll,
33,098✔
352
                              sbr,
353
                              this->log_cursor.lc_direction < 0
33,098✔
354
                                  ? line_buffer::scan_direction::backward
355
                                  : line_buffer::scan_direction::forward);
356
        sbr.erase_ansi();
33,098✔
357
        this->log_msg_line = this->log_cursor.lc_curr_line;
33,098✔
358
    }
359

360
    void invalidate()
33,971✔
361
    {
362
        this->attrs.clear();
33,971✔
363
        this->line_values.clear();
33,971✔
364
        this->log_msg_line = -1_vl;
33,971✔
365
    }
33,971✔
366

367
    sqlite3_vtab_cursor base;
368
    struct log_cursor log_cursor;
369
    vis_line_t log_msg_line{-1_vl};
370
    string_attrs_t attrs;
371
    logline_value_vector line_values;
372
};
373

374
static int vt_destructor(sqlite3_vtab* p_svt);
375

376
static int
377
vt_create(sqlite3* db,
48,467✔
378
          void* pAux,
379
          int argc,
380
          const char* const* argv,
381
          sqlite3_vtab** pp_vt,
382
          char** pzErr)
383
{
384
    auto* vm = (log_vtab_manager*) pAux;
48,467✔
385
    int rc = SQLITE_OK;
48,467✔
386
    /* Allocate the sqlite3_vtab/vtab structure itself */
387
    auto p_vt = std::make_unique<log_vtab>();
48,467✔
388

389
    p_vt->db = db;
48,467✔
390

391
    /* Declare the vtable's structure */
392
    p_vt->vi = vm->lookup_impl(intern_string::lookup(argv[3]));
96,934✔
393
    if (p_vt->vi == nullptr) {
48,467✔
UNCOV
394
        return SQLITE_ERROR;
×
395
    }
396
    p_vt->tc = vm->get_view();
48,467✔
397
    p_vt->lss = vm->get_source();
48,467✔
398
    rc = sqlite3_declare_vtab(db, p_vt->vi->get_table_statement().c_str());
48,467✔
399

400
    if (rc == SQLITE_OK) {
48,467✔
401
        /* Success. Set *pp_vt and return */
402
        auto loose_p_vt = p_vt.release();
48,466✔
403
        *pp_vt = &loose_p_vt->base;
48,466✔
404
        log_debug("creating log format table: %s = %p", argv[3], loose_p_vt);
48,466✔
405
    } else {
406
        log_error("sqlite3_declare_vtab(%s) failed: %s",
1✔
407
                  p_vt->vi->get_name().c_str(),
408
                  sqlite3_errmsg(db));
409
    }
410

411
    return rc;
48,467✔
412
}
48,467✔
413

414
static int
415
vt_destructor(sqlite3_vtab* p_svt)
48,466✔
416
{
417
    log_vtab* p_vt = (log_vtab*) p_svt;
48,466✔
418

419
    log_debug("deleting log format table: %p", p_vt);
48,466✔
420

421
    delete p_vt;
48,466✔
422

423
    return SQLITE_OK;
48,466✔
424
}
425

426
static int
427
vt_connect(sqlite3* db,
23✔
428
           void* p_aux,
429
           int argc,
430
           const char* const* argv,
431
           sqlite3_vtab** pp_vt,
432
           char** pzErr)
433
{
434
    return vt_create(db, p_aux, argc, argv, pp_vt, pzErr);
23✔
435
}
436

437
static int
438
vt_disconnect(sqlite3_vtab* pVtab)
23✔
439
{
440
    return vt_destructor(pVtab);
23✔
441
}
442

443
static int
444
vt_destroy(sqlite3_vtab* p_vt)
48,443✔
445
{
446
    return vt_destructor(p_vt);
48,443✔
447
}
448

449
static int vt_next(sqlite3_vtab_cursor* cur);
450

451
static int
452
vt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor)
181✔
453
{
454
    log_vtab* p_vt = (log_vtab*) p_svt;
181✔
455

456
    p_vt->base.zErrMsg = nullptr;
181✔
457

458
    vtab_cursor* p_cur = new vtab_cursor();
181✔
459

460
    *pp_cursor = (sqlite3_vtab_cursor*) p_cur;
181✔
461

462
    p_cur->base.pVtab = p_svt;
181✔
463
    p_cur->log_cursor.lc_opid = std::nullopt;
181✔
464
    p_cur->log_cursor.lc_curr_line = 0_vl;
181✔
465
    p_cur->log_cursor.lc_direction = 1_vl;
181✔
466
    p_cur->log_cursor.lc_end_line = vis_line_t(p_vt->lss->text_line_count());
181✔
467
    p_cur->log_cursor.lc_sub_index = 0;
181✔
468

469
    for (auto& ld : *p_vt->lss) {
373✔
470
        auto* lf = ld->get_file_ptr();
192✔
471

472
        if (lf == nullptr) {
192✔
UNCOV
473
            continue;
×
474
        }
475

476
        lf->enable_cache();
192✔
477
    }
478

479
    return SQLITE_OK;
181✔
480
}
481

482
static int
483
vt_close(sqlite3_vtab_cursor* cur)
181✔
484
{
485
    auto* p_cur = (vtab_cursor*) cur;
181✔
486

487
    /* Free cursor struct. */
488
    delete p_cur;
181✔
489

490
    return SQLITE_OK;
181✔
491
}
492

493
static int
494
vt_eof(sqlite3_vtab_cursor* cur)
33,971✔
495
{
496
    auto* vc = (vtab_cursor*) cur;
33,971✔
497

498
    return vc->log_cursor.is_eof();
33,971✔
499
}
500

501
static void
502
populate_indexed_columns(vtab_cursor* vc, log_vtab* vt)
33,616✔
503
{
504
    if (vc->log_cursor.is_eof() || vc->log_cursor.lc_indexed_columns.empty()) {
33,616✔
505
        return;
2,803✔
506
    }
507

508
    logfile* lf = nullptr;
30,813✔
509

510
    for (const auto& ic : vc->log_cursor.lc_indexed_columns) {
3,254,460✔
511
        auto& ci = vt->vi->vi_column_indexes[ic.cc_column];
3,223,647✔
512
        const auto vl = vc->log_cursor.lc_curr_line;
3,223,647✔
513

514
        if (ci.ci_indexed_range.contains(vl)) {
3,223,647✔
515
            // the index already contains this column, nothing to do
516
            continue;
3,222,558✔
517
        }
518

519
        if (lf == nullptr) {
2,123✔
520
            const auto cl = vt->lss->at(vl);
2,123✔
521
            uint64_t line_number;
522
            auto ld = vt->lss->find_data(cl, line_number);
2,123✔
523
            lf = (*ld)->get_file_ptr();
2,123✔
524
            auto ll = lf->begin() + line_number;
2,123✔
525

526
            vc->cache_msg(lf, ll);
2,123✔
527
            require(vc->line_values.lvv_sbr.get_data() != nullptr);
2,123✔
528
            vt->vi->extract(lf, line_number, vc->attrs, vc->line_values);
2,123✔
529
        }
530

531
        auto sub_col = logline_value_meta::table_column{
532
            (size_t) (ic.cc_column - VT_COL_MAX)};
2,123✔
533
        auto lv_iter = find_if(vc->line_values.lvv_values.begin(),
2,123✔
534
                               vc->line_values.lvv_values.end(),
535
                               logline_value_col_eq(sub_col));
536
        if (lv_iter == vc->line_values.lvv_values.end()
2,123✔
537
            || lv_iter->lv_meta.lvm_kind == value_kind_t::VALUE_NULL)
2,123✔
538
        {
539
            continue;
1,034✔
540
        }
541

542
        auto value = lv_iter->to_string_fragment(ci.ci_string_arena);
1,089✔
543

544
#ifdef DEBUG_INDEXING
545
        log_debug("updated index for column %d %.*s -> %d",
546
                  ic.cc_column,
547
                  value.length(),
548
                  value.data(),
549
                  (int) vc->log_cursor.lc_curr_line);
550
#endif
551

552
        auto& line_deq = ci.ci_value_to_lines[value];
1,089✔
553
        if (line_deq.empty()
1,089✔
554
            || (line_deq.front() != vl && line_deq.back() != vl))
1,089✔
555
        {
556
            if (vc->log_cursor.lc_direction < 0) {
1,089✔
557
                line_deq.push_front(vl);
774✔
558
            } else {
559
                line_deq.push_back(vl);
315✔
560
            }
561
        }
562
    }
563
}
564

565
static int
566
vt_next(sqlite3_vtab_cursor* cur)
33,802✔
567
{
568
    auto* vc = (vtab_cursor*) cur;
33,802✔
569
    auto* vt = (log_vtab*) cur->pVtab;
33,802✔
570
    auto done = false;
33,802✔
571

572
#ifdef DEBUG_INDEXING
573
    log_debug("vt_next([%d:%d:%d])",
574
              vc->log_cursor.lc_curr_line,
575
              vc->log_cursor.lc_end_line,
576
              vc->log_cursor.lc_direction);
577
#endif
578

579
    vc->invalidate();
33,802✔
580
    if (!vc->log_cursor.lc_indexed_lines.empty()
33,802✔
581
        && vc->log_cursor.lc_indexed_lines_range.contains(
33,802✔
582
            vc->log_cursor.lc_curr_line))
583
    {
584
        vc->log_cursor.lc_curr_line = vc->log_cursor.lc_indexed_lines.back();
19,534✔
585
        vc->log_cursor.lc_indexed_lines.pop_back();
19,534✔
586
    } else {
587
        vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
14,268✔
588
    }
589
    vc->log_cursor.lc_sub_index = 0;
33,802✔
590
    do {
591
        log_cursor_latest = vc->log_cursor;
54,234✔
592
        if (((log_cursor_latest.lc_curr_line % 1024) == 0)
54,234✔
593
            && (log_vtab_data.lvd_progress != nullptr
54,550✔
594
                && log_vtab_data.lvd_progress(log_cursor_latest)))
316✔
595
        {
UNCOV
596
            break;
×
597
        }
598

599
        while (vc->log_cursor.lc_curr_line != -1_vl && !vc->log_cursor.is_eof()
109,184✔
600
               && !vt->vi->is_valid(vc->log_cursor, *vt->lss))
109,184✔
601
        {
602
            vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
359✔
603
            vc->log_cursor.lc_sub_index = 0;
359✔
604
        }
605
        if (vc->log_cursor.is_eof()) {
54,234✔
606
            log_info("vt_next at EOF (%d:%d:%d), scanned rows %lu",
355✔
607
                     vc->log_cursor.lc_curr_line,
608
                     vc->log_cursor.lc_end_line,
609
                     vc->log_cursor.lc_direction,
610
                     vc->log_cursor.lc_scanned_rows);
611
            done = true;
355✔
612
        } else {
613
            done = vt->vi->next(vc->log_cursor, *vt->lss);
53,879✔
614
            if (done) {
53,879✔
615
                if (vc->log_cursor.lc_curr_line % 10000 == 0) {
33,447✔
616
                    log_debug("scanned %d", vc->log_cursor.lc_curr_line);
305✔
617
                }
618
#ifdef DEBUG_INDEXING
619
                log_debug("scanned %d", vc->log_cursor.lc_curr_line);
620
#endif
621
                vc->log_cursor.lc_scanned_rows += 1;
33,447✔
622
                populate_indexed_columns(vc, vt);
33,447✔
623
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
33,447✔
624
                                          vc->log_cursor.lc_curr_line);
625
            } else {
626
                if (!vc->log_cursor.lc_indexed_lines.empty()
20,432✔
627
                    && vc->log_cursor.lc_indexed_lines_range.contains(
20,432✔
628
                        vc->log_cursor.lc_curr_line))
629
                {
630
                    vc->log_cursor.lc_curr_line
UNCOV
631
                        = vc->log_cursor.lc_indexed_lines.back();
×
UNCOV
632
                    vc->log_cursor.lc_indexed_lines.pop_back();
×
633
                } else {
634
                    vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
20,432✔
635
                }
636
                vc->log_cursor.lc_sub_index = 0;
20,432✔
637
            }
638
        }
639
    } while (!done);
54,234✔
640

641
#ifdef DEBUG_INDEXING
642
    log_debug("vt_next() -> [%d:%d:%d]",
643
              vc->log_cursor.lc_curr_line,
644
              vc->log_cursor.lc_end_line,
645
              vc->log_cursor.lc_direction);
646
#endif
647

648
    return SQLITE_OK;
33,802✔
649
}
650

651
static int
652
vt_next_no_rowid(sqlite3_vtab_cursor* cur)
169✔
653
{
654
    auto* vc = (vtab_cursor*) cur;
169✔
655
    auto* vt = (log_vtab*) cur->pVtab;
169✔
656
    auto done = false;
169✔
657

658
    vc->invalidate();
169✔
659
    do {
660
        log_cursor_latest = vc->log_cursor;
760✔
661
        if (((log_cursor_latest.lc_curr_line % 1024) == 0)
760✔
662
            && (log_vtab_data.lvd_progress != nullptr
792✔
663
                && log_vtab_data.lvd_progress(log_cursor_latest)))
32✔
664
        {
UNCOV
665
            break;
×
666
        }
667

668
        auto vl_before = vc->log_cursor.lc_curr_line;
760✔
669
        done = vt->vi->next(vc->log_cursor, *vt->lss);
760✔
670
        if (vl_before != vc->log_cursor.lc_curr_line) {
760✔
UNCOV
671
            vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
×
672
                                      vl_before);
673
        }
674
        if (done) {
760✔
675
            populate_indexed_columns(vc, vt);
169✔
676
        } else if (vc->log_cursor.is_eof()) {
591✔
UNCOV
677
            done = true;
×
678
        } else {
679
            require(vc->log_cursor.lc_curr_line
591✔
680
                    < (ssize_t) vt->lss->text_line_count());
681

682
            if (!vc->log_cursor.lc_indexed_lines.empty()
591✔
683
                && vc->log_cursor.lc_indexed_lines_range.contains(
591✔
684
                    vc->log_cursor.lc_curr_line))
685
            {
686
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
3✔
687
                                          vc->log_cursor.lc_curr_line);
688
                vc->log_cursor.lc_curr_line
689
                    = vc->log_cursor.lc_indexed_lines.back();
3✔
690
                vc->log_cursor.lc_indexed_lines.pop_back();
3✔
691
#ifdef DEBUG_INDEXING
692
                log_debug("going to next line from index %d",
693
                          (int) vc->log_cursor.lc_curr_line);
694
#endif
695
            } else {
696
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
588✔
697
                                          vc->log_cursor.lc_curr_line);
698
                vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
588✔
699
            }
700
            vc->log_cursor.lc_sub_index = 0;
591✔
701
        }
702
    } while (!done);
760✔
703

704
#ifdef DEBUG_INDEXING
705
    log_debug("vt_next_no_rowid() -> %d:%d",
706
              vc->log_cursor.lc_curr_line,
707
              vc->log_cursor.lc_end_line);
708
#endif
709

710
    return SQLITE_OK;
169✔
711
}
712

713
static int
714
vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
83,422✔
715
{
716
    auto* vc = (vtab_cursor*) cur;
83,422✔
717
    auto* vt = (log_vtab*) cur->pVtab;
83,422✔
718

719
#ifdef DEBUG_INDEXING
720
    log_debug("vt_column(%s, %d:%d)",
721
              vt->vi->get_name().get(),
722
              (int) vc->log_cursor.lc_curr_line,
723
              col);
724
#endif
725

726
    content_line_t cl(vt->lss->at(vc->log_cursor.lc_curr_line));
83,422✔
727
    uint64_t line_number;
728
    auto ld = vt->lss->find_data(cl, line_number);
83,422✔
729
    auto* lf = (*ld)->get_file_ptr();
83,422✔
730
    auto ll = lf->begin() + line_number;
83,422✔
731

732
    require(col >= 0);
83,422✔
733

734
    /* Just return the ordinal of the column requested. */
735
    switch (col) {
83,422✔
736
        case VT_COL_LINE_NUMBER: {
2,576✔
737
            sqlite3_result_int64(ctx, vc->log_cursor.lc_curr_line);
2,576✔
738
            break;
2,576✔
739
        }
740

741
        case VT_COL_LOG_TIME: {
354✔
742
            char buffer[64];
743

744
            sql_strftime(buffer, sizeof(buffer), ll->get_timeval());
354✔
745
            sqlite3_result_text(ctx, buffer, strlen(buffer), SQLITE_TRANSIENT);
354✔
746
            break;
354✔
747
        }
748

749
        case VT_COL_LEVEL: {
1,271✔
750
            const auto& level_name = ll->get_level_name();
1,271✔
751

752
            sqlite3_result_text(
1,271✔
753
                ctx, level_name.data(), level_name.length(), SQLITE_STATIC);
754
            break;
1,271✔
755
        }
756

757
        default:
79,221✔
758
            if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
79,221✔
759
                auto footer_column = static_cast<log_footer_columns>(
760
                    col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
4,689✔
761

762
                switch (footer_column) {
4,689✔
763
                    case log_footer_columns::partition: {
364✔
764
                        auto& vb = vt->tc->get_bookmarks();
364✔
765
                        const auto& bv = vb[&textview_curses::BM_PARTITION];
364✔
766

767
                        if (bv.empty()) {
364✔
768
                            sqlite3_result_null(ctx);
267✔
769
                        } else {
770
                            vis_line_t curr_line(vc->log_cursor.lc_curr_line);
97✔
771
                            auto iter
772
                                = bv.bv_tree.lower_bound(curr_line + 1_vl);
97✔
773

774
                            if (iter != bv.bv_tree.begin()) {
97✔
775
                                --iter;
69✔
776
                                auto line_meta_opt
777
                                    = vt->lss->find_bookmark_metadata(*iter);
69✔
778
                                if (line_meta_opt
69✔
779
                                    && !line_meta_opt.value()->bm_name.empty())
69✔
780
                                {
781
                                    sqlite3_result_text(
69✔
782
                                        ctx,
783
                                        line_meta_opt.value()->bm_name.c_str(),
69✔
784
                                        line_meta_opt.value()->bm_name.size(),
69✔
785
                                        SQLITE_TRANSIENT);
786
                                } else {
UNCOV
787
                                    sqlite3_result_null(ctx);
×
788
                                }
789
                            } else {
790
                                sqlite3_result_null(ctx);
28✔
791
                            }
792
                        }
793
                        break;
364✔
794
                    }
795
                    case log_footer_columns::actual_time: {
58✔
796
                        char buffer[64] = "";
58✔
797

798
                        if (ll->is_time_skewed()) {
58✔
799
                            if (vc->line_values.lvv_values.empty()) {
2✔
800
                                vc->cache_msg(lf, ll);
2✔
801
                                require(vc->line_values.lvv_sbr.get_data()
2✔
802
                                        != nullptr);
803
                                vt->vi->extract(lf,
2✔
804
                                                line_number,
805
                                                vc->attrs,
2✔
806
                                                vc->line_values);
2✔
807
                            }
808

809
                            struct line_range time_range;
2✔
810

811
                            time_range = find_string_attr_range(vc->attrs,
2✔
812
                                                                &L_TIMESTAMP);
813

814
                            const auto* time_src
815
                                = vc->line_values.lvv_sbr.get_data()
2✔
816
                                + time_range.lr_start;
2✔
817
                            struct timeval actual_tv;
818
                            struct exttm tm;
2✔
819

820
                            if (lf->get_format_ptr()->lf_date_time.scan(
4✔
821
                                    time_src,
822
                                    time_range.length(),
2✔
823
                                    lf->get_format_ptr()
824
                                        ->get_timestamp_formats(),
825
                                    &tm,
826
                                    actual_tv,
827
                                    false))
828
                            {
829
                                sql_strftime(buffer, sizeof(buffer), actual_tv);
2✔
830
                            }
831
                        } else {
832
                            sql_strftime(
56✔
833
                                buffer, sizeof(buffer), ll->get_timeval());
112✔
834
                        }
835
                        sqlite3_result_text(
58✔
836
                            ctx, buffer, strlen(buffer), SQLITE_TRANSIENT);
58✔
837
                        break;
58✔
838
                    }
839
                    case log_footer_columns::idle_msecs: {
263✔
840
                        if (vc->log_cursor.lc_curr_line == 0) {
263✔
841
                            sqlite3_result_int64(ctx, 0);
52✔
842
                        } else {
843
                            content_line_t prev_cl(vt->lss->at(
211✔
844
                                vis_line_t(vc->log_cursor.lc_curr_line - 1)));
211✔
845
                            auto prev_lf = vt->lss->find(prev_cl);
211✔
846
                            auto prev_ll = prev_lf->begin() + prev_cl;
211✔
847

848
                            auto prev_time
849
                                = prev_ll
850
                                      ->get_time<std::chrono::milliseconds>();
211✔
851
                            auto curr_line_time
852
                                = ll->get_time<std::chrono::milliseconds>();
211✔
853
                            // require(curr_line_time >= prev_time);
854
                            sqlite3_result_int64(
211✔
855
                                ctx,
856
                                curr_line_time.count() - prev_time.count());
211✔
857
                        }
211✔
858
                        break;
263✔
859
                    }
860
                    case log_footer_columns::mark: {
261✔
861
                        sqlite3_result_int(ctx, ll->is_marked());
261✔
862
                        break;
261✔
863
                    }
864
                    case log_footer_columns::comment: {
272✔
865
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
272✔
866
                            vc->log_cursor.lc_curr_line);
867
                        if (!line_meta_opt
272✔
868
                            || line_meta_opt.value()->bm_comment.empty())
272✔
869
                        {
870
                            sqlite3_result_null(ctx);
270✔
871
                        } else {
872
                            const auto& meta = *(line_meta_opt.value());
2✔
873
                            sqlite3_result_text(ctx,
2✔
874
                                                meta.bm_comment.c_str(),
875
                                                meta.bm_comment.length(),
2✔
876
                                                SQLITE_TRANSIENT);
877
                        }
878
                        break;
272✔
879
                    }
880
                    case log_footer_columns::tags: {
667✔
881
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
667✔
882
                            vc->log_cursor.lc_curr_line);
883
                        if (!line_meta_opt
667✔
884
                            || line_meta_opt.value()->bm_tags.empty())
667✔
885
                        {
886
                            sqlite3_result_null(ctx);
660✔
887
                        } else {
888
                            const auto& meta = *(line_meta_opt.value());
7✔
889

890
                            yajlpp_gen gen;
7✔
891

892
                            yajl_gen_config(gen, yajl_gen_beautify, false);
7✔
893

894
                            {
895
                                yajlpp_array arr(gen);
7✔
896

897
                                for (const auto& str : meta.bm_tags) {
14✔
898
                                    arr.gen(str);
7✔
899
                                }
900
                            }
7✔
901

902
                            to_sqlite(ctx, json_string(gen));
7✔
903
                        }
7✔
904
                        break;
667✔
905
                    }
906
                    case log_footer_columns::annotations: {
275✔
907
                        if (sqlite3_vtab_nochange(ctx)) {
275✔
908
                            return SQLITE_OK;
39✔
909
                        }
910

911
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
236✔
912
                            vc->log_cursor.lc_curr_line);
913
                        if (!line_meta_opt
236✔
914
                            || line_meta_opt.value()
240✔
915
                                   ->bm_annotations.la_pairs.empty())
4✔
916
                        {
917
                            sqlite3_result_null(ctx);
232✔
918
                        } else {
919
                            const auto& meta = *(line_meta_opt.value());
4✔
920
                            to_sqlite(
4✔
921
                                ctx,
922
                                logmsg_annotations_handlers.to_json_string(
4✔
923
                                    meta.bm_annotations));
4✔
924
                        }
925
                        break;
236✔
926
                    }
927
                    case log_footer_columns::filters: {
263✔
928
                        const auto& filter_mask
929
                            = (*ld)->ld_filter_state.lfo_filter_state.tfs_mask;
263✔
930

931
                        if (!filter_mask[line_number]) {
263✔
932
                            sqlite3_result_null(ctx);
262✔
933
                        } else {
934
                            const auto& filters = vt->lss->get_filters();
1✔
935
                            yajlpp_gen gen;
1✔
936

937
                            yajl_gen_config(gen, yajl_gen_beautify, false);
1✔
938

939
                            {
940
                                yajlpp_array arr(gen);
1✔
941

942
                                for (const auto& filter : filters) {
2✔
943
                                    if (filter->lf_deleted) {
1✔
UNCOV
944
                                        continue;
×
945
                                    }
946

947
                                    uint32_t mask
948
                                        = (1UL << filter->get_index());
1✔
949

950
                                    if (filter_mask[line_number] & mask) {
1✔
951
                                        arr.gen(filter->get_index());
1✔
952
                                    }
953
                                }
954
                            }
1✔
955

956
                            to_sqlite(ctx, gen.to_string_fragment());
1✔
957
                            sqlite3_result_subtype(ctx, JSON_SUBTYPE);
1✔
958
                        }
1✔
959
                        break;
263✔
960
                    }
961
                    case log_footer_columns::opid: {
37✔
962
                        if (vc->line_values.lvv_values.empty()) {
37✔
963
                            vc->cache_msg(lf, ll);
8✔
964
                            require(vc->line_values.lvv_sbr.get_data()
8✔
965
                                    != nullptr);
966
                            vt->vi->extract(
8✔
967
                                lf, line_number, vc->attrs, vc->line_values);
8✔
968
                        }
969

970
                        if (vc->line_values.lvv_opid_value) {
37✔
971
                            to_sqlite(ctx,
31✔
972
                                      vc->line_values.lvv_opid_value.value());
31✔
973
                        } else {
974
                            sqlite3_result_null(ctx);
6✔
975
                        }
976
                        break;
37✔
977
                    }
978
                    case log_footer_columns::user_opid: {
55✔
979
                        if (vc->line_values.lvv_values.empty()) {
55✔
980
                            vc->cache_msg(lf, ll);
11✔
981
                            require(vc->line_values.lvv_sbr.get_data()
11✔
982
                                    != nullptr);
983
                            vt->vi->extract(
11✔
984
                                lf, line_number, vc->attrs, vc->line_values);
11✔
985
                        }
986

987
                        if (vc->line_values.lvv_opid_value
55✔
988
                            && vc->line_values.lvv_opid_provenance
55✔
989
                                == logline_value_vector::opid_provenance::user)
55✔
990
                        {
991
                            to_sqlite(ctx,
×
992
                                      vc->line_values.lvv_opid_value.value());
×
993
                        } else {
994
                            sqlite3_result_null(ctx);
55✔
995
                        }
996
                        break;
55✔
997
                    }
998
                    case log_footer_columns::format: {
68✔
999
                        auto format_name = lf->get_format_name();
68✔
1000
                        sqlite3_result_text(ctx,
68✔
1001
                                            format_name.get(),
1002
                                            format_name.size(),
68✔
1003
                                            SQLITE_STATIC);
1004
                        break;
68✔
1005
                    }
1006
                    case log_footer_columns::format_regex: {
45✔
1007
                        auto pat_name = lf->get_format_ptr()->get_pattern_name(
45✔
1008
                            line_number);
1009
                        sqlite3_result_text(ctx,
45✔
1010
                                            pat_name.get(),
1011
                                            pat_name.size(),
45✔
1012
                                            SQLITE_STATIC);
1013
                        break;
45✔
1014
                    }
1015
                    case log_footer_columns::time_msecs: {
1,444✔
1016
                        sqlite3_result_int64(
1,444✔
1017
                            ctx,
1018
                            ll->get_time<std::chrono::milliseconds>().count());
1,444✔
1019
                        break;
1,444✔
1020
                    }
1021
                    case log_footer_columns::path: {
49✔
1022
                        const auto& fn = lf->get_filename();
49✔
1023

1024
                        sqlite3_result_text(ctx,
49✔
1025
                                            fn.c_str(),
1026
                                            fn.native().length(),
49✔
1027
                                            SQLITE_STATIC);
1028
                        break;
49✔
1029
                    }
1030
                    case log_footer_columns::unique_path: {
49✔
1031
                        const auto& fn = lf->get_unique_path();
49✔
1032

1033
                        sqlite3_result_text(ctx,
49✔
1034
                                            fn.c_str(),
1035
                                            fn.native().length(),
49✔
1036
                                            SQLITE_STATIC);
1037
                        break;
49✔
1038
                    }
1039
                    case log_footer_columns::text: {
51✔
1040
                        shared_buffer_ref line;
51✔
1041

1042
                        lf->read_full_message(ll, line);
51✔
1043
                        line.erase_ansi();
51✔
1044
                        sqlite3_result_text(ctx,
51✔
1045
                                            line.get_data(),
1046
                                            line.length(),
51✔
1047
                                            SQLITE_TRANSIENT);
1048
                        break;
51✔
1049
                    }
51✔
1050
                    case log_footer_columns::body: {
210✔
1051
                        if (vc->line_values.lvv_values.empty()) {
210✔
1052
                            vc->cache_msg(lf, ll);
110✔
1053
                            require(vc->line_values.lvv_sbr.get_data()
110✔
1054
                                    != nullptr);
1055
                            vt->vi->extract(
110✔
1056
                                lf, line_number, vc->attrs, vc->line_values);
110✔
1057
                        }
1058

1059
                        auto body_range
1060
                            = find_string_attr_range(vc->attrs, &SA_BODY);
210✔
1061
                        if (!body_range.is_valid()) {
210✔
UNCOV
1062
                            sqlite3_result_null(ctx);
×
1063
                        } else {
1064
                            const char* msg_start
1065
                                = vc->line_values.lvv_sbr.get_data();
210✔
1066

1067
                            sqlite3_result_text(ctx,
210✔
1068
                                                &msg_start[body_range.lr_start],
210✔
1069
                                                body_range.length(),
1070
                                                SQLITE_TRANSIENT);
1071
                        }
1072
                        break;
210✔
1073
                    }
1074
                    case log_footer_columns::raw_text: {
58✔
1075
                        auto read_res = lf->read_raw_message(ll);
58✔
1076

1077
                        if (read_res.isErr()) {
58✔
1078
                            auto msg = fmt::format(
UNCOV
1079
                                FMT_STRING("unable to read line -- {}"),
×
UNCOV
1080
                                read_res.unwrapErr());
×
UNCOV
1081
                            sqlite3_result_error(
×
UNCOV
1082
                                ctx, msg.c_str(), msg.length());
×
UNCOV
1083
                        } else {
×
1084
                            auto sbr = read_res.unwrap();
58✔
1085

1086
                            sqlite3_result_text(ctx,
58✔
1087
                                                sbr.get_data(),
1088
                                                sbr.length(),
58✔
1089
                                                SQLITE_TRANSIENT);
1090
                        }
58✔
1091
                        break;
58✔
1092
                    }
58✔
1093
                    case log_footer_columns::line_hash: {
50✔
1094
                        auto lw
1095
                            = vt->lss->window_at(vc->log_cursor.lc_curr_line);
50✔
1096
                        for (const auto& li : lw) {
50✔
1097
                            auto hash_res = li.get_line_hash();
50✔
1098
                            if (hash_res.isErr()) {
50✔
1099
                                auto msg = fmt::format(
UNCOV
1100
                                    FMT_STRING("unable to read line -- {}"),
×
1101
                                    hash_res.unwrapErr());
×
UNCOV
1102
                                sqlite3_result_error(
×
UNCOV
1103
                                    ctx, msg.c_str(), msg.length());
×
UNCOV
1104
                            } else {
×
1105
                                to_sqlite(ctx,
50✔
1106
                                          text_auto_buffer{hash_res.unwrap()});
100✔
1107
                            }
1108
                            break;
50✔
1109
                        }
100✔
1110
                        break;
50✔
1111
                    }
1112
                    case log_footer_columns::line_link: {
50✔
1113
                        auto anchor_opt = vt->lss->anchor_for_row(
50✔
1114
                            vc->log_cursor.lc_curr_line);
50✔
1115
                        if (anchor_opt) {
50✔
1116
                            to_sqlite(ctx, anchor_opt.value());
50✔
1117
                        } else {
UNCOV
1118
                            sqlite3_result_null(ctx);
×
1119
                        }
1120
                        break;
50✔
1121
                    }
50✔
1122
                    case log_footer_columns::src_file: {
50✔
1123
                        if (vc->line_values.lvv_values.empty()) {
50✔
1124
                            vc->cache_msg(lf, ll);
7✔
1125
                            require(vc->line_values.lvv_sbr.get_data()
7✔
1126
                                    != nullptr);
1127
                            vt->vi->extract(
7✔
1128
                                lf, line_number, vc->attrs, vc->line_values);
7✔
1129
                        }
1130

1131
                        auto src_range
1132
                            = find_string_attr_range(vc->attrs, &SA_SRC_FILE);
50✔
1133
                        if (!src_range.is_valid()) {
50✔
1134
                            sqlite3_result_null(ctx);
34✔
1135
                        } else {
1136
                            const char* msg_start
1137
                                = vc->line_values.lvv_sbr.get_data();
16✔
1138

1139
                            sqlite3_result_text(ctx,
16✔
1140
                                                &msg_start[src_range.lr_start],
16✔
1141
                                                src_range.length(),
1142
                                                SQLITE_TRANSIENT);
1143
                        }
1144
                        break;
50✔
1145
                    }
1146
                    case log_footer_columns::src_line: {
50✔
1147
                        if (vc->line_values.lvv_values.empty()) {
50✔
NEW
1148
                            vc->cache_msg(lf, ll);
×
NEW
1149
                            require(vc->line_values.lvv_sbr.get_data()
×
1150
                                    != nullptr);
NEW
1151
                            vt->vi->extract(
×
NEW
1152
                                lf, line_number, vc->attrs, vc->line_values);
×
1153
                        }
1154

1155
                        auto src_range
1156
                            = find_string_attr_range(vc->attrs, &SA_SRC_LINE);
50✔
1157
                        if (!src_range.is_valid()) {
50✔
1158
                            sqlite3_result_null(ctx);
34✔
1159
                        } else {
1160
                            const char* msg_start
1161
                                = vc->line_values.lvv_sbr.get_data();
16✔
1162

1163
                            sqlite3_result_text(ctx,
16✔
1164
                                                &msg_start[src_range.lr_start],
16✔
1165
                                                src_range.length(),
1166
                                                SQLITE_TRANSIENT);
1167
                        }
1168
                        break;
50✔
1169
                    }
1170
                }
1171
            } else {
1172
                if (vc->line_values.lvv_values.empty()) {
74,532✔
1173
                    vc->cache_msg(lf, ll);
30,837✔
1174
                    require(vc->line_values.lvv_sbr.get_data() != nullptr);
30,837✔
1175
                    vt->vi->extract(
30,837✔
1176
                        lf, line_number, vc->attrs, vc->line_values);
30,837✔
1177
                }
1178

1179
                auto sub_col = logline_value_meta::table_column{
1180
                    (size_t) (col - VT_COL_MAX)};
74,532✔
1181
                auto lv_iter = find_if(vc->line_values.lvv_values.begin(),
74,532✔
1182
                                       vc->line_values.lvv_values.end(),
1183
                                       logline_value_col_eq(sub_col));
1184

1185
                if (lv_iter != vc->line_values.lvv_values.end()) {
74,532✔
1186
                    if (!lv_iter->lv_meta.lvm_struct_name.empty()) {
74,140✔
1187
                        yajlpp_gen gen;
8✔
1188
                        yajl_gen_config(gen, yajl_gen_beautify, false);
8✔
1189

1190
                        {
1191
                            yajlpp_map root(gen);
8✔
1192

1193
                            for (const auto& lv_struct :
8✔
1194
                                 vc->line_values.lvv_values)
82✔
1195
                            {
1196
                                if (lv_struct.lv_meta.lvm_column != sub_col) {
66✔
1197
                                    continue;
39✔
1198
                                }
1199

1200
                                root.gen(lv_struct.lv_meta.lvm_name);
27✔
1201
                                switch (lv_struct.lv_meta.lvm_kind) {
27✔
1202
                                    case value_kind_t::VALUE_NULL:
2✔
1203
                                        root.gen();
2✔
1204
                                        break;
2✔
UNCOV
1205
                                    case value_kind_t::VALUE_BOOLEAN:
×
UNCOV
1206
                                        root.gen((bool) lv_struct.lv_value.i);
×
UNCOV
1207
                                        break;
×
UNCOV
1208
                                    case value_kind_t::VALUE_INTEGER:
×
1209
                                        root.gen(lv_struct.lv_value.i);
×
UNCOV
1210
                                        break;
×
UNCOV
1211
                                    case value_kind_t::VALUE_FLOAT:
×
UNCOV
1212
                                        root.gen(lv_struct.lv_value.d);
×
UNCOV
1213
                                        break;
×
1214
                                    case value_kind_t::VALUE_JSON: {
1✔
1215
                                        auto_mem<yajl_handle_t> parse_handle(
1216
                                            yajl_free);
1✔
1217
                                        json_ptr jp("");
1✔
1218
                                        json_op jo(jp);
1✔
1219

1220
                                        jo.jo_ptr_callbacks
1221
                                            = json_op::gen_callbacks;
1✔
1222
                                        jo.jo_ptr_data = gen;
1✔
1223
                                        parse_handle.reset(
1✔
1224
                                            yajl_alloc(&json_op::ptr_callbacks,
1225
                                                       nullptr,
1226
                                                       &jo));
1227

1228
                                        const auto* json_in
1229
                                            = (const unsigned char*)
1230
                                                  lv_struct.text_value();
1✔
1231
                                        auto json_len = lv_struct.text_length();
1✔
1232

1233
                                        if (yajl_parse(parse_handle.in(),
1✔
1234
                                                       json_in,
1235
                                                       json_len)
1236
                                                != yajl_status_ok
1237
                                            || yajl_complete_parse(
1✔
1238
                                                   parse_handle.in())
1239
                                                != yajl_status_ok)
1240
                                        {
UNCOV
1241
                                            log_error(
×
1242
                                                "failed to parse json value: "
1243
                                                "%.*s",
1244
                                                lv_struct.text_length(),
1245
                                                lv_struct.text_value());
UNCOV
1246
                                            root.gen(lv_struct.to_string());
×
1247
                                        }
1248
                                        break;
1✔
1249
                                    }
1✔
1250
                                    default:
24✔
1251
                                        root.gen(lv_struct.to_string());
24✔
1252
                                        break;
24✔
1253
                                }
1254
                            }
1255
                        }
8✔
1256

1257
                        auto sf = gen.to_string_fragment();
8✔
1258
                        sqlite3_result_text(
8✔
1259
                            ctx, sf.data(), sf.length(), SQLITE_TRANSIENT);
1260
                        sqlite3_result_subtype(ctx, JSON_SUBTYPE);
8✔
1261
                    } else {
8✔
1262
                        switch (lv_iter->lv_meta.lvm_kind) {
74,132✔
1263
                            case value_kind_t::VALUE_NULL:
2,465✔
1264
                                sqlite3_result_null(ctx);
2,465✔
1265
                                break;
2,465✔
1266
                            case value_kind_t::VALUE_JSON: {
42✔
1267
                                sqlite3_result_text(ctx,
42✔
1268
                                                    lv_iter->text_value(),
1269
                                                    lv_iter->text_length(),
42✔
1270
                                                    SQLITE_TRANSIENT);
1271
                                sqlite3_result_subtype(ctx, JSON_SUBTYPE);
42✔
1272
                                break;
42✔
1273
                            }
1274
                            case value_kind_t::VALUE_STRUCT:
68,184✔
1275
                            case value_kind_t::VALUE_TEXT:
1276
                            case value_kind_t::VALUE_XML:
1277
                            case value_kind_t::VALUE_TIMESTAMP: {
1278
                                sqlite3_result_text(ctx,
68,184✔
1279
                                                    lv_iter->text_value(),
1280
                                                    lv_iter->text_length(),
68,184✔
1281
                                                    SQLITE_TRANSIENT);
1282
                                break;
68,184✔
1283
                            }
1284
                            case value_kind_t::VALUE_W3C_QUOTED:
20✔
1285
                            case value_kind_t::VALUE_QUOTED:
1286
                                if (lv_iter->text_length() == 0) {
20✔
UNCOV
1287
                                    sqlite3_result_text(
×
1288
                                        ctx, "", 0, SQLITE_STATIC);
1289
                                } else {
1290
                                    const char* text_value
1291
                                        = lv_iter->text_value();
20✔
1292
                                    size_t text_len = lv_iter->text_length();
20✔
1293

1294
                                    switch (text_value[0]) {
20✔
1295
                                        case '\'':
7✔
1296
                                        case '"': {
1297
                                            char* val = (char*) sqlite3_malloc(
7✔
1298
                                                text_len);
1299

1300
                                            if (val == nullptr) {
7✔
UNCOV
1301
                                                sqlite3_result_error_nomem(ctx);
×
1302
                                            } else {
1303
                                                auto unquote_func
1304
                                                    = lv_iter->lv_meta.lvm_kind
7✔
1305
                                                        == value_kind_t::
1306
                                                            VALUE_W3C_QUOTED
1307
                                                    ? unquote_w3c
7✔
1308
                                                    : unquote;
7✔
1309

1310
                                                size_t unquoted_len
1311
                                                    = unquote_func(val,
7✔
1312
                                                                   text_value,
1313
                                                                   text_len);
1314
                                                sqlite3_result_text(
7✔
1315
                                                    ctx,
1316
                                                    val,
1317
                                                    unquoted_len,
1318
                                                    sqlite3_free);
1319
                                            }
1320
                                            break;
7✔
1321
                                        }
1322
                                        default: {
13✔
1323
                                            sqlite3_result_text(
13✔
1324
                                                ctx,
1325
                                                text_value,
1326
                                                lv_iter->text_length(),
13✔
1327
                                                SQLITE_TRANSIENT);
1328
                                            break;
13✔
1329
                                        }
1330
                                    }
1331
                                }
1332
                                break;
20✔
1333

1334
                            case value_kind_t::VALUE_BOOLEAN:
2,834✔
1335
                            case value_kind_t::VALUE_INTEGER:
1336
                                sqlite3_result_int64(ctx, lv_iter->lv_value.i);
2,834✔
1337
                                break;
2,834✔
1338

1339
                            case value_kind_t::VALUE_FLOAT:
587✔
1340
                                sqlite3_result_double(ctx, lv_iter->lv_value.d);
587✔
1341
                                break;
587✔
1342

UNCOV
1343
                            case value_kind_t::VALUE_UNKNOWN:
×
1344
                            case value_kind_t::VALUE__MAX:
UNCOV
1345
                                require(0);
×
1346
                                break;
1347
                        }
1348
                    }
1349
                } else {
1350
                    sqlite3_result_null(ctx);
392✔
1351
                }
1352
            }
1353
            break;
79,182✔
1354
    }
1355

1356
    return SQLITE_OK;
83,383✔
1357
}
1358

1359
static int
1360
vt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid)
89✔
1361
{
1362
    vtab_cursor* p_cur = (vtab_cursor*) cur;
89✔
1363

1364
    *p_rowid = (((uint64_t) p_cur->log_cursor.lc_curr_line) << 8)
89✔
1365
        | (p_cur->log_cursor.lc_sub_index & 0xff);
89✔
1366

1367
    return SQLITE_OK;
89✔
1368
}
1369

1370
void
1371
log_cursor::update(unsigned char op, vis_line_t vl, constraint_t cons)
71✔
1372
{
1373
    switch (op) {
71✔
1374
        case SQLITE_INDEX_CONSTRAINT_EQ:
47✔
1375
            if (vl < 0_vl) {
47✔
1376
                this->lc_curr_line = this->lc_end_line;
×
1377
            } else if (vl < this->lc_end_line) {
47✔
1378
                this->lc_curr_line = vl;
42✔
1379
                if (cons == constraint_t::unique) {
42✔
1380
                    this->lc_end_line = this->lc_curr_line + 1_vl;
42✔
1381
                }
1382
            }
1383
            break;
47✔
1384
        case SQLITE_INDEX_CONSTRAINT_GE:
3✔
1385
            if (vl < 0_vl) {
3✔
UNCOV
1386
                vl = 0_vl;
×
1387
            }
1388
            this->lc_curr_line = vl;
3✔
1389
            break;
3✔
1390
        case SQLITE_INDEX_CONSTRAINT_GT:
7✔
1391
            if (vl < 0_vl) {
7✔
1392
                this->lc_curr_line = 0_vl;
2✔
1393
            } else {
1394
                this->lc_curr_line
1395
                    = vl + (cons == constraint_t::unique ? 1_vl : 0_vl);
5✔
1396
            }
1397
            break;
7✔
1398
        case SQLITE_INDEX_CONSTRAINT_LE:
4✔
1399
            if (vl < 0_vl) {
4✔
UNCOV
1400
                this->lc_curr_line = this->lc_end_line;
×
1401
            } else if (vl < this->lc_end_line) {
4✔
1402
                this->lc_end_line
1403
                    = vl + (cons == constraint_t::unique ? 1_vl : 0_vl);
2✔
1404
            }
1405
            break;
4✔
1406
        case SQLITE_INDEX_CONSTRAINT_LT:
10✔
1407
            if (vl <= 0_vl) {
10✔
1408
                this->lc_curr_line = this->lc_end_line;
2✔
1409
            } else if (this->lc_direction > 0) {
8✔
UNCOV
1410
                if (vl < this->lc_end_line) {
×
UNCOV
1411
                    this->lc_end_line = vl;
×
1412
                }
1413
            } else if (this->lc_direction < 0) {
8✔
1414
                if (vl <= this->lc_curr_line) {
8✔
1415
                    this->lc_curr_line = vl - 1_vl;
8✔
1416
                }
1417
            }
1418
            break;
10✔
1419
    }
1420
#ifdef DEBUG_INDEXING
1421
    log_debug("log_cursor::update(%s, %d) -> (%d:%d:%d)",
1422
              sql_constraint_op_name(op),
1423
              vl,
1424
              this->lc_curr_line,
1425
              this->lc_end_line,
1426
              this->lc_direction);
1427
#endif
1428
}
71✔
1429

1430
log_cursor::string_constraint::string_constraint(unsigned char op,
214✔
1431
                                                 std::string value)
214✔
1432
    : sc_op(op), sc_value(std::move(value))
214✔
1433
{
1434
    if (op == SQLITE_INDEX_CONSTRAINT_REGEXP) {
214✔
UNCOV
1435
        auto compile_res = lnav::pcre2pp::code::from(this->sc_value);
×
1436

UNCOV
1437
        if (compile_res.isErr()) {
×
UNCOV
1438
            auto ce = compile_res.unwrapErr();
×
UNCOV
1439
            log_error("unable to compile regexp constraint: %s -- %s",
×
1440
                      this->sc_value.c_str(),
1441
                      ce.get_message().c_str());
UNCOV
1442
        } else {
×
UNCOV
1443
            this->sc_pattern = compile_res.unwrap().to_shared();
×
1444
        }
1445
    }
1446
}
214✔
1447

1448
bool
1449
log_cursor::string_constraint::matches(const std::string& sf) const
4✔
1450
{
1451
    switch (this->sc_op) {
4✔
UNCOV
1452
        case SQLITE_INDEX_CONSTRAINT_EQ:
×
1453
        case SQLITE_INDEX_CONSTRAINT_IS:
UNCOV
1454
            return sf == this->sc_value;
×
UNCOV
1455
        case SQLITE_INDEX_CONSTRAINT_NE:
×
1456
        case SQLITE_INDEX_CONSTRAINT_ISNOT:
UNCOV
1457
            return sf != this->sc_value;
×
1458
        case SQLITE_INDEX_CONSTRAINT_GT:
×
UNCOV
1459
            return sf > this->sc_value;
×
UNCOV
1460
        case SQLITE_INDEX_CONSTRAINT_LE:
×
UNCOV
1461
            return sf <= this->sc_value;
×
UNCOV
1462
        case SQLITE_INDEX_CONSTRAINT_LT:
×
UNCOV
1463
            return sf < this->sc_value;
×
UNCOV
1464
        case SQLITE_INDEX_CONSTRAINT_GE:
×
UNCOV
1465
            return sf >= this->sc_value;
×
UNCOV
1466
        case SQLITE_INDEX_CONSTRAINT_LIKE:
×
UNCOV
1467
            return sqlite3_strlike(this->sc_value.c_str(), sf.data(), 0) == 0;
×
1468
        case SQLITE_INDEX_CONSTRAINT_GLOB:
4✔
1469
            return sqlite3_strglob(this->sc_value.c_str(), sf.data()) == 0;
4✔
UNCOV
1470
        case SQLITE_INDEX_CONSTRAINT_REGEXP: {
×
UNCOV
1471
            if (this->sc_pattern != nullptr) {
×
UNCOV
1472
                return this->sc_pattern->find_in(sf, PCRE2_NO_UTF_CHECK)
×
UNCOV
1473
                    .ignore_error()
×
UNCOV
1474
                    .has_value();
×
1475
            }
1476
            // return true here so that the regexp is actually run and fails
UNCOV
1477
            return true;
×
1478
        }
UNCOV
1479
        case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
×
UNCOV
1480
            return true;
×
UNCOV
1481
        default:
×
UNCOV
1482
            return false;
×
1483
    }
1484
}
1485

1486
struct vtab_time_range {
1487
    std::optional<timeval> vtr_begin;
1488
    std::optional<timeval> vtr_end;
1489

1490
    bool empty() const { return !this->vtr_begin && !this->vtr_end; }
4✔
1491

1492
    void add(const timeval& tv)
7✔
1493
    {
1494
        if (!this->vtr_begin || tv < this->vtr_begin) {
7✔
1495
            this->vtr_begin = tv;
3✔
1496
        }
1497
        if (!this->vtr_end || this->vtr_end < tv) {
7✔
1498
            this->vtr_end = tv;
6✔
1499
        }
1500
    }
7✔
1501
};
1502

1503
static int
1504
vt_filter(sqlite3_vtab_cursor* p_vtc,
384✔
1505
          int idxNum,
1506
          const char* idxStr,
1507
          int argc,
1508
          sqlite3_value** argv)
1509
{
1510
    auto* p_cur = (vtab_cursor*) p_vtc;
384✔
1511
    auto* vt = (log_vtab*) p_vtc->pVtab;
384✔
1512
    sqlite3_index_info::sqlite3_index_constraint* index = nullptr;
384✔
1513

1514
    if (idxStr != nullptr) {
384✔
1515
        auto desc_len = strlen(idxStr);
373✔
1516
        auto index_len = idxNum * sizeof(*index);
373✔
1517
        auto storage_len = desc_len + 128 + index_len;
373✔
1518
        auto direction_storage
373✔
1519
            = static_cast<const char*>(idxStr) + desc_len + 1;
373✔
1520
        p_cur->log_cursor.lc_direction = vis_line_t(direction_storage[0]);
373✔
1521
        auto* remaining_storage = const_cast<void*>(
373✔
1522
            static_cast<const void*>(idxStr + desc_len + 1 + 1));
373✔
1523
        auto* index_storage
1524
            = std::align(alignof(sqlite3_index_info::sqlite3_index_constraint),
373✔
1525
                         index_len,
1526
                         remaining_storage,
1527
                         storage_len);
1528
        index = static_cast<sqlite3_index_info::sqlite3_index_constraint*>(
373✔
1529
            index_storage);
1530
    } else {
1531
        p_cur->log_cursor.lc_direction = 1_vl;
11✔
1532
    }
1533

1534
#ifdef DEBUG_INDEXING
1535
    log_info("vt_filter(%s, %d, direction=%d)",
1536
             vt->vi->get_name().get(),
1537
             idxNum,
1538
             p_cur->log_cursor.lc_direction);
1539
    log_info("  index storage: %p", index);
1540
#endif
1541
    p_cur->log_cursor.lc_format_name.clear();
384✔
1542
    p_cur->log_cursor.lc_pattern_name.clear();
384✔
1543
    p_cur->log_cursor.lc_opid = std::nullopt;
384✔
1544
    p_cur->log_cursor.lc_level_constraint = std::nullopt;
384✔
1545
    p_cur->log_cursor.lc_log_path.clear();
384✔
1546
    p_cur->log_cursor.lc_last_log_path_match = nullptr;
384✔
1547
    p_cur->log_cursor.lc_last_log_path_mismatch = nullptr;
384✔
1548
    p_cur->log_cursor.lc_unique_path.clear();
384✔
1549
    p_cur->log_cursor.lc_last_unique_path_match = nullptr;
384✔
1550
    p_cur->log_cursor.lc_last_unique_path_mismatch = nullptr;
384✔
1551
    if (p_cur->log_cursor.lc_direction < 0) {
384✔
1552
        p_cur->log_cursor.lc_curr_line
1553
            = vis_line_t(vt->lss->text_line_count() - 1);
8✔
1554
        p_cur->log_cursor.lc_end_line = -1_vl;
8✔
1555
    } else {
1556
        p_cur->log_cursor.lc_curr_line = 0_vl;
376✔
1557
        p_cur->log_cursor.lc_end_line = vis_line_t(vt->lss->text_line_count());
376✔
1558
    }
1559
    p_cur->log_cursor.lc_scanned_rows = 0;
384✔
1560
    p_cur->log_cursor.lc_indexed_lines.clear();
384✔
1561
    p_cur->log_cursor.lc_indexed_lines_range = msg_range::empty();
384✔
1562

1563
    std::optional<vtab_time_range> log_time_range;
384✔
1564
    std::optional<uint16_t> opid_val;
384✔
1565
    std::vector<log_cursor::string_constraint> log_path_constraints;
384✔
1566
    std::vector<log_cursor::string_constraint> log_unique_path_constraints;
384✔
1567

1568
    for (int lpc = 0; lpc < idxNum; lpc++) {
681✔
1569
        auto col = index[lpc].iColumn;
297✔
1570
        auto op = index[lpc].op;
297✔
1571
        switch (col) {
297✔
1572
            case VT_COL_LINE_NUMBER: {
69✔
1573
                auto vl = vis_line_t(sqlite3_value_int64(argv[lpc]));
69✔
1574

1575
                p_cur->log_cursor.update(
69✔
1576
                    op, vl, log_cursor::constraint_t::unique);
1577
                break;
69✔
1578
            }
1579
            case VT_COL_LEVEL: {
6✔
1580
                if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
6✔
1581
                    continue;
×
1582
                }
1583

1584
                auto sf = from_sqlite<string_fragment>()(argc, argv, lpc);
6✔
1585
                auto level = string2level(sf.data(), sf.length());
6✔
1586

1587
                p_cur->log_cursor.lc_level_constraint
1588
                    = log_cursor::level_constraint{
6✔
1589
                        op,
1590
                        level,
1591
                    };
6✔
1592
                break;
6✔
1593
            }
1594

1595
            case VT_COL_LOG_TIME:
2✔
1596
                if (sqlite3_value_type(argv[lpc]) == SQLITE3_TEXT) {
2✔
1597
                    const auto* datestr
1598
                        = (const char*) sqlite3_value_text(argv[lpc]);
1✔
1599
                    auto datelen = sqlite3_value_bytes(argv[lpc]);
1✔
1600
                    date_time_scanner dts;
1✔
1601
                    struct timeval tv;
1602
                    struct exttm mytm;
1✔
1603

1604
                    const auto* date_end
1605
                        = dts.scan(datestr, datelen, nullptr, &mytm, tv);
1✔
1606
                    if (date_end != (datestr + datelen)) {
1✔
UNCOV
1607
                        log_warning(
×
1608
                            "  log_time constraint is not a valid datetime, "
1609
                            "index will not be applied: %s",
1610
                            datestr);
1611
                    } else {
1612
                        switch (op) {
1✔
1613
                            case SQLITE_INDEX_CONSTRAINT_EQ:
×
1614
                            case SQLITE_INDEX_CONSTRAINT_IS:
UNCOV
1615
                                if (!log_time_range) {
×
UNCOV
1616
                                    log_time_range = vtab_time_range{};
×
1617
                                }
UNCOV
1618
                                log_time_range->add(tv);
×
UNCOV
1619
                                break;
×
1620
                            case SQLITE_INDEX_CONSTRAINT_GT:
1✔
1621
                            case SQLITE_INDEX_CONSTRAINT_GE:
1622
                                if (!log_time_range) {
1✔
1623
                                    log_time_range = vtab_time_range{};
1✔
1624
                                }
1625
                                log_time_range->vtr_begin = tv;
1✔
1626
                                break;
1✔
UNCOV
1627
                            case SQLITE_INDEX_CONSTRAINT_LT:
×
1628
                            case SQLITE_INDEX_CONSTRAINT_LE:
UNCOV
1629
                                if (!log_time_range) {
×
UNCOV
1630
                                    log_time_range = vtab_time_range{};
×
1631
                                }
UNCOV
1632
                                log_time_range->vtr_end = tv;
×
UNCOV
1633
                                break;
×
1634
                        }
1635
                    }
1636
                } else {
1637
                    log_warning(
1✔
1638
                        "  log_time constraint is not text, index will not be "
1639
                        "applied: value_type(%d)=%d",
1640
                        lpc,
1641
                        sqlite3_value_type(argv[lpc]));
1642
                }
1643
                break;
2✔
1644
            default: {
220✔
1645
                if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
220✔
1646
                    auto footer_column = static_cast<log_footer_columns>(
1647
                        col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
7✔
1648

1649
                    switch (footer_column) {
7✔
1650
                        case log_footer_columns::time_msecs: {
1✔
1651
                            auto msecs = sqlite3_value_int64(argv[lpc]);
1✔
1652
                            struct timeval tv;
1653

1654
                            tv.tv_sec = msecs / 1000;
1✔
1655
                            tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
1✔
1656
                            switch (op) {
1✔
1657
                                case SQLITE_INDEX_CONSTRAINT_EQ:
1✔
1658
                                case SQLITE_INDEX_CONSTRAINT_IS:
1659
                                    if (!log_time_range) {
1✔
1660
                                        log_time_range = vtab_time_range{};
1✔
1661
                                    }
1662
                                    log_time_range->add(tv);
1✔
1663
                                    break;
1✔
1664
                                case SQLITE_INDEX_CONSTRAINT_GT:
×
1665
                                case SQLITE_INDEX_CONSTRAINT_GE:
1666
                                    if (!log_time_range) {
×
1667
                                        log_time_range = vtab_time_range{};
×
1668
                                    }
1669
                                    log_time_range->vtr_begin = tv;
×
1670
                                    break;
×
UNCOV
1671
                                case SQLITE_INDEX_CONSTRAINT_LT:
×
1672
                                case SQLITE_INDEX_CONSTRAINT_LE:
1673
                                    if (!log_time_range) {
×
1674
                                        log_time_range = vtab_time_range{};
×
1675
                                    }
UNCOV
1676
                                    log_time_range->vtr_end = tv;
×
1677
                                    break;
×
1678
                            }
1679
                            break;
1✔
1680
                        }
1681
                        case log_footer_columns::format: {
2✔
1682
                            const auto* format_name_str
1683
                                = (const char*) sqlite3_value_text(argv[lpc]);
2✔
1684

1685
                            if (format_name_str != nullptr) {
2✔
1686
                                p_cur->log_cursor.lc_format_name
1687
                                    = intern_string::lookup(format_name_str);
4✔
1688
                            }
1689
                            break;
2✔
1690
                        }
1691
                        case log_footer_columns::format_regex: {
×
1692
                            const auto* pattern_name_str
UNCOV
1693
                                = (const char*) sqlite3_value_text(argv[lpc]);
×
1694

UNCOV
1695
                            if (pattern_name_str != nullptr) {
×
1696
                                p_cur->log_cursor.lc_pattern_name
UNCOV
1697
                                    = intern_string::lookup(pattern_name_str);
×
1698
                            }
UNCOV
1699
                            break;
×
1700
                        }
1701
                        case log_footer_columns::opid:
1✔
1702
                        case log_footer_columns::user_opid: {
1703
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
1✔
UNCOV
1704
                                continue;
×
1705
                            }
1706
                            auto opid = from_sqlite<string_fragment>()(
1✔
1707
                                argc, argv, lpc);
1708
                            if (!log_time_range) {
1✔
1709
                                log_time_range = vtab_time_range{};
1✔
1710
                            }
1711
                            for (const auto& file_data : *vt->lss) {
2✔
1712
                                if (file_data->get_file_ptr() == nullptr) {
1✔
UNCOV
1713
                                    continue;
×
1714
                                }
1715
                                safe::ReadAccess<logfile::safe_opid_state>
1716
                                    r_opid_map(
1717
                                        file_data->get_file_ptr()->get_opids());
1✔
1718
                                const auto& iter
1719
                                    = r_opid_map->los_opid_ranges.find(opid);
1✔
1720
                                if (iter == r_opid_map->los_opid_ranges.end()) {
1✔
UNCOV
1721
                                    continue;
×
1722
                                }
1723
                                log_time_range->add(
1✔
1724
                                    iter->second.otr_range.tr_begin);
1✔
1725
                                log_time_range->add(
1✔
1726
                                    iter->second.otr_range.tr_end);
1✔
1727
                            }
1✔
1728

1729
                            opid_val = opid.hash();
1✔
1730
                            break;
1✔
1731
                        }
1732
                        case log_footer_columns::path: {
1✔
1733
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
1✔
1734
                                continue;
×
1735
                            }
1736

1737
                            const auto filename
1738
                                = from_sqlite<std::string>()(argc, argv, lpc);
1✔
1739
                            const auto fn_constraint
1740
                                = log_cursor::string_constraint{op, filename};
1✔
1741
                            auto found = false;
1✔
1742

1743
                            if (!log_time_range) {
1✔
1744
                                log_time_range = vtab_time_range{};
1✔
1745
                            }
1746
                            for (const auto& file_data : *vt->lss) {
3✔
1747
                                auto* lf = file_data->get_file_ptr();
2✔
1748
                                if (lf == nullptr) {
2✔
UNCOV
1749
                                    continue;
×
1750
                                }
1751
                                if (fn_constraint.matches(lf->get_filename())) {
2✔
1752
                                    found = true;
2✔
1753
                                    log_time_range->add(
4✔
1754
                                        lf->front().get_timeval());
2✔
1755
                                    log_time_range->add(
4✔
1756
                                        lf->back().get_timeval());
4✔
1757
                                }
1758
                            }
1759
                            if (found) {
1✔
1760
                                log_path_constraints.emplace_back(
1✔
1761
                                    fn_constraint);
1762
                            }
1763
                            break;
1✔
1764
                        }
1✔
UNCOV
1765
                        case log_footer_columns::unique_path: {
×
UNCOV
1766
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
×
UNCOV
1767
                                continue;
×
1768
                            }
1769

1770
                            const auto filename
UNCOV
1771
                                = from_sqlite<std::string>()(argc, argv, lpc);
×
1772
                            const auto fn_constraint
UNCOV
1773
                                = log_cursor::string_constraint{op, filename};
×
UNCOV
1774
                            auto found = false;
×
1775

UNCOV
1776
                            if (!log_time_range) {
×
UNCOV
1777
                                log_time_range = vtab_time_range{};
×
1778
                            }
UNCOV
1779
                            for (const auto& file_data : *vt->lss) {
×
UNCOV
1780
                                auto* lf = file_data->get_file_ptr();
×
UNCOV
1781
                                if (lf == nullptr) {
×
UNCOV
1782
                                    continue;
×
1783
                                }
UNCOV
1784
                                if (fn_constraint.matches(
×
UNCOV
1785
                                        lf->get_unique_path()))
×
1786
                                {
UNCOV
1787
                                    found = true;
×
UNCOV
1788
                                    log_time_range->add(
×
UNCOV
1789
                                        lf->front().get_timeval());
×
UNCOV
1790
                                    log_time_range->add(
×
UNCOV
1791
                                        lf->back().get_timeval());
×
1792
                                }
1793
                            }
1794
                            if (found) {
×
UNCOV
1795
                                log_unique_path_constraints.emplace_back(
×
1796
                                    fn_constraint);
1797
                            }
UNCOV
1798
                            break;
×
1799
                        }
1800
                        case log_footer_columns::partition:
×
1801
                        case log_footer_columns::actual_time:
1802
                        case log_footer_columns::idle_msecs:
1803
                        case log_footer_columns::mark:
1804
                        case log_footer_columns::comment:
1805
                        case log_footer_columns::tags:
1806
                        case log_footer_columns::annotations:
1807
                        case log_footer_columns::filters:
1808
                        case log_footer_columns::text:
1809
                        case log_footer_columns::body:
1810
                        case log_footer_columns::raw_text:
1811
                        case log_footer_columns::line_hash:
1812
                        case log_footer_columns::src_file:
1813
                        case log_footer_columns::src_line:
UNCOV
1814
                            break;
×
1815
                        case log_footer_columns::line_link: {
2✔
1816
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
2✔
UNCOV
1817
                                continue;
×
1818
                            }
1819
                            auto permalink
1820
                                = from_sqlite<std::string>()(argc, argv, lpc);
2✔
1821
                            auto row_opt = vt->lss->row_for_anchor(permalink);
2✔
1822
                            if (row_opt) {
2✔
1823
                                p_cur->log_cursor.update(
4✔
1824
                                    op,
1825
                                    row_opt.value(),
2✔
1826
                                    log_cursor::constraint_t::unique);
1827
                            } else {
UNCOV
1828
                                log_trace("could not find link: %s",
×
1829
                                          permalink.c_str());
1830
                            }
1831
                            break;
2✔
1832
                        }
2✔
1833
                    }
1834
                } else {
1835
                    const auto* value
1836
                        = (const char*) sqlite3_value_text(argv[lpc]);
213✔
1837

1838
                    if (value != nullptr) {
213✔
1839
                        auto value_len
1840
                            = (size_t) sqlite3_value_bytes(argv[lpc]);
213✔
1841

1842
#ifdef DEBUG_INDEXING
1843
                        log_debug("adding index request for column %d = %s",
1844
                                  col,
1845
                                  value);
1846
#endif
1847

1848
                        p_cur->log_cursor.lc_indexed_columns.emplace_back(
213✔
1849
                            col,
1850
                            log_cursor::string_constraint{
426✔
1851
                                op,
1852
                                std::string{value, value_len},
852✔
1853
                            });
1854
                    }
1855
                }
1856
                break;
220✔
1857
            }
1858
        }
1859
    }
1860

1861
    if (p_cur->log_cursor.lc_curr_line == p_cur->log_cursor.lc_end_line) {
384✔
1862
    } else if (!p_cur->log_cursor.lc_indexed_columns.empty()) {
379✔
1863
        auto min_index_range = msg_range::invalid();
213✔
1864
        auto scan_range
1865
            = msg_range::empty()
213✔
1866
                  .expand_to(p_cur->log_cursor.lc_curr_line)
213✔
1867
                  .expand_to(
213✔
1868
                      p_cur->log_cursor.lc_end_line
1869
                      - (p_cur->log_cursor.lc_direction > 0 ? 1_vl : -1_vl))
213✔
1870
                  .get_valid()
213✔
1871
                  .value();
213✔
1872
        for (const auto& icol : p_cur->log_cursor.lc_indexed_columns) {
19,732✔
1873
            auto& coli = vt->vi->vi_column_indexes[icol.cc_column];
19,519✔
1874
            if (coli.ci_index_generation != vt->lss->lss_index_generation) {
19,519✔
1875
                coli.ci_value_to_lines.clear();
7✔
1876
                coli.ci_index_generation = vt->lss->lss_index_generation;
7✔
1877
                coli.ci_indexed_range = msg_range::empty();
7✔
1878
                coli.ci_string_arena.reset();
7✔
1879
            }
1880

1881
            {
1882
                auto col_valid_opt = coli.ci_indexed_range.get_valid();
19,519✔
1883
                if (col_valid_opt) {
19,519✔
1884
                    log_debug("column %d valid range [%d:%d)",
19,512✔
1885
                              icol.cc_column,
1886
                              col_valid_opt->v_min_line,
1887
                              col_valid_opt->v_max_line);
1888
                }
1889
            }
1890

1891
            min_index_range.intersect(coli.ci_indexed_range);
19,519✔
1892
        }
1893

1894
        for (const auto& icol : p_cur->log_cursor.lc_indexed_columns) {
19,732✔
1895
            const auto& coli = vt->vi->vi_column_indexes[icol.cc_column];
19,519✔
1896

1897
            auto iter
1898
                = coli.ci_value_to_lines.find(icol.cc_constraint.sc_value);
19,519✔
1899
            if (iter != coli.ci_value_to_lines.end()) {
19,519✔
1900
                for (auto vl : iter->second) {
39,180✔
1901
                    if (!scan_range.contains(vl)) {
19,670✔
1902
#ifdef DEBUG_INDEXING
1903
                        log_debug(
1904
                            "indexed line %d is outside of scan range [%d:%d)",
1905
                            vl,
1906
                            scan_range.v_min_line,
1907
                            scan_range.v_max_line);
1908
#endif
1909
                        continue;
10✔
1910
                    }
1911

1912
                    if (!min_index_range.contains(vl)) {
19,660✔
1913
#ifdef DEBUG_INDEXING
1914
                        log_debug(
1915
                            "indexed line %d is outside of the min index range",
1916
                            vl);
1917
#endif
UNCOV
1918
                        continue;
×
1919
                    }
1920

1921
#ifdef DEBUG_INDEXING
1922
                    log_debug("adding indexed line %d", vl);
1923
#endif
1924
                    p_cur->log_cursor.lc_indexed_lines.push_back(vl);
19,660✔
1925
                }
1926
            }
1927
        }
1928
        p_cur->log_cursor.lc_indexed_lines_range = min_index_range;
213✔
1929

1930
#if 0
1931
        if (max_indexed_line && max_indexed_line.value() > 0_vl) {
1932
            p_cur->log_cursor.lc_indexed_lines.push_back(
1933
                max_indexed_line.value());
1934
        }
1935
#endif
1936

1937
        auto index_valid_opt = min_index_range.get_valid();
213✔
1938
        if (!min_index_range.contains(scan_range.v_min_line)
213✔
1939
            || !min_index_range.contains(scan_range.v_max_line - 1_vl))
213✔
1940
        {
1941
            log_debug(
208✔
1942
                "scan needed to populate index, clearing other indexes "
1943
                "scan_range[%d:%d)",
1944
                scan_range.v_min_line,
1945
                scan_range.v_max_line);
1946
            p_cur->log_cursor.lc_level_constraint = std::nullopt;
208✔
1947
            opid_val = std::nullopt;
208✔
1948
            log_time_range = std::nullopt;
208✔
1949
            log_path_constraints.clear();
208✔
1950
            log_unique_path_constraints.clear();
208✔
1951

1952
            if (index_valid_opt) {
208✔
1953
                log_debug("  min_index_range[%d:%d)",
201✔
1954
                          index_valid_opt->v_min_line,
1955
                          index_valid_opt->v_max_line);
1956
                if (scan_range.v_min_line < index_valid_opt->v_min_line
201✔
1957
                    && index_valid_opt->v_max_line < scan_range.v_max_line)
201✔
1958
                {
1959
                    for (const auto& icol :
×
1960
                         p_cur->log_cursor.lc_indexed_columns)
×
1961
                    {
1962
                        vt->vi->vi_column_indexes.erase(icol.cc_column);
×
1963
                    }
UNCOV
1964
                    p_cur->log_cursor.lc_indexed_lines.clear();
×
1965
                    p_cur->log_cursor.lc_indexed_lines_range
UNCOV
1966
                        = msg_range::empty();
×
1967
                } else if (scan_range.v_max_line < index_valid_opt->v_min_line
201✔
1968
                           || scan_range.v_min_line
402✔
1969
                               >= index_valid_opt->v_max_line)
201✔
1970
                {
1971
                    p_cur->log_cursor.lc_indexed_lines.clear();
1✔
1972
                    p_cur->log_cursor.lc_indexed_lines_range
1973
                        = msg_range::empty();
1✔
1974
                    if (p_cur->log_cursor.lc_direction < 0) {
1✔
1975
                        if (scan_range.v_max_line < index_valid_opt->v_min_line)
×
1976
                        {
1977
                            p_cur->log_cursor.lc_curr_line
1978
                                = index_valid_opt->v_min_line - 1_vl;
×
1979
                        } else {
1980
                            p_cur->log_cursor.lc_end_line
1981
                                = index_valid_opt->v_max_line - 1_vl;
×
1982
                        }
1983
                    } else {
1984
                        if (scan_range.v_max_line < index_valid_opt->v_min_line)
1✔
1985
                        {
1986
                            p_cur->log_cursor.lc_end_line
UNCOV
1987
                                = index_valid_opt->v_min_line;
×
1988
                        } else {
1989
                            p_cur->log_cursor.lc_curr_line
1990
                                = index_valid_opt->v_max_line;
1✔
1991
                        }
1992
                    }
1993
                }
1994
            } else {
1995
                log_debug("  min_index_range::empty");
7✔
1996
                p_cur->log_cursor.lc_indexed_lines.clear();
7✔
1997
            }
1998
        } else if (index_valid_opt) {
5✔
1999
            log_info("using existing index over range [%d:%d)",
5✔
2000
                     index_valid_opt->v_min_line,
2001
                     index_valid_opt->v_max_line);
2002
            if (p_cur->log_cursor.lc_direction < 0) {
5✔
2003
                p_cur->log_cursor.lc_indexed_lines.push_back(
4✔
2004
                    index_valid_opt->v_min_line - 1_vl);
8✔
2005
            } else {
2006
                p_cur->log_cursor.lc_indexed_lines.push_back(
2✔
2007
                    index_valid_opt->v_max_line);
1✔
2008
            }
2009
        }
2010

2011
        if (p_cur->log_cursor.lc_direction < 0) {
213✔
2012
            log_debug("ORDER BY is DESC, reversing indexed lines");
8✔
2013
            std::sort(p_cur->log_cursor.lc_indexed_lines.begin(),
8✔
2014
                      p_cur->log_cursor.lc_indexed_lines.end(),
2015
                      std::less<>());
2016
        } else {
2017
            std::sort(p_cur->log_cursor.lc_indexed_lines.begin(),
205✔
2018
                      p_cur->log_cursor.lc_indexed_lines.end(),
2019
                      std::greater<>());
2020
        }
2021

2022
#ifdef DEBUG_INDEXING
2023
        log_debug("indexed lines:");
2024
        for (auto indline : p_cur->log_cursor.lc_indexed_lines) {
2025
            log_debug("  %d", (int) indline);
2026
        }
2027
#endif
2028
    }
213✔
2029

2030
    if (!log_time_range) {
384✔
2031
    } else if (log_time_range->empty()) {
4✔
2032
#ifdef DEBUG_INDEXING
2033
        log_warning("time range is empty");
2034
#endif
UNCOV
2035
        p_cur->log_cursor.lc_curr_line = p_cur->log_cursor.lc_end_line;
×
2036
    } else {
2037
        if (log_time_range->vtr_begin) {
4✔
2038
            auto vl_opt
2039
                = vt->lss->row_for_time(log_time_range->vtr_begin.value());
4✔
2040
            if (!vl_opt) {
4✔
2041
#ifdef DEBUG_INDEXING
2042
                log_warning("cannot find row with begin time: %d",
2043
                            log_time_range->vtr_begin.value().tv_sec);
2044
#endif
UNCOV
2045
                p_cur->log_cursor.lc_curr_line = p_cur->log_cursor.lc_end_line;
×
2046
            } else {
2047
#ifdef DEBUG_INDEXING
2048
                log_debug("found row with begin time: %d -> %d",
2049
                          log_time_range->vtr_begin.value(),
2050
                          vl_opt.value());
2051
#endif
2052
                p_cur->log_cursor.lc_curr_line = vl_opt.value();
4✔
2053
            }
2054
        }
2055
        if (log_time_range->vtr_end) {
4✔
2056
            auto vl_max_opt
2057
                = vt->lss->row_for_time(log_time_range->vtr_end.value());
3✔
2058
            if (vl_max_opt) {
3✔
2059
                p_cur->log_cursor.lc_end_line = vl_max_opt.value();
3✔
2060
                for (const auto& msg_info :
3✔
2061
                     vt->lss->window_at(vl_max_opt.value(),
3✔
2062
                                        vis_line_t(vt->lss->text_line_count())))
13✔
2063
                {
2064
                    if (log_time_range->vtr_end.value()
5✔
2065
                        < msg_info.get_logline().get_timeval())
10✔
2066
                    {
2067
                        break;
1✔
2068
                    }
2069
                    p_cur->log_cursor.lc_end_line
2070
                        = msg_info.get_vis_line() + 1_vl;
4✔
2071
                }
3✔
2072
            }
2073
        }
2074
    }
2075

2076
    p_cur->log_cursor.lc_opid = opid_val;
384✔
2077
    p_cur->log_cursor.lc_log_path = std::move(log_path_constraints);
384✔
2078
    p_cur->log_cursor.lc_unique_path = std::move(log_unique_path_constraints);
384✔
2079

2080
#if 0
2081
    if (p_cur->log_cursor.lc_indexed_lines.empty()) {
2082
        p_cur->log_cursor.lc_indexed_lines.push_back(
2083
            p_cur->log_cursor.lc_curr_line);
2084
    }
2085
#endif
2086
    log_debug("before table filter [%d:%d)",
384✔
2087
              p_cur->log_cursor.lc_curr_line,
2088
              p_cur->log_cursor.lc_end_line);
2089
    vt->vi->filter(p_cur->log_cursor, *vt->lss);
384✔
2090

2091
    log_debug("before initial next [%d:%d)",
384✔
2092
              p_cur->log_cursor.lc_curr_line,
2093
              p_cur->log_cursor.lc_end_line);
2094
    if (vt->base.pModule->xNext != vt_next_no_rowid) {
384✔
2095
        p_cur->log_cursor.lc_curr_line -= p_cur->log_cursor.lc_direction;
371✔
2096
    }
2097
    vt->base.pModule->xNext(p_vtc);
384✔
2098

2099
#ifdef DEBUG_INDEXING
2100
    log_debug("vt_filter() -> cursor_range(%d:%d:%d)",
2101
              (int) p_cur->log_cursor.lc_curr_line,
2102
              (int) p_cur->log_cursor.lc_end_line,
2103
              p_cur->log_cursor.lc_direction);
2104
#endif
2105

2106
    return SQLITE_OK;
384✔
2107
}
384✔
2108

2109
static int
2110
vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
198✔
2111
{
2112
    std::vector<sqlite3_index_info::sqlite3_index_constraint> indexes;
198✔
2113
    std::vector<std::string> index_desc;
198✔
2114
    int argvInUse = 0;
198✔
2115
    auto* vt = (log_vtab*) tab;
198✔
2116
    char direction = 1;
198✔
2117

2118
    log_info("vt_best_index(%s, nConstraint=%d, nOrderBy=%d)",
198✔
2119
             vt->vi->get_name().get(),
2120
             p_info->nConstraint,
2121
             p_info->nOrderBy);
2122
    if (p_info->nOrderBy > 0) {
198✔
2123
        log_info("  ORDER BY");
17✔
2124
        for (int i = 0; i < p_info->nOrderBy; i++) {
34✔
2125
            const auto& orderby_info = p_info->aOrderBy[i];
17✔
2126
            log_info("    %d %s",
17✔
2127
                     orderby_info.iColumn,
2128
                     orderby_info.desc ? "DESC" : "ASC");
2129
        }
2130

2131
        if (p_info->aOrderBy[0].iColumn == 0) {
17✔
2132
            if (p_info->aOrderBy[0].desc) {
9✔
2133
                log_info("  consuming ORDER BY log_line DESC");
8✔
2134
                direction = -1;
8✔
2135
            } else {
2136
                log_info("  consuming ORDER BY log_line ASC");
1✔
2137
                direction = 1;
1✔
2138
            }
2139
            p_info->orderByConsumed = 1;
9✔
2140
        }
2141
    }
2142
    if (!vt->vi->vi_supports_indexes) {
198✔
2143
        p_info->orderByConsumed = 0;
10✔
2144
        return SQLITE_OK;
10✔
2145
    }
2146
    for (int lpc = 0; lpc < p_info->nConstraint; lpc++) {
344✔
2147
        const auto& constraint = p_info->aConstraint[lpc];
156✔
2148
        if (!constraint.usable || constraint.op == SQLITE_INDEX_CONSTRAINT_MATCH
156✔
2149
#ifdef SQLITE_INDEX_CONSTRAINT_OFFSET
2150
            || constraint.op == SQLITE_INDEX_CONSTRAINT_OFFSET
151✔
2151
            || constraint.op == SQLITE_INDEX_CONSTRAINT_LIMIT
151✔
2152
#endif
2153
        )
2154
        {
2155
            log_debug("  column %d: is not usable (usable=%d, op: %s)",
20✔
2156
                      lpc,
2157
                      constraint.usable,
2158
                      sql_constraint_op_name(constraint.op));
2159
            continue;
20✔
2160
        }
2161

2162
        auto col = constraint.iColumn;
136✔
2163
        auto op = constraint.op;
136✔
2164
        log_debug("  column %d: op: %s", col, sql_constraint_op_name(op));
136✔
2165
        switch (col) {
136✔
2166
            case VT_COL_LINE_NUMBER: {
63✔
2167
                argvInUse += 1;
63✔
2168
                indexes.push_back(constraint);
63✔
2169
                p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
63✔
2170
                index_desc.emplace_back(fmt::format(
63✔
2171
                    FMT_STRING("log_line {} ?"), sql_constraint_op_name(op)));
189✔
2172
                break;
63✔
2173
            }
2174
            case VT_COL_LOG_TIME: {
2✔
2175
                argvInUse += 1;
2✔
2176
                indexes.push_back(p_info->aConstraint[lpc]);
2✔
2177
                p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2178
                index_desc.emplace_back(fmt::format(
2✔
2179
                    FMT_STRING("log_time {} ?"), sql_constraint_op_name(op)));
6✔
2180
                break;
2✔
2181
            }
2182
            case VT_COL_LEVEL: {
7✔
2183
                if (log_cursor::level_constraint::op_is_supported(op)) {
7✔
2184
                    argvInUse += 1;
7✔
2185
                    indexes.push_back(constraint);
7✔
2186
                    p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
7✔
2187
                    index_desc.emplace_back(
7✔
2188
                        fmt::format(FMT_STRING("log_level {} ?"),
21✔
2189
                                    sql_constraint_op_name(op)));
14✔
2190
                }
2191
                break;
7✔
2192
            }
2193
            default: {
64✔
2194
                if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
64✔
2195
                    auto footer_column = static_cast<log_footer_columns>(
2196
                        col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
23✔
2197

2198
                    switch (footer_column) {
23✔
2199
                        case log_footer_columns::time_msecs: {
1✔
2200
                            argvInUse += 1;
1✔
2201
                            indexes.push_back(p_info->aConstraint[lpc]);
1✔
2202
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
1✔
2203
                            index_desc.emplace_back(
1✔
2204
                                fmt::format(FMT_STRING("log_time_msecs {} ?"),
3✔
2205
                                            sql_constraint_op_name(op)));
1✔
2206
                            break;
1✔
2207
                        }
2208
                        case log_footer_columns::format: {
3✔
2209
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
3✔
2210
                                argvInUse += 1;
3✔
2211
                                indexes.push_back(constraint);
3✔
2212
                                p_info->aConstraintUsage[lpc].argvIndex
3✔
2213
                                    = argvInUse;
3✔
2214
                                index_desc.emplace_back("log_format = ?");
3✔
2215
                            }
2216
                            break;
3✔
2217
                        }
UNCOV
2218
                        case log_footer_columns::format_regex: {
×
UNCOV
2219
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
×
UNCOV
2220
                                argvInUse += 1;
×
UNCOV
2221
                                indexes.push_back(constraint);
×
UNCOV
2222
                                p_info->aConstraintUsage[lpc].argvIndex
×
UNCOV
2223
                                    = argvInUse;
×
2224
                                index_desc.emplace_back("log_format_regex = ?");
×
2225
                            }
UNCOV
2226
                            break;
×
2227
                        }
2228
                        case log_footer_columns::opid:
1✔
2229
                        case log_footer_columns::user_opid: {
2230
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
1✔
2231
                                argvInUse += 1;
1✔
2232
                                indexes.push_back(constraint);
1✔
2233
                                p_info->aConstraintUsage[lpc].argvIndex
1✔
2234
                                    = argvInUse;
1✔
2235
                                index_desc.emplace_back("log_opid = ?");
1✔
2236
                            }
2237
                            break;
1✔
2238
                        }
2239
                        case log_footer_columns::path: {
2✔
2240
                            argvInUse += 1;
2✔
2241
                            indexes.push_back(constraint);
2✔
2242
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2243
                            index_desc.emplace_back(
2✔
2244
                                fmt::format(FMT_STRING("log_path {} ?"),
6✔
2245
                                            sql_constraint_op_name(op)));
2✔
2246
                            break;
2✔
2247
                        }
UNCOV
2248
                        case log_footer_columns::unique_path: {
×
UNCOV
2249
                            argvInUse += 1;
×
UNCOV
2250
                            indexes.push_back(constraint);
×
UNCOV
2251
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
×
UNCOV
2252
                            index_desc.emplace_back(
×
UNCOV
2253
                                fmt::format(FMT_STRING("log_unique_path {} ?"),
×
UNCOV
2254
                                            sql_constraint_op_name(op)));
×
UNCOV
2255
                            break;
×
2256
                        }
2257
                        case log_footer_columns::partition:
14✔
2258
                        case log_footer_columns::actual_time:
2259
                        case log_footer_columns::idle_msecs:
2260
                        case log_footer_columns::mark:
2261
                        case log_footer_columns::comment:
2262
                        case log_footer_columns::tags:
2263
                        case log_footer_columns::annotations:
2264
                        case log_footer_columns::filters:
2265
                        case log_footer_columns::text:
2266
                        case log_footer_columns::body:
2267
                        case log_footer_columns::raw_text:
2268
                        case log_footer_columns::line_hash:
2269
                        case log_footer_columns::src_file:
2270
                        case log_footer_columns::src_line:
2271
                            break;
14✔
2272
                        case log_footer_columns::line_link: {
2✔
2273
                            argvInUse += 1;
2✔
2274
                            indexes.push_back(constraint);
2✔
2275
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2276
                            index_desc.emplace_back("log_line_link = ?");
2✔
2277
                            break;
2✔
2278
                        }
2279
                    }
2280
                } else if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
41✔
2281
                    argvInUse += 1;
18✔
2282
                    indexes.push_back(constraint);
18✔
2283
                    p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
18✔
2284
                    index_desc.emplace_back(
18✔
2285
                        fmt::format(FMT_STRING("col({}) {} ?"),
54✔
2286
                                    col,
2287
                                    sql_constraint_op_name(op)));
36✔
2288
                }
2289
                break;
64✔
2290
            }
2291
        }
2292
    }
2293

2294
    if (argvInUse) {
188✔
2295
        auto full_desc = fmt::format(FMT_STRING("SEARCH {} USING {}"),
172✔
2296
                                     vt->vi->get_name().get(),
86✔
2297
                                     fmt::join(index_desc, " AND "));
86✔
2298
        log_info("found index: %s", full_desc.c_str());
86✔
2299

2300
        sqlite3_index_info::sqlite3_index_constraint* index_copy;
2301
        auto index_len = indexes.size() * sizeof(*index_copy);
86✔
2302
        size_t len = full_desc.size() + 128 + index_len;
86✔
2303
        auto* storage = sqlite3_malloc(len);
86✔
2304
        if (!storage) {
86✔
2305
            return SQLITE_NOMEM;
×
2306
        }
2307
        auto* desc_storage = static_cast<char*>(storage);
86✔
2308
        memcpy(desc_storage, full_desc.c_str(), full_desc.size() + 1);
86✔
2309
        desc_storage[full_desc.size() + 1] = direction;
86✔
2310
        auto* remaining_storage
2311
            = static_cast<void*>(desc_storage + full_desc.size() + 1 + 1);
86✔
2312
        len -= 1 + full_desc.size() - 1;
86✔
2313
        auto* index_storage
2314
            = std::align(alignof(sqlite3_index_info::sqlite3_index_constraint),
86✔
2315
                         index_len,
2316
                         remaining_storage,
2317
                         len);
2318
        index_copy
2319
            = reinterpret_cast<sqlite3_index_info::sqlite3_index_constraint*>(
86✔
2320
                index_storage);
2321
        log_info("  index storage: %p", index_copy);
86✔
2322
        memcpy(index_copy, &indexes[0], index_len);
86✔
2323
        p_info->idxNum = argvInUse;
86✔
2324
        p_info->idxStr = static_cast<char*>(storage);
86✔
2325
        p_info->needToFreeIdxStr = 1;
86✔
2326
        p_info->estimatedCost = 10.0;
86✔
2327
    } else {
86✔
2328
        static char fullscan_asc[] = "fullscan\0\001";
2329
        static char fullscan_desc[] = "fullscan\0\377";
2330

2331
        p_info->idxStr = direction < 0 ? fullscan_desc : fullscan_asc;
102✔
2332
        p_info->estimatedCost = 1000000000.0;
102✔
2333
    }
2334

2335
    return SQLITE_OK;
188✔
2336
}
198✔
2337

2338
static const struct json_path_container tags_handler = {
2339
    json_path_handler("#")
2340
        .with_synopsis("tag")
2341
        .with_description("A tag for the log line")
2342
        .with_pattern(R"(^#[^\s]+$)")
2343
        .for_field(&bookmark_metadata::bm_tags),
2344
};
2345

2346
static int
2347
vt_update(sqlite3_vtab* tab,
44✔
2348
          int argc,
2349
          sqlite3_value** argv,
2350
          sqlite_int64* rowid_out)
2351
{
2352
    auto* vt = (log_vtab*) tab;
44✔
2353
    int retval = SQLITE_READONLY;
44✔
2354

2355
    if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL
43✔
2356
        && sqlite3_value_int64(argv[0]) == sqlite3_value_int64(argv[1]))
87✔
2357
    {
2358
        int64_t rowid = sqlite3_value_int64(argv[0]) >> 8;
43✔
2359
        int val = sqlite3_value_int(
86✔
2360
            argv[2 + vt->footer_index(log_footer_columns::mark)]);
43✔
2361
        vis_line_t vrowid(rowid);
43✔
2362
        const auto msg_info = *vt->lss->window_at(vrowid).begin();
43✔
2363
        const auto* part_name = sqlite3_value_text(
86✔
2364
            argv[2 + vt->footer_index(log_footer_columns::partition)]);
43✔
2365
        const auto* log_comment = sqlite3_value_text(
86✔
2366
            argv[2 + vt->footer_index(log_footer_columns::comment)]);
43✔
UNCOV
2367
        const auto log_tags = from_sqlite<std::optional<string_fragment>>()(
×
2368
            argc, argv, 2 + vt->footer_index(log_footer_columns::tags));
43✔
UNCOV
2369
        const auto log_annos = from_sqlite<std::optional<string_fragment>>()(
×
2370
            argc, argv, 2 + vt->footer_index(log_footer_columns::annotations));
43✔
UNCOV
2371
        auto log_opid = from_sqlite<std::optional<string_fragment>>()(
×
2372
            argc, argv, 2 + vt->footer_index(log_footer_columns::opid));
43✔
2373
        const auto log_user_opid
UNCOV
2374
            = from_sqlite<std::optional<string_fragment>>()(
×
2375
                argc,
2376
                argv,
2377
                2 + vt->footer_index(log_footer_columns::user_opid));
43✔
2378
        bookmark_metadata tmp_bm;
43✔
2379

2380
        if (log_user_opid) {
43✔
UNCOV
2381
            log_opid = log_user_opid;
×
2382
        }
2383
        if (log_tags) {
43✔
2384
            std::vector<lnav::console::user_message> errors;
4✔
2385
            yajlpp_parse_context ypc(vt->vi->get_tags_name(), &tags_handler);
4✔
2386
            auto_mem<yajl_handle_t> handle(yajl_free);
4✔
2387

2388
            handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
4✔
2389
            ypc.ypc_userdata = &errors;
4✔
2390
            ypc.ypc_line_number = log_vtab_data.lvd_location.sl_line_number;
4✔
2391
            ypc.with_handle(handle)
4✔
2392
                .with_error_reporter([](const yajlpp_parse_context& ypc,
8✔
2393
                                        auto msg) {
2394
                    auto& errors = *((std::vector<lnav::console::user_message>*)
2✔
2395
                                         ypc.ypc_userdata);
2396
                    errors.emplace_back(msg);
2✔
2397
                })
2✔
2398
                .with_obj(tmp_bm);
4✔
2399
            ypc.parse_doc(log_tags.value());
4✔
2400
            if (!errors.empty()) {
4✔
UNCOV
2401
                auto top_error = lnav::console::user_message::error(
×
2402
                                     attr_line_t("invalid value for ")
2✔
2403
                                         .append_quoted("log_tags"_symbol)
2✔
2404
                                         .append(" column of table ")
2✔
2405
                                         .append_quoted(lnav::roles::symbol(
4✔
2406
                                             vt->vi->get_name().to_string())))
4✔
2407
                                     .with_reason(errors[0].to_attr_line({}))
4✔
2408
                                     .move();
2✔
2409
                set_vtable_errmsg(tab, top_error);
2✔
2410
                return SQLITE_ERROR;
2✔
2411
            }
2✔
2412
        }
8✔
2413
        if (log_annos) {
41✔
2414
            static const intern_string_t SRC
2415
                = intern_string::lookup("log_annotations");
9✔
2416

2417
            auto parse_res = logmsg_annotations_handlers.parser_for(SRC).of(
6✔
2418
                log_annos.value());
3✔
2419
            if (parse_res.isErr()) {
3✔
2420
                set_vtable_errmsg(tab, parse_res.unwrapErr()[0]);
2✔
2421
                return SQLITE_ERROR;
2✔
2422
            }
2423

2424
            tmp_bm.bm_annotations = parse_res.unwrap();
1✔
2425
        }
3✔
2426

2427
        auto& bv_meta = vt->tc->get_bookmarks()[&textview_curses::BM_META];
39✔
2428
        bool has_meta = log_comment != nullptr || log_tags.has_value()
37✔
2429
            || log_annos.has_value();
76✔
2430

2431
        if (bv_meta.bv_tree.exists(vrowid) && !has_meta) {
39✔
2432
            vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, false);
2✔
2433
            vt->lss->set_line_meta_changed();
2✔
2434
        }
2435

2436
        if (!has_meta && part_name == nullptr
35✔
2437
            && (!log_opid
99✔
2438
                || msg_info.get_values().lvv_opid_provenance
25✔
2439
                    == logline_value_vector::opid_provenance::file))
2440
        {
2441
            vt->lss->erase_bookmark_metadata(vrowid);
23✔
2442
        }
2443

2444
        if (part_name) {
39✔
2445
            auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
3✔
2446
            line_meta.bm_name = std::string((const char*) part_name);
3✔
2447
            vt->tc->set_user_mark(&textview_curses::BM_PARTITION, vrowid, true);
3✔
2448
        } else {
2449
            vt->tc->set_user_mark(
36✔
2450
                &textview_curses::BM_PARTITION, vrowid, false);
2451
        }
2452

2453
        if (log_opid) {
39✔
2454
            auto& lvv = msg_info.get_values();
32✔
2455
            if (!lvv.lvv_opid_value
32✔
2456
                || lvv.lvv_opid_provenance
32✔
2457
                    == logline_value_vector::opid_provenance::user)
2458
            {
2459
                msg_info.get_file_ptr()->set_logline_opid(
18✔
2460
                    msg_info.get_file_line_number(), log_opid.value());
9✔
2461
                vt->lss->set_line_meta_changed();
9✔
2462
            }
2463
        } else if (msg_info.get_values().lvv_opid_provenance
7✔
2464
                   == logline_value_vector::opid_provenance::user)
7✔
2465
        {
UNCOV
2466
            msg_info.get_file_ptr()->clear_logline_opid(
×
2467
                msg_info.get_file_line_number());
2468
        }
2469

2470
        if (has_meta) {
39✔
2471
            auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
4✔
2472

2473
            vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, true);
4✔
2474
            if (part_name == nullptr) {
4✔
2475
                line_meta.bm_name.clear();
4✔
2476
            }
2477
            if (log_comment) {
4✔
2478
                line_meta.bm_comment = std::string((const char*) log_comment);
4✔
2479
            } else {
2480
                line_meta.bm_comment.clear();
2✔
2481
            }
2482
            if (log_tags) {
4✔
2483
                line_meta.bm_tags.clear();
2✔
2484
                for (const auto& tag : tmp_bm.bm_tags) {
5✔
2485
                    line_meta.add_tag(tag);
3✔
2486
                }
2487

2488
                for (const auto& tag : line_meta.bm_tags) {
4✔
2489
                    bookmark_metadata::KNOWN_TAGS.insert(tag);
2✔
2490
                }
2491
            } else {
2492
                line_meta.bm_tags.clear();
2✔
2493
            }
2494
            if (log_annos) {
4✔
2495
                line_meta.bm_annotations = std::move(tmp_bm.bm_annotations);
1✔
2496
            } else if (!sqlite3_value_nochange(
3✔
2497
                           argv[2
3✔
2498
                                + vt->footer_index(
3✔
2499
                                    log_footer_columns::annotations)]))
3✔
2500
            {
UNCOV
2501
                line_meta.bm_annotations.la_pairs.clear();
×
2502
            }
2503
            vt->lss->set_line_meta_changed();
4✔
2504
        }
2505

2506
        vt->tc->set_user_mark(&textview_curses::BM_USER, vrowid, val);
39✔
2507
        rowid += 1;
39✔
2508
        while ((size_t) rowid < vt->lss->text_line_count()) {
42✔
2509
            vis_line_t vl(rowid);
28✔
2510
            auto cl = vt->lss->at(vl);
28✔
2511
            auto* ll = vt->lss->find_line(cl);
28✔
2512
            if (ll->is_message()) {
28✔
2513
                break;
25✔
2514
            }
2515
            vt->tc->set_user_mark(&textview_curses::BM_USER, vl, val);
3✔
2516
            rowid += 1;
3✔
2517
        }
2518

2519
        if (retval != SQLITE_ERROR) {
39✔
2520
            retval = SQLITE_OK;
39✔
2521
        }
2522
    }
47✔
2523

2524
    return retval;
40✔
2525
}
2526

2527
static const sqlite3_module generic_vtab_module = {
2528
    0, /* iVersion */
2529
    vt_create, /* xCreate       - create a vtable */
2530
    vt_connect, /* xConnect      - associate a vtable with a connection */
2531
    vt_best_index, /* xBestIndex    - best index */
2532
    vt_disconnect, /* xDisconnect   - disassociate a vtable with a connection */
2533
    vt_destroy, /* xDestroy      - destroy a vtable */
2534
    vt_open, /* xOpen         - open a cursor */
2535
    vt_close, /* xClose        - close a cursor */
2536
    vt_filter, /* xFilter       - configure scan constraints */
2537
    vt_next, /* xNext         - advance a cursor */
2538
    vt_eof, /* xEof          - inidicate end of result set*/
2539
    vt_column, /* xColumn       - read data */
2540
    vt_rowid, /* xRowid        - read data */
2541
    vt_update, /* xUpdate       - write data */
2542
    nullptr, /* xBegin        - begin transaction */
2543
    nullptr, /* xSync         - sync transaction */
2544
    nullptr, /* xCommit       - commit transaction */
2545
    nullptr, /* xRollback     - rollback transaction */
2546
    nullptr, /* xFindFunction - function overloading */
2547
};
2548

2549
static const sqlite3_module no_rowid_vtab_module = {
2550
    0, /* iVersion */
2551
    vt_create, /* xCreate       - create a vtable */
2552
    vt_connect, /* xConnect      - associate a vtable with a connection */
2553
    vt_best_index, /* xBestIndex    - best index */
2554
    vt_disconnect, /* xDisconnect   - disassociate a vtable with a connection */
2555
    vt_destroy, /* xDestroy      - destroy a vtable */
2556
    vt_open, /* xOpen         - open a cursor */
2557
    vt_close, /* xClose        - close a cursor */
2558
    vt_filter, /* xFilter       - configure scan constraints */
2559
    vt_next_no_rowid, /* xNext         - advance a cursor */
2560
    vt_eof, /* xEof          - inidicate end of result set*/
2561
    vt_column, /* xColumn       - read data */
2562
    nullptr, /* xRowid        - read data */
2563
    nullptr, /* xUpdate       - write data */
2564
    nullptr, /* xBegin        - begin transaction */
2565
    nullptr, /* xSync         - sync transaction */
2566
    nullptr, /* xCommit       - commit transaction */
2567
    nullptr, /* xRollback     - rollback transaction */
2568
    nullptr, /* xFindFunction - function overloading */
2569
};
2570

2571
static int
2572
progress_callback(void* ptr)
746,738✔
2573
{
2574
    int retval = 0;
746,738✔
2575

2576
    if (log_vtab_data.lvd_progress != nullptr) {
746,738✔
2577
        retval = log_vtab_data.lvd_progress(log_cursor_latest);
11,351✔
2578
    }
2579
    if (!log_vtab_data.lvd_looping) {
746,738✔
UNCOV
2580
        retval = 1;
×
2581
    }
2582

2583
    return retval;
746,738✔
2584
}
2585

2586
log_vtab_manager::log_vtab_manager(sqlite3* memdb,
579✔
2587
                                   textview_curses& tc,
2588
                                   logfile_sub_source& lss)
579✔
2589
    : vm_db(memdb), vm_textview(tc), vm_source(lss)
579✔
2590
{
2591
    sqlite3_create_module(
579✔
2592
        this->vm_db, "log_vtab_impl", &generic_vtab_module, this);
2593
    sqlite3_create_module(
579✔
2594
        this->vm_db, "log_vtab_no_rowid_impl", &no_rowid_vtab_module, this);
2595
    sqlite3_progress_handler(memdb, 32, progress_callback, nullptr);
579✔
2596
}
579✔
2597

2598
log_vtab_manager::~log_vtab_manager()
579✔
2599
{
2600
    while (!this->vm_impls.empty()) {
48,863✔
2601
        auto first_name = this->vm_impls.begin()->first;
48,284✔
2602

2603
        this->unregister_vtab(first_name);
48,284✔
2604
    }
2605
}
579✔
2606

2607
std::string
2608
log_vtab_manager::register_vtab(std::shared_ptr<log_vtab_impl> vi)
48,444✔
2609
{
2610
    std::string retval;
48,444✔
2611

2612
    if (this->vm_impls.find(vi->get_name().to_string_fragment())
48,444✔
2613
        == this->vm_impls.end())
96,888✔
2614
    {
2615
        std::vector<std::string> primary_keys;
48,444✔
2616
        auto_mem<char, sqlite3_free> errmsg;
48,444✔
2617
        auto_mem<char, sqlite3_free> sql;
48,444✔
2618
        int rc;
2619

2620
        this->vm_impls[vi->get_name().to_string_fragment()] = vi;
48,444✔
2621

2622
        vi->get_primary_keys(primary_keys);
48,444✔
2623

2624
        sql = sqlite3_mprintf(
2625
            "CREATE VIRTUAL TABLE %s "
2626
            "USING %s(%s)",
2627
            vi->get_name().get(),
48,444✔
2628
            primary_keys.empty() ? "log_vtab_impl" : "log_vtab_no_rowid_impl",
48,444✔
2629
            vi->get_name().get());
145,332✔
2630
        rc = sqlite3_exec(this->vm_db, sql, nullptr, nullptr, errmsg.out());
48,444✔
2631
        if (rc != SQLITE_OK) {
48,444✔
2632
            retval = errmsg;
1✔
2633
        }
2634
    } else {
48,444✔
UNCOV
2635
        retval = "a table with the given name already exists";
×
2636
    }
2637

2638
    return retval;
48,444✔
UNCOV
2639
}
×
2640

2641
std::string
2642
log_vtab_manager::unregister_vtab(string_fragment name)
48,677✔
2643
{
2644
    std::string retval;
48,677✔
2645

2646
    if (this->vm_impls.find(name) == this->vm_impls.end()) {
48,677✔
2647
        retval = fmt::format(FMT_STRING("unknown table -- {}"), name);
932✔
2648
    } else {
2649
        auto_mem<char, sqlite3_free> sql;
48,444✔
2650
        __attribute((unused)) int rc;
2651

2652
        sql = sqlite3_mprintf(
2653
            "DROP TABLE IF EXISTS %.*s", name.length(), name.data());
48,444✔
2654
        log_debug("unregister_vtab: %s", sql.in());
48,444✔
2655
        rc = sqlite3_exec(this->vm_db, sql, nullptr, nullptr, nullptr);
48,444✔
2656

2657
        this->vm_impls.erase(name);
48,444✔
2658
    }
48,444✔
2659

2660
    return retval;
48,677✔
UNCOV
2661
}
×
2662

2663
std::shared_ptr<log_vtab_impl>
2664
log_vtab_manager::lookup_impl(string_fragment name) const
48,870✔
2665
{
2666
    const auto iter = this->vm_impls.find(name);
48,870✔
2667
    if (iter != this->vm_impls.end()) {
48,870✔
2668
        return iter->second;
48,863✔
2669
    }
2670
    return nullptr;
7✔
2671
}
2672

2673
bool
2674
log_format_vtab_impl::next(log_cursor& lc, logfile_sub_source& lss)
50,276✔
2675
{
2676
    if (lc.is_eof()) {
50,276✔
UNCOV
2677
        return true;
×
2678
    }
2679

2680
    auto cl = content_line_t(lss.at(lc.lc_curr_line));
50,276✔
2681
    auto* lf = lss.find_file_ptr(cl);
50,276✔
2682
    auto lf_iter = lf->begin() + cl;
50,276✔
2683
    uint8_t mod_id = lf_iter->get_module_id();
50,276✔
2684

2685
    if (!lf_iter->is_message()) {
50,276✔
UNCOV
2686
        return false;
×
2687
    }
2688

2689
    auto format = lf->get_format_ptr();
50,276✔
2690
    if (format->get_name() == this->lfvi_format.get_name()) {
50,276✔
2691
        return true;
29,900✔
2692
    }
2693
    if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
20,376✔
2694
        // XXX
UNCOV
2695
        return true;
×
2696
    }
2697

2698
    return false;
20,376✔
2699
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc