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

tstack / lnav / 24005722728-2927

05 Apr 2026 04:31PM UTC coverage: 69.142% (+0.002%) from 69.14%
24005722728-2927

push

github

tstack
[docs] add some missing stuff

53377 of 77199 relevant lines covered (69.14%)

538923.9 hits per line

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

84.74
/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 <cstdint>
31
#include <memory>
32
#include <string>
33
#include <unordered_set>
34
#include <vector>
35

36
#include "log_vtab_impl.hh"
37

38
#include "base/ansi_scrubber.hh"
39
#include "base/intern_string.hh"
40
#include "base/itertools.hh"
41
#include "base/lnav_log.hh"
42
#include "base/string_util.hh"
43
#include "bookmarks.json.hh"
44
#include "config.h"
45
#include "lnav_util.hh"
46
#include "logfile_sub_source.hh"
47
#include "logline_window.hh"
48
#include "sql_util.hh"
49
#include "vtab_module.hh"
50
#include "vtab_module_json.hh"
51
#include "yajlpp/json_op.hh"
52
#include "yajlpp/yajlpp_def.hh"
53

54
// #define DEBUG_INDEXING 1
55

56
using namespace lnav::roles::literals;
57

58
static auto intern_lifetime = intern_string::get_table_lifetime();
59

60
static log_cursor log_cursor_latest;
61

62
thread_local _log_vtab_data log_vtab_data;
63

64
const std::unordered_set<string_fragment, frag_hasher>
65
    log_vtab_impl::RESERVED_COLUMNS = {
66
        "log_line"_frag,
67
        "log_time"_frag,
68
        "log_level"_frag,
69
        "log_part"_frag,
70
        "log_actual_time"_frag,
71
        "log_idle_msecs"_frag,
72
        "log_mark"_frag,
73
        "log_sticky_mark"_frag,
74
        "log_comment"_frag,
75
        "log_tags"_frag,
76
        "log_annotations"_frag,
77
        "log_filters"_frag,
78
        "log_opid"_frag,
79
        "log_user_opid"_frag,
80
        "log_opid_definition"_frag,
81
        "log_format"_frag,
82
        "log_format_regex"_frag,
83
        "log_time_msecs"_frag,
84
        "log_path"_frag,
85
        "log_unique_path"_frag,
86
        "log_text"_frag,
87
        "log_body"_frag,
88
        "log_raw_text"_frag,
89
        "log_line_hash"_frag,
90
        "log_line_link"_frag,
91
        "log_src_file"_frag,
92
        "log_src_line"_frag,
93
        "log_thread_id"_frag,
94
};
95

96
static const char* const LOG_COLUMNS = R"(  (
97
  log_line        INTEGER,                         -- The line number for the log message
98
  log_time        DATETIME,                        -- The adjusted timestamp for the log message
99
  log_level       TEXT     COLLATE loglevel,       -- The log message level
100
  -- BEGIN Format-specific fields:
101
)";
102

103
static const char* const LOG_FOOTER_COLUMNS = R"(
104
  -- END Format-specific fields
105
  log_part         TEXT     COLLATE naturalnocase,    -- The partition the message is in
106
  log_actual_time  DATETIME HIDDEN,                   -- The timestamp from the original log file for this message
107
  log_idle_msecs   INTEGER,                           -- The difference in time between this messages and the previous
108
  log_mark         BOOLEAN,                           -- True if the log message was marked
109
  log_sticky_mark  BOOLEAN HIDDEN,                    -- True if the log message is a sticky header
110
  log_comment      TEXT,                              -- The comment for this message
111
  log_tags         TEXT,                              -- A JSON list of tags for this message
112
  log_annotations  TEXT,                              -- A JSON object of annotations for this messages
113
  log_filters      TEXT,                              -- A JSON list of filter IDs that matched this message
114
  log_opid         TEXT HIDDEN,                       -- The message's OPID from the log message or user
115
  log_user_opid    TEXT HIDDEN,                       -- The message's OPID as set by the user
116
  log_opid_definition TEXT HIDDEN,                    -- The matching OPID description definition
117
  log_format       TEXT HIDDEN,                       -- The name of the log file format
118
  log_format_regex TEXT HIDDEN,                       -- The name of the regex used to parse this log message
119
  log_time_msecs   INTEGER HIDDEN,                    -- The adjusted timestamp for the log message as the number of milliseconds from the epoch
120
  log_path         TEXT HIDDEN COLLATE naturalnocase, -- The path to the log file this message is from
121
  log_unique_path  TEXT HIDDEN COLLATE naturalnocase, -- The unique portion of the path this message is from
122
  log_text         TEXT HIDDEN,                       -- The full text of the log message
123
  log_body         TEXT HIDDEN,                       -- The body of the log message
124
  log_raw_text     TEXT HIDDEN,                       -- The raw text from the log file
125
  log_line_hash    TEXT HIDDEN,                       -- A hash of the first line of the log message
126
  log_line_link    TEXT HIDDEN,                       -- The permalink for the log message
127
  log_src_file     TEXT HIDDEN,                       -- The source file the log message came from
128
  log_src_line     TEXT HIDDEN,                       -- The source line the log message came from
129
  log_thread_id    TEXT HIDDEN                        -- The ID of the thread that generated this message
130
)";
131

132
enum class log_footer_columns : uint32_t {
133
    partition,
134
    actual_time,
135
    idle_msecs,
136
    mark,
137
    sticky_mark,
138
    comment,
139
    tags,
140
    annotations,
141
    filters,
142
    opid,
143
    user_opid,
144
    opid_definition,
145
    format,
146
    format_regex,
147
    time_msecs,
148
    path,
149
    unique_path,
150
    text,
151
    body,
152
    raw_text,
153
    line_hash,
154
    line_link,
155
    src_file,
156
    src_line,
157
    thread_id,
158
};
159

160
const std::string&
161
log_vtab_impl::get_table_statement()
64,549✔
162
{
163
    if (this->vi_table_statement.empty()) {
64,549✔
164
        std::vector<vtab_column> cols;
64,344✔
165
        std::ostringstream oss;
64,344✔
166
        size_t max_name_len = 15;
64,344✔
167

168
        oss << "CREATE TABLE lnav_db." << this->get_name().to_string()
128,688✔
169
            << LOG_COLUMNS;
128,688✔
170
        this->get_columns(cols);
64,344✔
171
        this->vi_column_count = cols.size();
64,344✔
172
        for (const auto& col : cols) {
655,575✔
173
            max_name_len = std::max(max_name_len, col.vc_name.length());
591,231✔
174
        }
175
        for (const auto& col : cols) {
655,575✔
176
            std::string comment;
591,231✔
177

178
            require(!col.vc_name.empty());
591,231✔
179

180
            if (!col.vc_comment.empty()) {
591,231✔
181
                comment.append(" -- ").append(col.vc_comment);
77,781✔
182
            }
183

184
            auto colname = sql_quote_ident(col.vc_name.c_str());
591,231✔
185
            auto coldecl = lnav::sql::mprintf(
186
                "  %-*s %-7s %s COLLATE %-15Q,%s\n",
187
                max_name_len,
188
                colname.in(),
189
                sqlite3_type_to_string(col.vc_type),
591,231✔
190
                col.vc_hidden ? "hidden" : "",
591,231✔
191
                col.vc_collator.empty() ? "BINARY" : col.vc_collator.c_str(),
607,394✔
192
                comment.c_str());
1,198,625✔
193
            oss << coldecl;
591,231✔
194
        }
591,231✔
195
        oss << LOG_FOOTER_COLUMNS;
64,344✔
196

197
        {
198
            std::vector<std::string> primary_keys;
64,344✔
199

200
            this->get_primary_keys(primary_keys);
64,344✔
201
            if (!primary_keys.empty()) {
64,344✔
202
                auto first = true;
9,836✔
203

204
                oss << ", PRIMARY KEY (";
9,836✔
205
                for (const auto& pkey : primary_keys) {
29,508✔
206
                    if (!first) {
19,672✔
207
                        oss << ", ";
9,836✔
208
                    }
209
                    oss << pkey;
19,672✔
210
                    first = false;
19,672✔
211
                }
212
                oss << ")\n";
9,836✔
213
            } else {
214
                oss << ", PRIMARY KEY (log_line)\n";
54,508✔
215
            }
216
        }
64,344✔
217

218
        oss << ");\n";
64,344✔
219

220
        log_trace("log_vtab_impl.get_table_statement() -> %s",
64,344✔
221
                  oss.str().c_str());
222

223
        this->vi_table_statement = oss.str();
64,344✔
224
    }
64,344✔
225

226
    return this->vi_table_statement;
64,549✔
227
}
228

229
std::pair<int, unsigned int>
230
log_vtab_impl::logline_value_to_sqlite_type(value_kind_t kind)
559,849✔
231
{
232
    int type = 0;
559,849✔
233
    unsigned int subtype = 0;
559,849✔
234

235
    switch (kind) {
559,849✔
236
        case value_kind_t::VALUE_JSON:
15,681✔
237
            type = SQLITE3_TEXT;
15,681✔
238
            subtype = JSON_SUBTYPE;
15,681✔
239
            break;
15,681✔
240
        case value_kind_t::VALUE_NULL:
429,798✔
241
        case value_kind_t::VALUE_TEXT:
242
        case value_kind_t::VALUE_STRUCT:
243
        case value_kind_t::VALUE_QUOTED:
244
        case value_kind_t::VALUE_W3C_QUOTED:
245
        case value_kind_t::VALUE_TIMESTAMP:
246
        case value_kind_t::VALUE_XML:
247
            type = SQLITE3_TEXT;
429,798✔
248
            break;
429,798✔
249
        case value_kind_t::VALUE_FLOAT:
7,745✔
250
            type = SQLITE_FLOAT;
7,745✔
251
            break;
7,745✔
252
        case value_kind_t::VALUE_BOOLEAN:
106,625✔
253
        case value_kind_t::VALUE_INTEGER:
254
            type = SQLITE_INTEGER;
106,625✔
255
            break;
106,625✔
256
        case value_kind_t::VALUE_UNKNOWN:
×
257
        case value_kind_t::VALUE__MAX:
258
            ensure(0);
×
259
            break;
260
    }
261
    return std::make_pair(type, subtype);
1,119,698✔
262
}
263

264
void
265
log_vtab_impl::get_foreign_keys(
50,263✔
266
    std::unordered_set<std::string>& keys_inout) const
267
{
268
    keys_inout.emplace("id");
50,263✔
269
    keys_inout.emplace("parent");
50,263✔
270
    keys_inout.emplace("notused");
50,263✔
271

272
    keys_inout.emplace("log_line");
50,263✔
273
    keys_inout.emplace("min(log_line)");
50,263✔
274
    keys_inout.emplace("log_mark");
50,263✔
275
    keys_inout.emplace("log_time_msecs");
50,263✔
276
    keys_inout.emplace("log_top_line()");
50,263✔
277
    keys_inout.emplace("log_msg_line()");
50,263✔
278
    keys_inout.emplace("log_src_line");
50,263✔
279
    keys_inout.emplace("log_thread_id");
50,263✔
280
}
50,263✔
281

282
void
283
log_vtab_impl::extract(logfile* lf,
29,504✔
284
                       uint64_t line_number,
285
                       string_attrs_t& sa,
286
                       logline_value_vector& values)
287
{
288
    const auto* format = lf->get_format_ptr();
29,504✔
289

290
    format->annotate(lf, line_number, sa, values);
29,504✔
291
}
29,504✔
292

293
bool
294
log_vtab_impl::is_valid(log_cursor& lc, logfile_sub_source& lss)
55,161✔
295
{
296
    if (lc.lc_curr_line < 0_vl) {
55,161✔
297
        return false;
×
298
    }
299

300
    content_line_t cl(lss.at(lc.lc_curr_line));
55,161✔
301
    auto* lf = lss.find_file_ptr(cl);
55,161✔
302
    auto lf_iter = lf->begin() + cl;
55,161✔
303

304
    if (!lf_iter->is_message()) {
55,161✔
305
        return false;
386✔
306
    }
307

308
    if (!lc.lc_format_name.empty()
54,775✔
309
        && lc.lc_format_name != lf->get_format_name())
54,775✔
310
    {
311
        return false;
3✔
312
    }
313

314
    if (!lc.lc_pattern_name.empty()
54,772✔
315
        && lc.lc_pattern_name
54,772✔
316
            != lf->get_format_ptr()->get_pattern_name(
54,772✔
317
                lf->get_format_file_state().lffs_pattern_locks, cl))
×
318
    {
319
        return false;
×
320
    }
321

322
    if (lc.lc_level_constraint
54,772✔
323
        && !lc.lc_level_constraint->matches(lf_iter->get_msg_level()))
54,772✔
324
    {
325
        return false;
206✔
326
    }
327

328
    if (!lc.lc_log_path.empty()) {
54,566✔
329
        if (lf == lc.lc_last_log_path_match) {
4✔
330
        } else if (lf == lc.lc_last_log_path_mismatch) {
2✔
331
            return false;
×
332
        } else {
333
            for (const auto& path_cons : lc.lc_log_path) {
4✔
334
                if (!path_cons.matches(lf->get_filename())) {
2✔
335
                    lc.lc_last_log_path_mismatch = lf;
×
336
                    return false;
×
337
                }
338
            }
339
            lc.lc_last_log_path_match = lf;
2✔
340
        }
341
    }
342

343
    if (!lc.lc_unique_path.empty()) {
54,566✔
344
        if (lf == lc.lc_last_unique_path_match) {
×
345
        } else if (lf == lc.lc_last_unique_path_mismatch) {
×
346
            return false;
×
347
        } else {
348
            for (const auto& path_cons : lc.lc_unique_path) {
×
349
                if (!path_cons.matches(lf->get_unique_path())) {
×
350
                    lc.lc_last_unique_path_mismatch = lf;
×
351
                    return false;
×
352
                }
353
            }
354
            lc.lc_last_unique_path_match = lf;
×
355
        }
356
    }
357

358
    if (lc.lc_opid_bloom_bits
54,566✔
359
        && !lf_iter->match_bloom_bits(lc.lc_opid_bloom_bits.value()))
54,566✔
360
    {
361
        return false;
×
362
    }
363

364
    if (lc.lc_tid_bloom_bits
54,566✔
365
        && !lf_iter->match_bloom_bits(lc.lc_tid_bloom_bits.value()))
54,566✔
366
    {
367
        return false;
×
368
    }
369

370
    return true;
54,566✔
371
}
372

373
struct log_vtab {
374
    sqlite3_vtab base;
375
    sqlite3* db;
376
    textview_curses* tc{nullptr};
377
    logfile_sub_source* lss{nullptr};
378
    std::shared_ptr<log_vtab_impl> vi;
379

380
    size_t footer_index(log_footer_columns col) const
549✔
381
    {
382
        return VT_COL_MAX + this->vi->vi_column_count
549✔
383
            + lnav::enums::to_underlying(col);
549✔
384
    }
385
};
386

387
struct vtab_cursor {
388
    void cache_msg(logfile* lf, logfile::const_iterator ll)
33,250✔
389
    {
390
        if (this->log_msg_line == this->log_cursor.lc_curr_line) {
33,250✔
391
            return;
8✔
392
        }
393
        auto& sbr = this->line_values.lvv_sbr;
33,242✔
394
        lf->read_full_message(ll,
33,242✔
395
                              sbr,
396
                              this->log_cursor.lc_direction < 0
33,242✔
397
                                  ? line_buffer::scan_direction::backward
398
                                  : line_buffer::scan_direction::forward);
399
        sbr.erase_ansi();
33,242✔
400
        this->log_msg_line = this->log_cursor.lc_curr_line;
33,242✔
401
    }
402

403
    void invalidate()
34,243✔
404
    {
405
        this->attrs.clear();
34,243✔
406
        this->line_values.clear();
34,243✔
407
        this->log_msg_line = -1_vl;
34,243✔
408
    }
34,243✔
409

410
    sqlite3_vtab_cursor base;
411
    struct log_cursor log_cursor;
412
    vis_line_t log_msg_line{-1_vl};
413
    string_attrs_t attrs;
414
    logline_value_vector line_values;
415
};
416

417
static int vt_destructor(sqlite3_vtab* p_svt);
418

419
static int
420
vt_create(sqlite3* db,
64,368✔
421
          void* pAux,
422
          int argc,
423
          const char* const* argv,
424
          sqlite3_vtab** pp_vt,
425
          char** pzErr)
426
{
427
    auto* vm = (log_vtab_manager*) pAux;
64,368✔
428
    int rc = SQLITE_OK;
64,368✔
429
    /* Allocate the sqlite3_vtab/vtab structure itself */
430
    auto p_vt = std::make_unique<log_vtab>();
64,368✔
431

432
    p_vt->db = db;
64,368✔
433

434
    /* Declare the vtable's structure */
435
    p_vt->vi = vm->lookup_impl(intern_string::lookup(argv[3]));
128,736✔
436
    if (p_vt->vi == nullptr) {
64,368✔
437
        return SQLITE_ERROR;
×
438
    }
439
    p_vt->lss = vm->get_source();
64,368✔
440
    p_vt->tc = p_vt->lss->get_view();
64,368✔
441
    rc = sqlite3_declare_vtab(db, p_vt->vi->get_table_statement().c_str());
64,368✔
442

443
    if (rc == SQLITE_OK) {
64,368✔
444
        /* Success. Set *pp_vt and return */
445
        auto loose_p_vt = p_vt.release();
64,367✔
446
        *pp_vt = &loose_p_vt->base;
64,367✔
447
        log_debug("creating log format table: %s = %p", argv[3], loose_p_vt);
64,367✔
448
    } else {
449
        log_error("sqlite3_declare_vtab(%s) failed: %s",
1✔
450
                  p_vt->vi->get_name().c_str(),
451
                  sqlite3_errmsg(db));
452
    }
453

454
    return rc;
64,368✔
455
}
64,368✔
456

457
static int
458
vt_destructor(sqlite3_vtab* p_svt)
64,367✔
459
{
460
    log_vtab* p_vt = (log_vtab*) p_svt;
64,367✔
461

462
    log_debug("deleting log format table: %p", p_vt);
64,367✔
463

464
    delete p_vt;
64,367✔
465

466
    return SQLITE_OK;
64,367✔
467
}
468

469
static int
470
vt_connect(sqlite3* db,
24✔
471
           void* p_aux,
472
           int argc,
473
           const char* const* argv,
474
           sqlite3_vtab** pp_vt,
475
           char** pzErr)
476
{
477
    return vt_create(db, p_aux, argc, argv, pp_vt, pzErr);
24✔
478
}
479

480
static int
481
vt_disconnect(sqlite3_vtab* pVtab)
24✔
482
{
483
    return vt_destructor(pVtab);
24✔
484
}
485

486
static int
487
vt_destroy(sqlite3_vtab* p_vt)
64,343✔
488
{
489
    return vt_destructor(p_vt);
64,343✔
490
}
491

492
static int vt_next(sqlite3_vtab_cursor* cur);
493

494
static int
495
vt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor)
210✔
496
{
497
    log_vtab* p_vt = (log_vtab*) p_svt;
210✔
498

499
    p_vt->base.zErrMsg = nullptr;
210✔
500

501
    vtab_cursor* p_cur = new vtab_cursor();
210✔
502

503
    *pp_cursor = (sqlite3_vtab_cursor*) p_cur;
210✔
504

505
    p_cur->base.pVtab = p_svt;
210✔
506
    p_cur->log_cursor.lc_opid_bloom_bits = std::nullopt;
210✔
507
    p_cur->log_cursor.lc_tid_bloom_bits = std::nullopt;
210✔
508
    p_cur->log_cursor.lc_curr_line = 0_vl;
210✔
509
    p_cur->log_cursor.lc_direction = 1_vl;
210✔
510
    p_cur->log_cursor.lc_end_line = vis_line_t(p_vt->lss->text_line_count());
210✔
511
    p_cur->log_cursor.lc_sub_index = 0;
210✔
512

513
    for (auto& ld : *p_vt->lss) {
430✔
514
        auto* lf = ld->get_file_ptr();
220✔
515

516
        if (lf == nullptr) {
220✔
517
            continue;
×
518
        }
519

520
        lf->enable_cache();
220✔
521
    }
522

523
    return SQLITE_OK;
210✔
524
}
525

526
static int
527
vt_close(sqlite3_vtab_cursor* cur)
210✔
528
{
529
    auto* p_cur = (vtab_cursor*) cur;
210✔
530

531
    /* Free cursor struct. */
532
    delete p_cur;
210✔
533

534
    return SQLITE_OK;
210✔
535
}
536

537
static int
538
vt_eof(sqlite3_vtab_cursor* cur)
34,243✔
539
{
540
    auto* vc = (vtab_cursor*) cur;
34,243✔
541

542
    return vc->log_cursor.is_eof();
34,243✔
543
}
544

545
static void
546
populate_indexed_columns(vtab_cursor* vc, log_vtab* vt)
33,845✔
547
{
548
    if (vc->log_cursor.is_eof() || vc->log_cursor.lc_indexed_columns.empty()) {
33,845✔
549
        return;
3,028✔
550
    }
551

552
    logfile* lf = nullptr;
30,817✔
553

554
    for (const auto& ic : vc->log_cursor.lc_indexed_columns) {
3,254,468✔
555
        auto& ci = vt->vi->vi_column_indexes[ic.cc_column];
3,223,651✔
556
        const auto vl = vc->log_cursor.lc_curr_line;
3,223,651✔
557

558
        if (ci.ci_indexed_range.contains(vl)) {
3,223,651✔
559
            // the index already contains this column, nothing to do
560
            continue;
3,222,558✔
561
        }
562

563
        if (lf == nullptr) {
2,127✔
564
            const auto cl = vt->lss->at(vl);
2,127✔
565
            uint64_t line_number;
566
            auto ld = vt->lss->find_data(cl, line_number);
2,127✔
567
            lf = (*ld)->get_file_ptr();
2,127✔
568
            auto ll = lf->begin() + line_number;
2,127✔
569

570
            vc->cache_msg(lf, ll);
2,127✔
571
            require(vc->line_values.lvv_sbr.get_data() != nullptr);
2,127✔
572
            vt->vi->extract(lf, line_number, vc->attrs, vc->line_values);
2,127✔
573
        }
574

575
        auto sub_col = logline_value_meta::table_column{
576
            (size_t) (ic.cc_column - VT_COL_MAX)};
2,127✔
577
        auto lv_iter = find_if(vc->line_values.lvv_values.begin(),
2,127✔
578
                               vc->line_values.lvv_values.end(),
579
                               logline_value_col_eq(sub_col));
580
        if (lv_iter == vc->line_values.lvv_values.end()
2,127✔
581
            || lv_iter->lv_meta.lvm_kind == value_kind_t::VALUE_NULL)
2,127✔
582
        {
583
            continue;
1,034✔
584
        }
585

586
        auto value = lv_iter->to_string_fragment(ci.ci_string_arena);
1,093✔
587

588
#ifdef DEBUG_INDEXING
589
        log_debug("updated index for column %d %.*s -> %d",
590
                  ic.cc_column,
591
                  value.length(),
592
                  value.data(),
593
                  (int) vc->log_cursor.lc_curr_line);
594
#endif
595

596
        auto& line_deq = ci.ci_value_to_lines[value];
1,093✔
597
        if (line_deq.empty()
1,093✔
598
            || (line_deq.front() != vl && line_deq.back() != vl))
1,093✔
599
        {
600
            if (vc->log_cursor.lc_direction < 0) {
1,093✔
601
                line_deq.push_front(vl);
774✔
602
            } else {
603
                line_deq.push_back(vl);
319✔
604
            }
605
        }
606
    }
607
}
608

609
static int
610
vt_next(sqlite3_vtab_cursor* cur)
34,074✔
611
{
612
    auto* vc = (vtab_cursor*) cur;
34,074✔
613
    auto* vt = (log_vtab*) cur->pVtab;
34,074✔
614
    auto done = false;
34,074✔
615

616
#ifdef DEBUG_INDEXING
617
    log_debug("vt_next([%d:%d:%d])",
618
              vc->log_cursor.lc_curr_line,
619
              vc->log_cursor.lc_end_line,
620
              vc->log_cursor.lc_direction);
621
#endif
622

623
    vc->invalidate();
34,074✔
624
    if (!vc->log_cursor.lc_indexed_lines.empty()
34,074✔
625
        && vc->log_cursor.lc_indexed_lines_range.contains(
34,074✔
626
            vc->log_cursor.lc_curr_line))
627
    {
628
        vc->log_cursor.lc_curr_line = vc->log_cursor.lc_indexed_lines.back();
19,534✔
629
        vc->log_cursor.lc_indexed_lines.pop_back();
19,534✔
630
    } else {
631
        vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
14,540✔
632
    }
633
    vc->log_cursor.lc_sub_index = 0;
34,074✔
634
    do {
635
        log_cursor_latest = vc->log_cursor;
54,503✔
636
        if (((log_cursor_latest.lc_curr_line % 1024) == 0)
54,503✔
637
            && (log_vtab_data.lvd_progress != nullptr
54,834✔
638
                && log_vtab_data.lvd_progress(log_cursor_latest)))
331✔
639
        {
640
            break;
×
641
        }
642

643
        while (vc->log_cursor.lc_curr_line != -1_vl && !vc->log_cursor.is_eof()
109,934✔
644
               && !vt->vi->is_valid(vc->log_cursor, *vt->lss))
109,934✔
645
        {
646
            vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
465✔
647
            vc->log_cursor.lc_sub_index = 0;
465✔
648
        }
649
        if (vc->log_cursor.is_eof()) {
54,503✔
650
            log_info("vt_next at EOF (%d:%d:%d), scanned rows %lu",
398✔
651
                     (int) vc->log_cursor.lc_curr_line,
652
                     (int) vc->log_cursor.lc_end_line,
653
                     (int) vc->log_cursor.lc_direction,
654
                     vc->log_cursor.lc_scanned_rows);
655
            done = true;
398✔
656
        } else {
657
            done = vt->vi->next(vc->log_cursor, *vt->lss);
54,105✔
658
            if (done) {
54,105✔
659
                if (vc->log_cursor.lc_curr_line % 10000 == 0) {
33,676✔
660
                    log_debug("scanned %d", (int) vc->log_cursor.lc_curr_line);
321✔
661
                }
662
#ifdef DEBUG_INDEXING
663
                log_debug("scanned %d", vc->log_cursor.lc_curr_line);
664
#endif
665
                vc->log_cursor.lc_scanned_rows += 1;
33,676✔
666
                populate_indexed_columns(vc, vt);
33,676✔
667
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
33,676✔
668
                                          vc->log_cursor.lc_curr_line);
669
            } else {
670
                if (!vc->log_cursor.lc_indexed_lines.empty()
20,429✔
671
                    && vc->log_cursor.lc_indexed_lines_range.contains(
20,429✔
672
                        vc->log_cursor.lc_curr_line))
673
                {
674
                    vc->log_cursor.lc_curr_line
675
                        = vc->log_cursor.lc_indexed_lines.back();
×
676
                    vc->log_cursor.lc_indexed_lines.pop_back();
×
677
                } else {
678
                    vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
20,429✔
679
                }
680
                vc->log_cursor.lc_sub_index = 0;
20,429✔
681
            }
682
        }
683
    } while (!done);
54,503✔
684

685
#ifdef DEBUG_INDEXING
686
    log_debug("vt_next() -> [%d:%d:%d]",
687
              vc->log_cursor.lc_curr_line,
688
              vc->log_cursor.lc_end_line,
689
              vc->log_cursor.lc_direction);
690
#endif
691

692
    return SQLITE_OK;
34,074✔
693
}
694

695
static int
696
vt_next_no_rowid(sqlite3_vtab_cursor* cur)
169✔
697
{
698
    auto* vc = (vtab_cursor*) cur;
169✔
699
    auto* vt = (log_vtab*) cur->pVtab;
169✔
700
    auto done = false;
169✔
701

702
    vc->invalidate();
169✔
703
    do {
704
        log_cursor_latest = vc->log_cursor;
760✔
705
        if (((log_cursor_latest.lc_curr_line % 1024) == 0)
760✔
706
            && (log_vtab_data.lvd_progress != nullptr
792✔
707
                && log_vtab_data.lvd_progress(log_cursor_latest)))
32✔
708
        {
709
            break;
×
710
        }
711

712
        auto vl_before = vc->log_cursor.lc_curr_line;
760✔
713
        done = vt->vi->next(vc->log_cursor, *vt->lss);
760✔
714
        if (vl_before != vc->log_cursor.lc_curr_line) {
760✔
715
            vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
×
716
                                      vl_before);
717
        }
718
        if (done) {
760✔
719
            populate_indexed_columns(vc, vt);
169✔
720
        } else if (vc->log_cursor.is_eof()) {
591✔
721
            done = true;
×
722
        } else {
723
            require(vc->log_cursor.lc_curr_line
591✔
724
                    < (ssize_t) vt->lss->text_line_count());
725

726
            if (!vc->log_cursor.lc_indexed_lines.empty()
591✔
727
                && vc->log_cursor.lc_indexed_lines_range.contains(
591✔
728
                    vc->log_cursor.lc_curr_line))
729
            {
730
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
3✔
731
                                          vc->log_cursor.lc_curr_line);
732
                vc->log_cursor.lc_curr_line
733
                    = vc->log_cursor.lc_indexed_lines.back();
3✔
734
                vc->log_cursor.lc_indexed_lines.pop_back();
3✔
735
#ifdef DEBUG_INDEXING
736
                log_debug("going to next line from index %d",
737
                          (int) vc->log_cursor.lc_curr_line);
738
#endif
739
            } else {
740
                vt->vi->expand_indexes_to(vc->log_cursor.lc_indexed_columns,
588✔
741
                                          vc->log_cursor.lc_curr_line);
742
                vc->log_cursor.lc_curr_line += vc->log_cursor.lc_direction;
588✔
743
            }
744
            vc->log_cursor.lc_sub_index = 0;
591✔
745
        }
746
    } while (!done);
760✔
747

748
#ifdef DEBUG_INDEXING
749
    log_debug("vt_next_no_rowid() -> %d:%d",
750
              vc->log_cursor.lc_curr_line,
751
              vc->log_cursor.lc_end_line);
752
#endif
753

754
    return SQLITE_OK;
169✔
755
}
756

757
static int
758
vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
85,571✔
759
{
760
    auto* vc = (vtab_cursor*) cur;
85,571✔
761
    auto* vt = (log_vtab*) cur->pVtab;
85,571✔
762

763
#ifdef DEBUG_INDEXING
764
    log_debug("vt_column(%s, %d:%d)",
765
              vt->vi->get_name().get(),
766
              (int) vc->log_cursor.lc_curr_line,
767
              col);
768
#endif
769

770
    content_line_t cl(vt->lss->at(vc->log_cursor.lc_curr_line));
85,571✔
771
    uint64_t line_number;
772
    auto ld = vt->lss->find_data(cl, line_number);
85,571✔
773
    auto* lf = (*ld)->get_file_ptr();
85,571✔
774
    auto ll = lf->begin() + line_number;
85,571✔
775

776
    require(col >= 0);
85,571✔
777

778
    /* Just return the ordinal of the column requested. */
779
    switch (col) {
85,571✔
780
        case VT_COL_LINE_NUMBER: {
2,782✔
781
            sqlite3_result_int64(ctx, vc->log_cursor.lc_curr_line);
2,782✔
782
            break;
2,782✔
783
        }
784

785
        case VT_COL_LOG_TIME: {
405✔
786
            char buffer[64];
787

788
            sql_strftime(buffer, sizeof(buffer), ll->get_timeval());
405✔
789
            sqlite3_result_text(ctx, buffer, strlen(buffer), SQLITE_TRANSIENT);
405✔
790
            break;
405✔
791
        }
792

793
        case VT_COL_LEVEL: {
1,322✔
794
            const auto& level_name = ll->get_level_name();
1,322✔
795

796
            sqlite3_result_text(
1,322✔
797
                ctx, level_name.data(), level_name.length(), SQLITE_STATIC);
798
            break;
1,322✔
799
        }
800

801
        default:
81,062✔
802
            if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
81,062✔
803
                auto footer_column = static_cast<log_footer_columns>(
804
                    col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
6,086✔
805

806
                switch (footer_column) {
6,086✔
807
                    case log_footer_columns::partition: {
555✔
808
                        auto& vb = vt->tc->get_bookmarks();
555✔
809
                        const auto& bv = vb[&textview_curses::BM_PARTITION];
555✔
810

811
                        if (bv.empty()) {
555✔
812
                            sqlite3_result_null(ctx);
318✔
813
                        } else {
814
                            vis_line_t curr_line(vc->log_cursor.lc_curr_line);
237✔
815
                            auto iter
816
                                = bv.bv_tree.lower_bound(curr_line + 1_vl);
237✔
817

818
                            if (iter != bv.bv_tree.begin()) {
237✔
819
                                --iter;
185✔
820
                                auto line_meta_opt
821
                                    = vt->lss->find_bookmark_metadata(*iter);
185✔
822
                                if (line_meta_opt
185✔
823
                                    && !line_meta_opt.value()->bm_name.empty())
185✔
824
                                {
825
                                    sqlite3_result_text(
185✔
826
                                        ctx,
827
                                        line_meta_opt.value()->bm_name.c_str(),
185✔
828
                                        line_meta_opt.value()->bm_name.size(),
185✔
829
                                        SQLITE_TRANSIENT);
830
                                } else {
831
                                    sqlite3_result_null(ctx);
×
832
                                }
833
                            } else {
834
                                sqlite3_result_null(ctx);
52✔
835
                            }
836
                        }
837
                        break;
555✔
838
                    }
839
                    case log_footer_columns::actual_time: {
93✔
840
                        char buffer[64] = "";
93✔
841

842
                        if (ll->is_time_skewed()) {
93✔
843
                            if (vc->line_values.lvv_values.empty()) {
2✔
844
                                vc->cache_msg(lf, ll);
2✔
845
                                require(vc->line_values.lvv_sbr.get_data()
2✔
846
                                        != nullptr);
847
                                vt->vi->extract(lf,
2✔
848
                                                line_number,
849
                                                vc->attrs,
2✔
850
                                                vc->line_values);
2✔
851
                            }
852

853
                            struct line_range time_range;
2✔
854

855
                            time_range = find_string_attr_range(vc->attrs,
2✔
856
                                                                &L_TIMESTAMP);
857

858
                            const auto* time_src
859
                                = vc->line_values.lvv_sbr.get_data()
2✔
860
                                + time_range.lr_start;
2✔
861
                            struct timeval actual_tv;
862
                            struct exttm tm;
2✔
863

864
                            if (lf->get_format_ptr()->lf_date_time.scan(
4✔
865
                                    time_src,
866
                                    time_range.length(),
2✔
867
                                    lf->get_format_ptr()
868
                                        ->get_timestamp_formats(),
869
                                    &tm,
870
                                    actual_tv,
871
                                    false))
872
                            {
873
                                sql_strftime(buffer, sizeof(buffer), actual_tv);
2✔
874
                            }
875
                        } else {
876
                            sql_strftime(
91✔
877
                                buffer, sizeof(buffer), ll->get_timeval());
182✔
878
                        }
879
                        sqlite3_result_text(
93✔
880
                            ctx, buffer, strlen(buffer), SQLITE_TRANSIENT);
93✔
881
                        break;
93✔
882
                    }
883
                    case log_footer_columns::idle_msecs: {
314✔
884
                        if (vc->log_cursor.lc_curr_line == 0) {
314✔
885
                            sqlite3_result_int64(ctx, 0);
57✔
886
                        } else {
887
                            content_line_t prev_cl(vt->lss->at(
257✔
888
                                vis_line_t(vc->log_cursor.lc_curr_line - 1)));
257✔
889
                            auto prev_lf = vt->lss->find(prev_cl);
257✔
890
                            auto prev_ll = prev_lf->begin() + prev_cl;
257✔
891

892
                            auto prev_time
893
                                = prev_ll
894
                                      ->get_time<std::chrono::milliseconds>();
257✔
895
                            auto curr_line_time
896
                                = ll->get_time<std::chrono::milliseconds>();
257✔
897
                            // require(curr_line_time >= prev_time);
898
                            sqlite3_result_int64(
257✔
899
                                ctx,
900
                                curr_line_time.count() - prev_time.count());
257✔
901
                        }
257✔
902
                        break;
314✔
903
                    }
904
                    case log_footer_columns::mark: {
333✔
905
                        sqlite3_result_int(ctx, ll->is_marked());
333✔
906
                        break;
333✔
907
                    }
908
                    case log_footer_columns::sticky_mark: {
78✔
909
                        auto& sticky_bv = vt->tc->get_bookmarks()
78✔
910
                                              [&textview_curses::BM_STICKY];
78✔
911
                        sqlite3_result_int(
78✔
912
                            ctx,
913
                            sticky_bv.bv_tree.find(vc->log_cursor.lc_curr_line)
78✔
914
                                != sticky_bv.bv_tree.end());
156✔
915
                        break;
78✔
916
                    }
917
                    case log_footer_columns::comment: {
344✔
918
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
344✔
919
                            vc->log_cursor.lc_curr_line);
920
                        if (!line_meta_opt
344✔
921
                            || line_meta_opt.value()->bm_comment.empty())
344✔
922
                        {
923
                            sqlite3_result_null(ctx);
342✔
924
                        } else {
925
                            const auto& meta = *(line_meta_opt.value());
2✔
926
                            sqlite3_result_text(ctx,
2✔
927
                                                meta.bm_comment.c_str(),
928
                                                meta.bm_comment.length(),
2✔
929
                                                SQLITE_TRANSIENT);
930
                        }
931
                        break;
344✔
932
                    }
933
                    case log_footer_columns::tags: {
741✔
934
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
741✔
935
                            vc->log_cursor.lc_curr_line);
936
                        if (!line_meta_opt
741✔
937
                            || line_meta_opt.value()->bm_tags.empty())
741✔
938
                        {
939
                            sqlite3_result_null(ctx);
734✔
940
                        } else {
941
                            const auto& meta = *(line_meta_opt.value());
7✔
942

943
                            yajlpp_gen gen;
7✔
944

945
                            yajl_gen_config(gen, yajl_gen_beautify, false);
7✔
946

947
                            {
948
                                yajlpp_array arr(gen);
7✔
949

950
                                for (const auto& entry : meta.bm_tags) {
14✔
951
                                    arr.gen(entry.te_tag);
7✔
952
                                }
953
                            }
7✔
954

955
                            to_sqlite(ctx, json_string(gen));
7✔
956
                        }
7✔
957
                        break;
741✔
958
                    }
959
                    case log_footer_columns::annotations: {
347✔
960
                        if (sqlite3_vtab_nochange(ctx)) {
347✔
961
                            return SQLITE_OK;
74✔
962
                        }
963

964
                        auto line_meta_opt = vt->lss->find_bookmark_metadata(
273✔
965
                            vc->log_cursor.lc_curr_line);
966
                        if (!line_meta_opt
273✔
967
                            || line_meta_opt.value()
277✔
968
                                   ->bm_annotations.la_pairs.empty())
4✔
969
                        {
970
                            sqlite3_result_null(ctx);
269✔
971
                        } else {
972
                            const auto& meta = *(line_meta_opt.value());
4✔
973
                            to_sqlite(
4✔
974
                                ctx,
975
                                logmsg_annotations_handlers.to_json_string(
4✔
976
                                    meta.bm_annotations));
4✔
977
                        }
978
                        break;
273✔
979
                    }
980
                    case log_footer_columns::filters: {
314✔
981
                        const auto& filter_mask
982
                            = (*ld)->ld_filter_state.lfo_filter_state.tfs_mask;
314✔
983

984
                        if (!filter_mask[line_number]) {
314✔
985
                            sqlite3_result_null(ctx);
313✔
986
                        } else {
987
                            const auto& filters = vt->lss->get_filters();
1✔
988
                            yajlpp_gen gen;
1✔
989

990
                            yajl_gen_config(gen, yajl_gen_beautify, false);
1✔
991

992
                            {
993
                                yajlpp_array arr(gen);
1✔
994

995
                                for (const auto& filter : filters) {
2✔
996
                                    if (filter->lf_deleted) {
1✔
997
                                        continue;
×
998
                                    }
999

1000
                                    uint32_t mask
1001
                                        = (1UL << filter->get_index());
1✔
1002

1003
                                    if (filter_mask[line_number] & mask) {
1✔
1004
                                        arr.gen(filter->get_index());
1✔
1005
                                    }
1006
                                }
1007
                            }
1✔
1008

1009
                            to_sqlite(ctx, gen.to_string_fragment());
1✔
1010
                            sqlite3_result_subtype(ctx, JSON_SUBTYPE);
1✔
1011
                        }
1✔
1012
                        break;
314✔
1013
                    }
1014
                    case log_footer_columns::opid: {
92✔
1015
                        if (vc->line_values.lvv_values.empty()) {
92✔
1016
                            vc->cache_msg(lf, ll);
63✔
1017
                            require(vc->line_values.lvv_sbr.get_data()
63✔
1018
                                    != nullptr);
1019
                            vt->vi->extract(
63✔
1020
                                lf, line_number, vc->attrs, vc->line_values);
63✔
1021
                        }
1022

1023
                        if (vc->line_values.lvv_opid_value) {
92✔
1024
                            to_sqlite(ctx,
46✔
1025
                                      vc->line_values.lvv_opid_value.value());
46✔
1026
                        } else {
1027
                            sqlite3_result_null(ctx);
46✔
1028
                        }
1029
                        break;
92✔
1030
                    }
1031
                    case log_footer_columns::user_opid: {
111✔
1032
                        if (vc->line_values.lvv_values.empty()) {
111✔
1033
                            vc->cache_msg(lf, ll);
32✔
1034
                            require(vc->line_values.lvv_sbr.get_data()
32✔
1035
                                    != nullptr);
1036
                            vt->vi->extract(
32✔
1037
                                lf, line_number, vc->attrs, vc->line_values);
32✔
1038
                        }
1039

1040
                        if (vc->line_values.lvv_opid_value
111✔
1041
                            && vc->line_values.lvv_opid_provenance
111✔
1042
                                == logline_value_vector::opid_provenance::user)
111✔
1043
                        {
1044
                            to_sqlite(ctx,
3✔
1045
                                      vc->line_values.lvv_opid_value.value());
3✔
1046
                        } else {
1047
                            sqlite3_result_null(ctx);
108✔
1048
                        }
1049
                        break;
111✔
1050
                    }
1051
                    case log_footer_columns::opid_definition: {
80✔
1052
                        if (vc->line_values.lvv_values.empty()) {
80✔
1053
                            vc->cache_msg(lf, ll);
×
1054
                            require(vc->line_values.lvv_sbr.get_data()
×
1055
                                    != nullptr);
1056
                            vt->vi->extract(
×
1057
                                lf, line_number, vc->attrs, vc->line_values);
×
1058
                        }
1059

1060
                        if (vc->line_values.lvv_opid_value) {
80✔
1061
                            auto opids = lf->get_opids().readAccess();
33✔
1062

1063
                            auto iter = opids->los_opid_ranges.find(
66✔
1064
                                vc->line_values.lvv_opid_value.value());
33✔
1065
                            if (iter != opids->los_opid_ranges.end()
33✔
1066
                                && iter->second.otr_description.lod_index)
33✔
1067
                            {
1068
                                const auto& opid_def
1069
                                    = (*lf->get_format_ptr()
29✔
1070
                                            ->lf_opid_description_def_vec)
29✔
1071
                                        [iter->second.otr_description.lod_index
29✔
1072
                                             .value()];
29✔
1073
                                to_sqlite(ctx, opid_def->od_name);
29✔
1074
                            } else {
1075
                                sqlite3_result_null(ctx);
4✔
1076
                            }
1077
                        } else {
33✔
1078
                            sqlite3_result_null(ctx);
47✔
1079
                        }
1080
                        break;
80✔
1081
                    }
1082
                    case log_footer_columns::format: {
103✔
1083
                        auto format_name = lf->get_format_name();
103✔
1084
                        sqlite3_result_text(ctx,
103✔
1085
                                            format_name.get(),
1086
                                            format_name.size(),
103✔
1087
                                            SQLITE_STATIC);
1088
                        break;
103✔
1089
                    }
1090
                    case log_footer_columns::format_regex: {
80✔
1091
                        auto pat_name = lf->get_format_ptr()->get_pattern_name(
160✔
1092
                            lf->get_format_file_state().lffs_pattern_locks,
80✔
1093
                            line_number);
1094
                        sqlite3_result_text(ctx,
80✔
1095
                                            pat_name.get(),
1096
                                            pat_name.size(),
80✔
1097
                                            SQLITE_STATIC);
1098
                        break;
80✔
1099
                    }
1100
                    case log_footer_columns::time_msecs: {
1,479✔
1101
                        sqlite3_result_int64(
1,479✔
1102
                            ctx,
1103
                            ll->get_time<std::chrono::milliseconds>().count());
1,479✔
1104
                        break;
1,479✔
1105
                    }
1106
                    case log_footer_columns::path: {
84✔
1107
                        const auto& fn = lf->get_filename();
84✔
1108

1109
                        sqlite3_result_text(ctx,
84✔
1110
                                            fn.c_str(),
1111
                                            fn.native().length(),
84✔
1112
                                            SQLITE_STATIC);
1113
                        break;
84✔
1114
                    }
1115
                    case log_footer_columns::unique_path: {
84✔
1116
                        const auto& fn = lf->get_unique_path();
84✔
1117

1118
                        sqlite3_result_text(ctx,
84✔
1119
                                            fn.c_str(),
1120
                                            fn.native().length(),
84✔
1121
                                            SQLITE_STATIC);
1122
                        break;
84✔
1123
                    }
1124
                    case log_footer_columns::text: {
86✔
1125
                        shared_buffer_ref line;
86✔
1126

1127
                        lf->read_full_message(ll, line);
86✔
1128
                        line.erase_ansi();
86✔
1129
                        sqlite3_result_text(ctx,
86✔
1130
                                            line.get_data(),
1131
                                            line.length(),
86✔
1132
                                            SQLITE_TRANSIENT);
1133
                        break;
86✔
1134
                    }
86✔
1135
                    case log_footer_columns::body: {
245✔
1136
                        if (vc->line_values.lvv_values.empty()) {
245✔
1137
                            vc->cache_msg(lf, ll);
110✔
1138
                            require(vc->line_values.lvv_sbr.get_data()
110✔
1139
                                    != nullptr);
1140
                            vt->vi->extract(
110✔
1141
                                lf, line_number, vc->attrs, vc->line_values);
110✔
1142
                        }
1143

1144
                        auto body_range
1145
                            = find_string_attr_range(vc->attrs, &SA_BODY);
245✔
1146
                        if (!body_range.is_valid()) {
245✔
1147
                            sqlite3_result_null(ctx);
×
1148
                        } else {
1149
                            const char* msg_start
1150
                                = vc->line_values.lvv_sbr.get_data();
245✔
1151

1152
                            sqlite3_result_text(ctx,
245✔
1153
                                                &msg_start[body_range.lr_start],
245✔
1154
                                                body_range.length(),
1155
                                                SQLITE_TRANSIENT);
1156
                        }
1157
                        break;
245✔
1158
                    }
1159
                    case log_footer_columns::raw_text: {
94✔
1160
                        auto read_res = lf->read_raw_message(ll);
94✔
1161

1162
                        if (read_res.isErr()) {
94✔
1163
                            auto msg = fmt::format(
1164
                                FMT_STRING("unable to read line -- {}"),
×
1165
                                read_res.unwrapErr());
×
1166
                            sqlite3_result_error(
×
1167
                                ctx, msg.c_str(), msg.length());
×
1168
                        } else {
×
1169
                            auto sbr = read_res.unwrap();
94✔
1170

1171
                            sqlite3_result_text(ctx,
94✔
1172
                                                sbr.get_data(),
1173
                                                sbr.length(),
94✔
1174
                                                SQLITE_TRANSIENT);
1175
                        }
94✔
1176
                        break;
94✔
1177
                    }
94✔
1178
                    case log_footer_columns::line_hash: {
85✔
1179
                        auto lw
1180
                            = vt->lss->window_at(vc->log_cursor.lc_curr_line);
85✔
1181
                        for (const auto& li : *lw) {
85✔
1182
                            auto hash_res = li.get_line_hash();
85✔
1183
                            if (hash_res.isErr()) {
85✔
1184
                                auto msg = fmt::format(
1185
                                    FMT_STRING("unable to read line -- {}"),
×
1186
                                    hash_res.unwrapErr());
×
1187
                                sqlite3_result_error(
×
1188
                                    ctx, msg.c_str(), msg.length());
×
1189
                            } else {
×
1190
                                to_sqlite(ctx,
85✔
1191
                                          text_auto_buffer{hash_res.unwrap()});
170✔
1192
                            }
1193
                            break;
85✔
1194
                        }
170✔
1195
                        break;
85✔
1196
                    }
85✔
1197
                    case log_footer_columns::line_link: {
85✔
1198
                        auto anchor_opt = vt->lss->anchor_for_row(
85✔
1199
                            vc->log_cursor.lc_curr_line);
85✔
1200
                        if (anchor_opt) {
85✔
1201
                            to_sqlite(ctx, anchor_opt.value());
85✔
1202
                        } else {
1203
                            sqlite3_result_null(ctx);
×
1204
                        }
1205
                        break;
85✔
1206
                    }
85✔
1207
                    case log_footer_columns::src_file: {
87✔
1208
                        if (vc->line_values.lvv_values.empty()) {
87✔
1209
                            vc->cache_msg(lf, ll);
7✔
1210
                            require(vc->line_values.lvv_sbr.get_data()
7✔
1211
                                    != nullptr);
1212
                            vt->vi->extract(
7✔
1213
                                lf, line_number, vc->attrs, vc->line_values);
7✔
1214
                        }
1215

1216
                        to_sqlite(ctx, vc->line_values.lvv_src_file_value);
87✔
1217
                        break;
87✔
1218
                    }
1219
                    case log_footer_columns::src_line: {
87✔
1220
                        if (vc->line_values.lvv_values.empty()) {
87✔
1221
                            vc->cache_msg(lf, ll);
×
1222
                            require(vc->line_values.lvv_sbr.get_data()
×
1223
                                    != nullptr);
1224
                            vt->vi->extract(
×
1225
                                lf, line_number, vc->attrs, vc->line_values);
×
1226
                        }
1227

1228
                        to_sqlite(ctx, vc->line_values.lvv_src_line_value);
87✔
1229
                        break;
87✔
1230
                    }
1231
                    case log_footer_columns::thread_id: {
85✔
1232
                        if (vc->line_values.lvv_values.empty()) {
85✔
1233
                            vc->cache_msg(lf, ll);
5✔
1234
                            require(vc->line_values.lvv_sbr.get_data()
5✔
1235
                                    != nullptr);
1236
                            vt->vi->extract(
5✔
1237
                                lf, line_number, vc->attrs, vc->line_values);
5✔
1238
                        }
1239

1240
                        to_sqlite(ctx, vc->line_values.lvv_thread_id_value);
85✔
1241
                        break;
85✔
1242
                    }
1243
                }
1244
            } else {
1245
                if (vc->line_values.lvv_values.empty()) {
74,976✔
1246
                    vc->cache_msg(lf, ll);
30,904✔
1247
                    require(vc->line_values.lvv_sbr.get_data() != nullptr);
30,904✔
1248
                    vt->vi->extract(
30,904✔
1249
                        lf, line_number, vc->attrs, vc->line_values);
30,904✔
1250
                }
1251

1252
                auto sub_col = logline_value_meta::table_column{
1253
                    (size_t) (col - VT_COL_MAX)};
74,976✔
1254
                auto lv_iter = find_if(vc->line_values.lvv_values.begin(),
74,976✔
1255
                                       vc->line_values.lvv_values.end(),
1256
                                       logline_value_col_eq(sub_col));
1257

1258
                if (lv_iter != vc->line_values.lvv_values.end()) {
74,976✔
1259
                    if (!lv_iter->lv_meta.lvm_struct_name.empty()) {
74,351✔
1260
                        yajlpp_gen gen;
8✔
1261
                        yajl_gen_config(gen, yajl_gen_beautify, false);
8✔
1262

1263
                        {
1264
                            yajlpp_map root(gen);
8✔
1265

1266
                            for (const auto& lv_struct :
8✔
1267
                                 vc->line_values.lvv_values)
82✔
1268
                            {
1269
                                if (lv_struct.lv_meta.lvm_column != sub_col) {
66✔
1270
                                    continue;
39✔
1271
                                }
1272

1273
                                root.gen(lv_struct.lv_meta.lvm_name);
27✔
1274
                                switch (lv_struct.lv_meta.lvm_kind) {
27✔
1275
                                    case value_kind_t::VALUE_NULL:
2✔
1276
                                        root.gen();
2✔
1277
                                        break;
2✔
1278
                                    case value_kind_t::VALUE_BOOLEAN:
×
1279
                                        root.gen((bool) lv_struct.lv_value.i);
×
1280
                                        break;
×
1281
                                    case value_kind_t::VALUE_INTEGER:
×
1282
                                        root.gen(lv_struct.lv_value.i);
×
1283
                                        break;
×
1284
                                    case value_kind_t::VALUE_FLOAT:
×
1285
                                        root.gen(lv_struct.lv_value.d);
×
1286
                                        break;
×
1287
                                    case value_kind_t::VALUE_JSON: {
1✔
1288
                                        auto_mem<yajl_handle_t> parse_handle(
1289
                                            yajl_free);
1✔
1290
                                        json_ptr jp("");
1✔
1291
                                        json_op jo(jp);
1✔
1292

1293
                                        jo.jo_ptr_callbacks
1294
                                            = json_op::gen_callbacks;
1✔
1295
                                        jo.jo_ptr_data = gen;
1✔
1296
                                        parse_handle.reset(
1✔
1297
                                            yajl_alloc(&json_op::ptr_callbacks,
1298
                                                       nullptr,
1299
                                                       &jo));
1300

1301
                                        const auto* json_in
1302
                                            = (const unsigned char*)
1303
                                                  lv_struct.text_value();
1✔
1304
                                        auto json_len = lv_struct.text_length();
1✔
1305

1306
                                        if (yajl_parse(parse_handle.in(),
1✔
1307
                                                       json_in,
1308
                                                       json_len)
1309
                                                != yajl_status_ok
1310
                                            || yajl_complete_parse(
1✔
1311
                                                   parse_handle.in())
1312
                                                != yajl_status_ok)
1313
                                        {
1314
                                            log_error(
×
1315
                                                "failed to parse json value: "
1316
                                                "%.*s",
1317
                                                (int) lv_struct.text_length(),
1318
                                                lv_struct.text_value());
1319
                                            root.gen(lv_struct.to_string());
×
1320
                                        }
1321
                                        break;
1✔
1322
                                    }
1✔
1323
                                    default:
24✔
1324
                                        root.gen(lv_struct.to_string());
24✔
1325
                                        break;
24✔
1326
                                }
1327
                            }
1328
                        }
8✔
1329

1330
                        auto sf = gen.to_string_fragment();
8✔
1331
                        sqlite3_result_text(
8✔
1332
                            ctx, sf.data(), sf.length(), SQLITE_TRANSIENT);
1333
                        sqlite3_result_subtype(ctx, JSON_SUBTYPE);
8✔
1334
                    } else {
8✔
1335
                        switch (lv_iter->lv_meta.lvm_kind) {
74,343✔
1336
                            case value_kind_t::VALUE_NULL:
2,461✔
1337
                                sqlite3_result_null(ctx);
2,461✔
1338
                                break;
2,461✔
1339
                            case value_kind_t::VALUE_JSON: {
97✔
1340
                                sqlite3_result_text(ctx,
97✔
1341
                                                    lv_iter->text_value(),
1342
                                                    lv_iter->text_length(),
97✔
1343
                                                    SQLITE_TRANSIENT);
1344
                                sqlite3_result_subtype(ctx, JSON_SUBTYPE);
97✔
1345
                                break;
97✔
1346
                            }
1347
                            case value_kind_t::VALUE_STRUCT:
68,333✔
1348
                            case value_kind_t::VALUE_TEXT:
1349
                            case value_kind_t::VALUE_XML: {
1350
                                sqlite3_result_text(ctx,
68,333✔
1351
                                                    lv_iter->text_value(),
1352
                                                    lv_iter->text_length(),
68,333✔
1353
                                                    SQLITE_TRANSIENT);
1354
                                break;
68,333✔
1355
                            }
1356
                            case value_kind_t::VALUE_TIMESTAMP: {
11✔
1357
                                auto* fmt = lf->get_format_ptr();
11✔
1358
                                auto dts = fmt->build_time_scanner();
11✔
1359
                                exttm tm;
11✔
1360
                                timeval tv;
1361

1362
                                if (dts.scan(lv_iter->text_value(),
11✔
1363
                                             lv_iter->text_length(),
1364
                                             fmt->get_timestamp_formats(),
1365
                                             &tm,
1366
                                             tv))
1367
                                {
1368
                                    char buffer[64];
1369
                                    sql_strftime(buffer, sizeof(buffer), tv);
5✔
1370
                                    sqlite3_result_text(ctx,
5✔
1371
                                                        buffer,
1372
                                                        strlen(buffer),
5✔
1373
                                                        SQLITE_TRANSIENT);
1374
                                } else {
1375
                                    sqlite3_result_text(ctx,
6✔
1376
                                                        lv_iter->text_value(),
1377
                                                        lv_iter->text_length(),
6✔
1378
                                                        SQLITE_TRANSIENT);
1379
                                }
1380
                                break;
11✔
1381
                            }
1382
                            case value_kind_t::VALUE_W3C_QUOTED:
20✔
1383
                            case value_kind_t::VALUE_QUOTED:
1384
                                if (lv_iter->text_length() == 0) {
20✔
1385
                                    sqlite3_result_text(
×
1386
                                        ctx, "", 0, SQLITE_STATIC);
1387
                                } else {
1388
                                    const char* text_value
1389
                                        = lv_iter->text_value();
20✔
1390
                                    size_t text_len = lv_iter->text_length();
20✔
1391

1392
                                    switch (text_value[0]) {
20✔
1393
                                        case '\'':
7✔
1394
                                        case '"': {
1395
                                            char* val = (char*) sqlite3_malloc(
7✔
1396
                                                text_len);
1397

1398
                                            if (val == nullptr) {
7✔
1399
                                                sqlite3_result_error_nomem(ctx);
×
1400
                                            } else {
1401
                                                auto unquote_func
1402
                                                    = lv_iter->lv_meta.lvm_kind
7✔
1403
                                                        == value_kind_t::
1404
                                                            VALUE_W3C_QUOTED
1405
                                                    ? unquote_w3c
7✔
1406
                                                    : unquote;
7✔
1407

1408
                                                size_t unquoted_len
1409
                                                    = unquote_func(val,
7✔
1410
                                                                   text_value,
1411
                                                                   text_len);
1412
                                                sqlite3_result_text(
7✔
1413
                                                    ctx,
1414
                                                    val,
1415
                                                    unquoted_len,
1416
                                                    sqlite3_free);
1417
                                            }
1418
                                            break;
7✔
1419
                                        }
1420
                                        default: {
13✔
1421
                                            sqlite3_result_text(
13✔
1422
                                                ctx,
1423
                                                text_value,
1424
                                                lv_iter->text_length(),
13✔
1425
                                                SQLITE_TRANSIENT);
1426
                                            break;
13✔
1427
                                        }
1428
                                    }
1429
                                }
1430
                                break;
20✔
1431

1432
                            case value_kind_t::VALUE_BOOLEAN:
2,834✔
1433
                            case value_kind_t::VALUE_INTEGER:
1434
                                sqlite3_result_int64(ctx, lv_iter->lv_value.i);
2,834✔
1435
                                break;
2,834✔
1436

1437
                            case value_kind_t::VALUE_FLOAT:
587✔
1438
                                sqlite3_result_double(ctx, lv_iter->lv_value.d);
587✔
1439
                                break;
587✔
1440

1441
                            case value_kind_t::VALUE_UNKNOWN:
×
1442
                            case value_kind_t::VALUE__MAX:
1443
                                require(0);
×
1444
                                break;
1445
                        }
1446
                    }
1447
                } else {
1448
                    sqlite3_result_null(ctx);
625✔
1449
                }
1450
            }
1451
            break;
80,988✔
1452
    }
1453

1454
    return SQLITE_OK;
85,497✔
1455
}
1456

1457
static int
1458
vt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid)
159✔
1459
{
1460
    vtab_cursor* p_cur = (vtab_cursor*) cur;
159✔
1461

1462
    *p_rowid = (((uint64_t) p_cur->log_cursor.lc_curr_line) << 8)
159✔
1463
        | (p_cur->log_cursor.lc_sub_index & 0xff);
159✔
1464

1465
    return SQLITE_OK;
159✔
1466
}
1467

1468
void
1469
log_cursor::update(unsigned char op, vis_line_t vl, constraint_t cons)
100✔
1470
{
1471
    switch (op) {
100✔
1472
        case SQLITE_INDEX_CONSTRAINT_EQ:
75✔
1473
            if (vl < 0_vl) {
75✔
1474
                this->lc_curr_line = this->lc_end_line;
×
1475
            } else if (vl < this->lc_end_line) {
75✔
1476
                this->lc_curr_line = vl;
70✔
1477
                if (cons == constraint_t::unique) {
70✔
1478
                    this->lc_end_line = this->lc_curr_line + 1_vl;
70✔
1479
                }
1480
            }
1481
            break;
75✔
1482
        case SQLITE_INDEX_CONSTRAINT_GE:
3✔
1483
            if (vl < 0_vl) {
3✔
1484
                vl = 0_vl;
×
1485
            }
1486
            this->lc_curr_line = vl;
3✔
1487
            break;
3✔
1488
        case SQLITE_INDEX_CONSTRAINT_GT:
8✔
1489
            if (vl < 0_vl) {
8✔
1490
                this->lc_curr_line = 0_vl;
2✔
1491
            } else {
1492
                this->lc_curr_line
1493
                    = vl + (cons == constraint_t::unique ? 1_vl : 0_vl);
6✔
1494
            }
1495
            break;
8✔
1496
        case SQLITE_INDEX_CONSTRAINT_LE:
4✔
1497
            if (vl < 0_vl) {
4✔
1498
                this->lc_curr_line = this->lc_end_line;
×
1499
            } else if (vl < this->lc_end_line) {
4✔
1500
                this->lc_end_line
1501
                    = vl + (cons == constraint_t::unique ? 1_vl : 0_vl);
2✔
1502
            }
1503
            break;
4✔
1504
        case SQLITE_INDEX_CONSTRAINT_LT:
10✔
1505
            if (vl <= 0_vl) {
10✔
1506
                this->lc_curr_line = this->lc_end_line;
2✔
1507
            } else if (this->lc_direction > 0) {
8✔
1508
                if (vl < this->lc_end_line) {
×
1509
                    this->lc_end_line = vl;
×
1510
                }
1511
            } else if (this->lc_direction < 0) {
8✔
1512
                if (vl <= this->lc_curr_line) {
8✔
1513
                    this->lc_curr_line = vl - 1_vl;
8✔
1514
                }
1515
            }
1516
            break;
10✔
1517
    }
1518
#ifdef DEBUG_INDEXING
1519
    log_debug("log_cursor::update(%s, %d) -> (%d:%d:%d)",
1520
              sql_constraint_op_name(op),
1521
              vl,
1522
              this->lc_curr_line,
1523
              this->lc_end_line,
1524
              this->lc_direction);
1525
#endif
1526
}
100✔
1527

1528
log_cursor::string_constraint::string_constraint(unsigned char op,
215✔
1529
                                                 std::string value)
215✔
1530
    : sc_op(op), sc_value(std::move(value))
215✔
1531
{
1532
    if (op == SQLITE_INDEX_CONSTRAINT_REGEXP) {
215✔
1533
        auto compile_res = lnav::pcre2pp::code::from(this->sc_value);
×
1534

1535
        if (compile_res.isErr()) {
×
1536
            auto ce = compile_res.unwrapErr();
×
1537
            log_error("unable to compile regexp constraint: %s -- %s",
×
1538
                      this->sc_value.c_str(),
1539
                      ce.get_message().c_str());
1540
        } else {
×
1541
            this->sc_pattern = compile_res.unwrap().to_shared();
×
1542
        }
1543
    }
1544
}
215✔
1545

1546
bool
1547
log_cursor::string_constraint::matches(const std::string& sf) const
4✔
1548
{
1549
    switch (this->sc_op) {
4✔
1550
        case SQLITE_INDEX_CONSTRAINT_EQ:
×
1551
        case SQLITE_INDEX_CONSTRAINT_IS:
1552
            return sf == this->sc_value;
×
1553
        case SQLITE_INDEX_CONSTRAINT_NE:
×
1554
        case SQLITE_INDEX_CONSTRAINT_ISNOT:
1555
            return sf != this->sc_value;
×
1556
        case SQLITE_INDEX_CONSTRAINT_GT:
×
1557
            return sf > this->sc_value;
×
1558
        case SQLITE_INDEX_CONSTRAINT_LE:
×
1559
            return sf <= this->sc_value;
×
1560
        case SQLITE_INDEX_CONSTRAINT_LT:
×
1561
            return sf < this->sc_value;
×
1562
        case SQLITE_INDEX_CONSTRAINT_GE:
×
1563
            return sf >= this->sc_value;
×
1564
        case SQLITE_INDEX_CONSTRAINT_LIKE:
×
1565
            return sqlite3_strlike(this->sc_value.c_str(), sf.data(), 0) == 0;
×
1566
        case SQLITE_INDEX_CONSTRAINT_GLOB:
4✔
1567
            return sqlite3_strglob(this->sc_value.c_str(), sf.data()) == 0;
4✔
1568
        case SQLITE_INDEX_CONSTRAINT_REGEXP: {
×
1569
            if (this->sc_pattern != nullptr) {
×
1570
                return this->sc_pattern->find_in(sf, PCRE2_NO_UTF_CHECK)
×
1571
                    .ignore_error()
×
1572
                    .has_value();
×
1573
            }
1574
            // return true here so that the regexp is actually run and fails
1575
            return true;
×
1576
        }
1577
        case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
×
1578
            return true;
×
1579
        default:
×
1580
            return false;
×
1581
    }
1582
}
1583

1584
struct vtab_time_range {
1585
    std::optional<std::chrono::microseconds> vtr_begin;
1586
    std::optional<std::chrono::microseconds> vtr_end;
1587

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

1590
    void add(const std::chrono::microseconds& us)
7✔
1591
    {
1592
        if (!this->vtr_begin || us < this->vtr_begin) {
7✔
1593
            this->vtr_begin = us;
3✔
1594
        }
1595
        if (!this->vtr_end || this->vtr_end < us) {
7✔
1596
            this->vtr_end = us;
6✔
1597
        }
1598
    }
7✔
1599
};
1600

1601
static int
1602
vt_filter(sqlite3_vtab_cursor* p_vtc,
429✔
1603
          int idxNum,
1604
          const char* idxStr,
1605
          int argc,
1606
          sqlite3_value** argv)
1607
{
1608
    auto* p_cur = (vtab_cursor*) p_vtc;
429✔
1609
    auto* vt = (log_vtab*) p_vtc->pVtab;
429✔
1610
    sqlite3_index_info::sqlite3_index_constraint* index = nullptr;
429✔
1611

1612
    if (idxStr != nullptr) {
429✔
1613
        auto desc_len = strlen(idxStr);
418✔
1614
        auto index_len = idxNum * sizeof(*index);
418✔
1615
        auto storage_len = desc_len + 128 + index_len;
418✔
1616
        auto direction_storage
418✔
1617
            = static_cast<const char*>(idxStr) + desc_len + 1;
418✔
1618
        p_cur->log_cursor.lc_direction = vis_line_t(direction_storage[0]);
418✔
1619
        auto* remaining_storage = const_cast<void*>(
418✔
1620
            static_cast<const void*>(idxStr + desc_len + 1 + 1));
418✔
1621
        auto* index_storage
1622
            = std::align(alignof(sqlite3_index_info::sqlite3_index_constraint),
418✔
1623
                         index_len,
1624
                         remaining_storage,
1625
                         storage_len);
1626
        index = static_cast<sqlite3_index_info::sqlite3_index_constraint*>(
418✔
1627
            index_storage);
1628
    } else {
1629
        p_cur->log_cursor.lc_direction = 1_vl;
11✔
1630
    }
1631

1632
#ifdef DEBUG_INDEXING
1633
    log_info("vt_filter(%s, %d, direction=%d)",
1634
             vt->vi->get_name().get(),
1635
             idxNum,
1636
             p_cur->log_cursor.lc_direction);
1637
    log_info("  index storage: %p", index);
1638
#endif
1639
    p_cur->log_cursor.lc_format_name.clear();
429✔
1640
    p_cur->log_cursor.lc_pattern_name.clear();
429✔
1641
    p_cur->log_cursor.lc_opid_bloom_bits = std::nullopt;
429✔
1642
    p_cur->log_cursor.lc_tid_bloom_bits = std::nullopt;
429✔
1643
    p_cur->log_cursor.lc_level_constraint = std::nullopt;
429✔
1644
    p_cur->log_cursor.lc_log_path.clear();
429✔
1645
    p_cur->log_cursor.lc_last_log_path_match = nullptr;
429✔
1646
    p_cur->log_cursor.lc_last_log_path_mismatch = nullptr;
429✔
1647
    p_cur->log_cursor.lc_unique_path.clear();
429✔
1648
    p_cur->log_cursor.lc_last_unique_path_match = nullptr;
429✔
1649
    p_cur->log_cursor.lc_last_unique_path_mismatch = nullptr;
429✔
1650
    if (p_cur->log_cursor.lc_direction < 0) {
429✔
1651
        p_cur->log_cursor.lc_curr_line
1652
            = vis_line_t(vt->lss->text_line_count() - 1);
8✔
1653
        p_cur->log_cursor.lc_end_line = -1_vl;
8✔
1654
    } else {
1655
        p_cur->log_cursor.lc_curr_line = 0_vl;
421✔
1656
        p_cur->log_cursor.lc_end_line = vis_line_t(vt->lss->text_line_count());
421✔
1657
    }
1658
    p_cur->log_cursor.lc_scanned_rows = 0;
429✔
1659
    p_cur->log_cursor.lc_indexed_lines.clear();
429✔
1660
    p_cur->log_cursor.lc_indexed_lines_range = msg_range::empty();
429✔
1661

1662
    std::optional<vtab_time_range> log_time_range;
429✔
1663
    std::optional<uint64_t> opid_val;
429✔
1664
    std::optional<uint64_t> tid_val;
429✔
1665
    std::vector<log_cursor::string_constraint> log_path_constraints;
429✔
1666
    std::vector<log_cursor::string_constraint> log_unique_path_constraints;
429✔
1667

1668
    for (int lpc = 0; lpc < idxNum; lpc++) {
757✔
1669
        auto col = index[lpc].iColumn;
328✔
1670
        auto op = index[lpc].op;
328✔
1671
        switch (col) {
328✔
1672
            case VT_COL_LINE_NUMBER: {
98✔
1673
                auto vl = vis_line_t(sqlite3_value_int64(argv[lpc]));
98✔
1674

1675
                p_cur->log_cursor.update(
98✔
1676
                    op, vl, log_cursor::constraint_t::unique);
1677
                break;
98✔
1678
            }
1679
            case VT_COL_LEVEL: {
7✔
1680
                if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
7✔
1681
                    continue;
×
1682
                }
1683

1684
                auto sf = from_sqlite<string_fragment>()(argc, argv, lpc);
7✔
1685
                auto level = string2level(sf.data(), sf.length());
7✔
1686

1687
                p_cur->log_cursor.lc_level_constraint
1688
                    = log_cursor::level_constraint{
7✔
1689
                        op,
1690
                        level,
1691
                    };
7✔
1692
                break;
7✔
1693
            }
1694

1695
            case VT_COL_LOG_TIME:
2✔
1696
                if (sqlite3_value_type(argv[lpc]) == SQLITE3_TEXT) {
2✔
1697
                    const auto* datestr
1698
                        = (const char*) sqlite3_value_text(argv[lpc]);
1✔
1699
                    auto datelen = sqlite3_value_bytes(argv[lpc]);
1✔
1700
                    date_time_scanner dts;
1✔
1701
                    timeval tv;
1702
                    exttm mytm;
1✔
1703

1704
                    const auto* date_end
1705
                        = dts.scan(datestr, datelen, nullptr, &mytm, tv);
1✔
1706
                    auto us = to_us(tv);
1✔
1707
                    if (date_end != (datestr + datelen)) {
1✔
1708
                        log_warning(
×
1709
                            "  log_time constraint is not a valid datetime, "
1710
                            "index will not be applied: %s",
1711
                            datestr);
1712
                    } else {
1713
                        switch (op) {
1✔
1714
                            case SQLITE_INDEX_CONSTRAINT_EQ:
×
1715
                            case SQLITE_INDEX_CONSTRAINT_IS:
1716
                                if (!log_time_range) {
×
1717
                                    log_time_range = vtab_time_range{};
×
1718
                                }
1719
                                log_time_range->add(us);
×
1720
                                break;
×
1721
                            case SQLITE_INDEX_CONSTRAINT_GT:
1✔
1722
                            case SQLITE_INDEX_CONSTRAINT_GE:
1723
                                if (!log_time_range) {
1✔
1724
                                    log_time_range = vtab_time_range{};
1✔
1725
                                }
1726
                                log_time_range->vtr_begin = us;
1✔
1727
                                break;
1✔
1728
                            case SQLITE_INDEX_CONSTRAINT_LT:
×
1729
                            case SQLITE_INDEX_CONSTRAINT_LE:
1730
                                if (!log_time_range) {
×
1731
                                    log_time_range = vtab_time_range{};
×
1732
                                }
1733
                                log_time_range->vtr_end = us;
×
1734
                                break;
×
1735
                        }
1736
                    }
1737
                } else {
1738
                    log_warning(
1✔
1739
                        "  log_time constraint is not text, index will not be "
1740
                        "applied: value_type(%d)=%d",
1741
                        lpc,
1742
                        sqlite3_value_type(argv[lpc]));
1743
                }
1744
                break;
2✔
1745
            default: {
221✔
1746
                if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
221✔
1747
                    auto footer_column = static_cast<log_footer_columns>(
1748
                        col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
7✔
1749

1750
                    switch (footer_column) {
7✔
1751
                        case log_footer_columns::time_msecs: {
1✔
1752
                            auto msecs = std::chrono::milliseconds{
1753
                                sqlite3_value_int64(argv[lpc])};
1✔
1754
                            switch (op) {
1✔
1755
                                case SQLITE_INDEX_CONSTRAINT_EQ:
1✔
1756
                                case SQLITE_INDEX_CONSTRAINT_IS:
1757
                                    if (!log_time_range) {
1✔
1758
                                        log_time_range = vtab_time_range{};
1✔
1759
                                    }
1760
                                    log_time_range->add(msecs);
1✔
1761
                                    break;
1✔
1762
                                case SQLITE_INDEX_CONSTRAINT_GT:
×
1763
                                case SQLITE_INDEX_CONSTRAINT_GE:
1764
                                    if (!log_time_range) {
×
1765
                                        log_time_range = vtab_time_range{};
×
1766
                                    }
1767
                                    log_time_range->vtr_begin = msecs;
×
1768
                                    break;
×
1769
                                case SQLITE_INDEX_CONSTRAINT_LT:
×
1770
                                case SQLITE_INDEX_CONSTRAINT_LE:
1771
                                    if (!log_time_range) {
×
1772
                                        log_time_range = vtab_time_range{};
×
1773
                                    }
1774
                                    log_time_range->vtr_end = msecs;
×
1775
                                    break;
×
1776
                            }
1777
                            break;
1✔
1778
                        }
1779
                        case log_footer_columns::format: {
2✔
1780
                            const auto* format_name_str
1781
                                = (const char*) sqlite3_value_text(argv[lpc]);
2✔
1782

1783
                            if (format_name_str != nullptr) {
2✔
1784
                                p_cur->log_cursor.lc_format_name
1785
                                    = intern_string::lookup(format_name_str);
4✔
1786
                            }
1787
                            break;
2✔
1788
                        }
1789
                        case log_footer_columns::format_regex: {
×
1790
                            const auto* pattern_name_str
1791
                                = (const char*) sqlite3_value_text(argv[lpc]);
×
1792

1793
                            if (pattern_name_str != nullptr) {
×
1794
                                p_cur->log_cursor.lc_pattern_name
1795
                                    = intern_string::lookup(pattern_name_str);
×
1796
                            }
1797
                            break;
×
1798
                        }
1799
                        case log_footer_columns::opid:
1✔
1800
                        case log_footer_columns::user_opid: {
1801
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
1✔
1802
                                continue;
×
1803
                            }
1804
                            auto opid = from_sqlite<string_fragment>()(
1✔
1805
                                argc, argv, lpc);
1806
                            if (!log_time_range) {
1✔
1807
                                log_time_range = vtab_time_range{};
1✔
1808
                            }
1809
                            for (const auto& file_data : *vt->lss) {
2✔
1810
                                if (file_data->get_file_ptr() == nullptr) {
1✔
1811
                                    continue;
×
1812
                                }
1813
                                safe::ReadAccess<logfile::safe_opid_state>
1814
                                    r_opid_map(
1815
                                        file_data->get_file_ptr()->get_opids());
1✔
1816
                                const auto& iter
1817
                                    = r_opid_map->los_opid_ranges.find(opid);
1✔
1818
                                if (iter == r_opid_map->los_opid_ranges.end()) {
1✔
1819
                                    continue;
×
1820
                                }
1821
                                log_time_range->add(
1✔
1822
                                    iter->second.otr_range.tr_begin);
1✔
1823
                                log_time_range->add(
1✔
1824
                                    iter->second.otr_range.tr_end);
1✔
1825
                            }
1✔
1826

1827
                            opid_val = opid.bloom_bits();
1✔
1828
                            break;
1✔
1829
                        }
1830
                        case log_footer_columns::path: {
1✔
1831
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
1✔
1832
                                continue;
×
1833
                            }
1834

1835
                            const auto filename
1836
                                = from_sqlite<std::string>()(argc, argv, lpc);
1✔
1837
                            const auto fn_constraint
1838
                                = log_cursor::string_constraint{op, filename};
1✔
1839
                            auto found = false;
1✔
1840

1841
                            if (!log_time_range) {
1✔
1842
                                log_time_range = vtab_time_range{};
1✔
1843
                            }
1844
                            for (const auto& file_data : *vt->lss) {
3✔
1845
                                auto* lf = file_data->get_file_ptr();
2✔
1846
                                if (lf == nullptr) {
2✔
1847
                                    continue;
×
1848
                                }
1849
                                if (fn_constraint.matches(lf->get_filename())) {
2✔
1850
                                    found = true;
2✔
1851
                                    log_time_range->add(
4✔
1852
                                        lf->front()
2✔
1853
                                            .get_time<
1854
                                                std::chrono::microseconds>());
2✔
1855
                                    log_time_range->add(
4✔
1856
                                        lf->back()
2✔
1857
                                            .get_time<
1858
                                                std::chrono::microseconds>());
4✔
1859
                                }
1860
                            }
1861
                            if (found) {
1✔
1862
                                log_path_constraints.emplace_back(
1✔
1863
                                    fn_constraint);
1864
                            }
1865
                            break;
1✔
1866
                        }
1✔
1867
                        case log_footer_columns::unique_path: {
×
1868
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
×
1869
                                continue;
×
1870
                            }
1871

1872
                            const auto filename
1873
                                = from_sqlite<std::string>()(argc, argv, lpc);
×
1874
                            const auto fn_constraint
1875
                                = log_cursor::string_constraint{op, filename};
×
1876
                            auto found = false;
×
1877

1878
                            if (!log_time_range) {
×
1879
                                log_time_range = vtab_time_range{};
×
1880
                            }
1881
                            for (const auto& file_data : *vt->lss) {
×
1882
                                auto* lf = file_data->get_file_ptr();
×
1883
                                if (lf == nullptr) {
×
1884
                                    continue;
×
1885
                                }
1886
                                if (fn_constraint.matches(
×
1887
                                        lf->get_unique_path()))
×
1888
                                {
1889
                                    found = true;
×
1890
                                    log_time_range->add(
×
1891
                                        lf->front()
×
1892
                                            .get_time<
1893
                                                std::chrono::microseconds>());
×
1894
                                    log_time_range->add(
×
1895
                                        lf->back()
×
1896
                                            .get_time<
1897
                                                std::chrono::microseconds>());
×
1898
                                }
1899
                            }
1900
                            if (found) {
×
1901
                                log_unique_path_constraints.emplace_back(
×
1902
                                    fn_constraint);
1903
                            }
1904
                            break;
×
1905
                        }
1906
                        case log_footer_columns::partition:
×
1907
                        case log_footer_columns::actual_time:
1908
                        case log_footer_columns::idle_msecs:
1909
                        case log_footer_columns::mark:
1910
                        case log_footer_columns::sticky_mark:
1911
                        case log_footer_columns::comment:
1912
                        case log_footer_columns::tags:
1913
                        case log_footer_columns::annotations:
1914
                        case log_footer_columns::filters:
1915
                        case log_footer_columns::text:
1916
                        case log_footer_columns::body:
1917
                        case log_footer_columns::raw_text:
1918
                        case log_footer_columns::line_hash:
1919
                        case log_footer_columns::opid_definition:
1920
                        case log_footer_columns::src_file:
1921
                        case log_footer_columns::src_line:
1922
                            break;
×
1923
                        case log_footer_columns::line_link: {
2✔
1924
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
2✔
1925
                                continue;
×
1926
                            }
1927
                            auto permalink
1928
                                = from_sqlite<std::string>()(argc, argv, lpc);
2✔
1929
                            auto row_opt = vt->lss->row_for_anchor(permalink);
2✔
1930
                            if (row_opt) {
2✔
1931
                                p_cur->log_cursor.update(
4✔
1932
                                    op,
1933
                                    row_opt.value(),
2✔
1934
                                    log_cursor::constraint_t::unique);
1935
                            } else {
1936
                                log_trace("could not find link: %s",
×
1937
                                          permalink.c_str());
1938
                            }
1939
                            break;
2✔
1940
                        }
2✔
1941
                        case log_footer_columns::thread_id: {
×
1942
                            if (sqlite3_value_type(argv[lpc]) != SQLITE3_TEXT) {
×
1943
                                continue;
×
1944
                            }
1945
                            auto tid = from_sqlite<string_fragment>()(
×
1946
                                argc, argv, lpc);
1947
                            if (!log_time_range) {
×
1948
                                log_time_range = vtab_time_range{};
×
1949
                            }
1950
                            for (const auto& file_data : *vt->lss) {
×
1951
                                if (file_data->get_file_ptr() == nullptr) {
×
1952
                                    continue;
×
1953
                                }
1954
                                safe::ReadAccess<logfile::safe_thread_id_state>
1955
                                    r_tid_map(file_data->get_file_ptr()
1956
                                                  ->get_thread_ids());
×
1957
                                const auto& iter
1958
                                    = r_tid_map->ltis_tid_ranges.find(tid);
×
1959
                                if (iter == r_tid_map->ltis_tid_ranges.end()) {
×
1960
                                    continue;
×
1961
                                }
1962
                                log_time_range->add(
×
1963
                                    iter->second.titr_range.tr_begin);
×
1964
                                log_time_range->add(
×
1965
                                    iter->second.titr_range.tr_end);
×
1966
                            }
1967

1968
                            tid_val = tid.bloom_bits();
×
1969
                            break;
×
1970
                        }
1971
                    }
1972
                } else {
1973
                    const auto* value
1974
                        = (const char*) sqlite3_value_text(argv[lpc]);
214✔
1975

1976
                    if (value != nullptr) {
214✔
1977
                        auto value_len
1978
                            = (size_t) sqlite3_value_bytes(argv[lpc]);
214✔
1979

1980
#ifdef DEBUG_INDEXING
1981
                        log_debug("adding index request for column %d = %s",
1982
                                  col,
1983
                                  value);
1984
#endif
1985

1986
                        p_cur->log_cursor.lc_indexed_columns.emplace_back(
214✔
1987
                            col,
1988
                            log_cursor::string_constraint{
428✔
1989
                                op,
1990
                                std::string{value, value_len},
856✔
1991
                            });
1992
                    }
1993
                }
1994
                break;
221✔
1995
            }
1996
        }
1997
    }
1998

1999
    if (p_cur->log_cursor.lc_curr_line == p_cur->log_cursor.lc_end_line) {
429✔
2000
    } else if (!p_cur->log_cursor.lc_indexed_columns.empty()) {
423✔
2001
        auto min_index_range = msg_range::invalid();
214✔
2002
        auto scan_range
2003
            = msg_range::empty()
214✔
2004
                  .expand_to(p_cur->log_cursor.lc_curr_line)
214✔
2005
                  .expand_to(
214✔
2006
                      p_cur->log_cursor.lc_end_line
2007
                      - (p_cur->log_cursor.lc_direction > 0 ? 1_vl : -1_vl))
214✔
2008
                  .get_valid()
214✔
2009
                  .value();
214✔
2010
        for (const auto& icol : p_cur->log_cursor.lc_indexed_columns) {
19,734✔
2011
            auto& coli = vt->vi->vi_column_indexes[icol.cc_column];
19,520✔
2012
            if (coli.ci_index_generation != vt->lss->lss_index_generation) {
19,520✔
2013
                coli.ci_value_to_lines.clear();
8✔
2014
                coli.ci_index_generation = vt->lss->lss_index_generation;
8✔
2015
                coli.ci_indexed_range = msg_range::empty();
8✔
2016
                coli.ci_string_arena.reset();
8✔
2017
            }
2018

2019
            {
2020
                auto col_valid_opt = coli.ci_indexed_range.get_valid();
19,520✔
2021
                if (col_valid_opt) {
19,520✔
2022
                    log_debug("column %d valid range [%d:%d)",
19,512✔
2023
                              icol.cc_column,
2024
                              (int) col_valid_opt->v_min_line,
2025
                              (int) col_valid_opt->v_max_line);
2026
                }
2027
            }
2028

2029
            min_index_range.intersect(coli.ci_indexed_range);
19,520✔
2030
        }
2031

2032
        for (const auto& icol : p_cur->log_cursor.lc_indexed_columns) {
19,734✔
2033
            const auto& coli = vt->vi->vi_column_indexes[icol.cc_column];
19,520✔
2034

2035
            auto iter
2036
                = coli.ci_value_to_lines.find(icol.cc_constraint.sc_value);
19,520✔
2037
            if (iter != coli.ci_value_to_lines.end()) {
19,520✔
2038
                for (auto vl : iter->second) {
39,180✔
2039
                    if (!scan_range.contains(vl)) {
19,670✔
2040
#ifdef DEBUG_INDEXING
2041
                        log_debug(
2042
                            "indexed line %d is outside of scan range [%d:%d)",
2043
                            vl,
2044
                            scan_range.v_min_line,
2045
                            scan_range.v_max_line);
2046
#endif
2047
                        continue;
10✔
2048
                    }
2049

2050
                    if (!min_index_range.contains(vl)) {
19,660✔
2051
#ifdef DEBUG_INDEXING
2052
                        log_debug(
2053
                            "indexed line %d is outside of the min index range",
2054
                            vl);
2055
#endif
2056
                        continue;
×
2057
                    }
2058

2059
#ifdef DEBUG_INDEXING
2060
                    log_debug("adding indexed line %d", vl);
2061
#endif
2062
                    p_cur->log_cursor.lc_indexed_lines.push_back(vl);
19,660✔
2063
                }
2064
            }
2065
        }
2066
        p_cur->log_cursor.lc_indexed_lines_range = min_index_range;
214✔
2067

2068
#if 0
2069
        if (max_indexed_line && max_indexed_line.value() > 0_vl) {
2070
            p_cur->log_cursor.lc_indexed_lines.push_back(
2071
                max_indexed_line.value());
2072
        }
2073
#endif
2074

2075
        auto index_valid_opt = min_index_range.get_valid();
214✔
2076
        if (!min_index_range.contains(scan_range.v_min_line)
214✔
2077
            || !min_index_range.contains(scan_range.v_max_line - 1_vl))
214✔
2078
        {
2079
            log_debug(
209✔
2080
                "scan needed to populate index, clearing other indexes "
2081
                "scan_range[%d:%d)",
2082
                (int) scan_range.v_min_line,
2083
                (int) scan_range.v_max_line);
2084
            p_cur->log_cursor.lc_level_constraint = std::nullopt;
209✔
2085
            opid_val = std::nullopt;
209✔
2086
            tid_val = std::nullopt;
209✔
2087
            log_time_range = std::nullopt;
209✔
2088
            log_path_constraints.clear();
209✔
2089
            log_unique_path_constraints.clear();
209✔
2090

2091
            if (index_valid_opt) {
209✔
2092
                log_debug("  min_index_range[%d:%d)",
201✔
2093
                          (int) index_valid_opt->v_min_line,
2094
                          (int) index_valid_opt->v_max_line);
2095
                if (scan_range.v_min_line < index_valid_opt->v_min_line
201✔
2096
                    && index_valid_opt->v_max_line < scan_range.v_max_line)
201✔
2097
                {
2098
                    for (const auto& icol :
×
2099
                         p_cur->log_cursor.lc_indexed_columns)
×
2100
                    {
2101
                        vt->vi->vi_column_indexes.erase(icol.cc_column);
×
2102
                    }
2103
                    p_cur->log_cursor.lc_indexed_lines.clear();
×
2104
                    p_cur->log_cursor.lc_indexed_lines_range
2105
                        = msg_range::empty();
×
2106
                } else if (scan_range.v_max_line < index_valid_opt->v_min_line
201✔
2107
                           || scan_range.v_min_line
402✔
2108
                               >= index_valid_opt->v_max_line)
201✔
2109
                {
2110
                    p_cur->log_cursor.lc_indexed_lines.clear();
1✔
2111
                    p_cur->log_cursor.lc_indexed_lines_range
2112
                        = msg_range::empty();
1✔
2113
                    if (p_cur->log_cursor.lc_direction < 0) {
1✔
2114
                        if (scan_range.v_max_line < index_valid_opt->v_min_line)
×
2115
                        {
2116
                            p_cur->log_cursor.lc_curr_line
2117
                                = index_valid_opt->v_min_line - 1_vl;
×
2118
                        } else {
2119
                            p_cur->log_cursor.lc_end_line
2120
                                = index_valid_opt->v_max_line - 1_vl;
×
2121
                        }
2122
                    } else {
2123
                        if (scan_range.v_max_line < index_valid_opt->v_min_line)
1✔
2124
                        {
2125
                            p_cur->log_cursor.lc_end_line
2126
                                = index_valid_opt->v_min_line;
×
2127
                        } else {
2128
                            p_cur->log_cursor.lc_curr_line
2129
                                = index_valid_opt->v_max_line;
1✔
2130
                        }
2131
                    }
2132
                }
2133
            } else {
2134
                log_debug("  min_index_range::empty");
8✔
2135
                p_cur->log_cursor.lc_indexed_lines.clear();
8✔
2136
            }
2137
        } else if (index_valid_opt) {
5✔
2138
            log_info("using existing index over range [%d:%d)",
5✔
2139
                     (int) index_valid_opt->v_min_line,
2140
                     (int) index_valid_opt->v_max_line);
2141
            if (p_cur->log_cursor.lc_direction < 0) {
5✔
2142
                p_cur->log_cursor.lc_indexed_lines.push_back(
4✔
2143
                    index_valid_opt->v_min_line - 1_vl);
8✔
2144
            } else {
2145
                p_cur->log_cursor.lc_indexed_lines.push_back(
2✔
2146
                    index_valid_opt->v_max_line);
1✔
2147
            }
2148
        }
2149

2150
        if (p_cur->log_cursor.lc_direction < 0) {
214✔
2151
            log_debug("ORDER BY is DESC, reversing indexed lines");
8✔
2152
            std::sort(p_cur->log_cursor.lc_indexed_lines.begin(),
8✔
2153
                      p_cur->log_cursor.lc_indexed_lines.end(),
2154
                      std::less<>());
2155
        } else {
2156
            std::sort(p_cur->log_cursor.lc_indexed_lines.begin(),
206✔
2157
                      p_cur->log_cursor.lc_indexed_lines.end(),
2158
                      std::greater<>());
2159
        }
2160

2161
#ifdef DEBUG_INDEXING
2162
        log_debug("indexed lines:");
2163
        for (auto indline : p_cur->log_cursor.lc_indexed_lines) {
2164
            log_debug("  %d", (int) indline);
2165
        }
2166
#endif
2167
    }
214✔
2168

2169
    if (!log_time_range) {
429✔
2170
    } else if (log_time_range->empty()) {
4✔
2171
#ifdef DEBUG_INDEXING
2172
        log_warning("time range is empty");
2173
#endif
2174
        p_cur->log_cursor.lc_curr_line = p_cur->log_cursor.lc_end_line;
×
2175
    } else {
2176
        if (log_time_range->vtr_begin) {
4✔
2177
            auto vl_opt = vt->lss->row_for_time(
4✔
2178
                to_timeval(log_time_range->vtr_begin.value()));
4✔
2179
            if (!vl_opt) {
4✔
2180
#ifdef DEBUG_INDEXING
2181
                log_warning("cannot find row with begin time: %d",
2182
                            log_time_range->vtr_begin.value().count());
2183
#endif
2184
                p_cur->log_cursor.lc_curr_line = p_cur->log_cursor.lc_end_line;
×
2185
            } else {
2186
#ifdef DEBUG_INDEXING
2187
                log_debug("found row with begin time: %d -> %d",
2188
                          log_time_range->vtr_begin.value().count(),
2189
                          vl_opt.value());
2190
#endif
2191
                p_cur->log_cursor.lc_curr_line = vl_opt.value();
4✔
2192
            }
2193
        }
2194
        if (log_time_range->vtr_end) {
4✔
2195
            auto vl_max_opt = vt->lss->row_for_time(
3✔
2196
                to_timeval(log_time_range->vtr_end.value()));
3✔
2197
            if (vl_max_opt) {
3✔
2198
                p_cur->log_cursor.lc_end_line = vl_max_opt.value();
3✔
2199
                auto win = vt->lss->window_at(
3✔
2200
                    vl_max_opt.value(), vis_line_t(vt->lss->text_line_count()));
3✔
2201
                for (const auto& msg_info : *win) {
7✔
2202
                    if (log_time_range->vtr_end.value()
5✔
2203
                        < msg_info.get_logline()
10✔
2204
                              .get_time<std::chrono::microseconds>())
10✔
2205
                    {
2206
                        break;
1✔
2207
                    }
2208
                    p_cur->log_cursor.lc_end_line
2209
                        = msg_info.get_vis_line() + 1_vl;
4✔
2210
                }
3✔
2211
            }
3✔
2212
        }
2213
    }
2214

2215
    p_cur->log_cursor.lc_opid_bloom_bits = opid_val;
429✔
2216
    p_cur->log_cursor.lc_tid_bloom_bits = tid_val;
429✔
2217
    p_cur->log_cursor.lc_log_path = std::move(log_path_constraints);
429✔
2218
    p_cur->log_cursor.lc_unique_path = std::move(log_unique_path_constraints);
429✔
2219

2220
#if 0
2221
    if (p_cur->log_cursor.lc_indexed_lines.empty()) {
2222
        p_cur->log_cursor.lc_indexed_lines.push_back(
2223
            p_cur->log_cursor.lc_curr_line);
2224
    }
2225
#endif
2226
    log_debug("before table filter [%d:%d)",
429✔
2227
              (int) p_cur->log_cursor.lc_curr_line,
2228
              (int) p_cur->log_cursor.lc_end_line);
2229
    vt->vi->filter(p_cur->log_cursor, *vt->lss);
429✔
2230

2231
    log_debug("before initial next [%d:%d)",
429✔
2232
              (int) p_cur->log_cursor.lc_curr_line,
2233
              (int) p_cur->log_cursor.lc_end_line);
2234
    if (vt->base.pModule->xNext != vt_next_no_rowid) {
429✔
2235
        p_cur->log_cursor.lc_curr_line -= p_cur->log_cursor.lc_direction;
416✔
2236
    }
2237
    vt->base.pModule->xNext(p_vtc);
429✔
2238

2239
#ifdef DEBUG_INDEXING
2240
    log_debug("vt_filter() -> cursor_range(%d:%d:%d)",
2241
              (int) p_cur->log_cursor.lc_curr_line,
2242
              (int) p_cur->log_cursor.lc_end_line,
2243
              p_cur->log_cursor.lc_direction);
2244
#endif
2245

2246
    return SQLITE_OK;
429✔
2247
}
429✔
2248

2249
static int
2250
vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
236✔
2251
{
2252
    std::vector<sqlite3_index_info::sqlite3_index_constraint> indexes;
236✔
2253
    std::vector<std::string> index_desc;
236✔
2254
    int argvInUse = 0;
236✔
2255
    auto* vt = (log_vtab*) tab;
236✔
2256
    char direction = 1;
236✔
2257

2258
    log_info("vt_best_index(%s, nConstraint=%d, nOrderBy=%d)",
236✔
2259
             vt->vi->get_name().get(),
2260
             p_info->nConstraint,
2261
             p_info->nOrderBy);
2262
    if (p_info->nOrderBy > 0) {
236✔
2263
        log_info("  ORDER BY");
18✔
2264
        for (int i = 0; i < p_info->nOrderBy; i++) {
36✔
2265
            const auto& orderby_info = p_info->aOrderBy[i];
18✔
2266
            log_info("    %d %s",
18✔
2267
                     orderby_info.iColumn,
2268
                     orderby_info.desc ? "DESC" : "ASC");
2269
        }
2270

2271
        if (p_info->aOrderBy[0].iColumn == 0) {
18✔
2272
            if (p_info->aOrderBy[0].desc) {
10✔
2273
                log_info("  consuming ORDER BY log_line DESC");
8✔
2274
                direction = -1;
8✔
2275
            } else {
2276
                log_info("  consuming ORDER BY log_line ASC");
2✔
2277
                direction = 1;
2✔
2278
            }
2279
            p_info->orderByConsumed = 1;
10✔
2280
        }
2281
    }
2282
    if (!vt->vi->vi_supports_indexes) {
236✔
2283
        p_info->orderByConsumed = 0;
10✔
2284
        return SQLITE_OK;
10✔
2285
    }
2286
    for (int lpc = 0; lpc < p_info->nConstraint; lpc++) {
408✔
2287
        const auto& constraint = p_info->aConstraint[lpc];
182✔
2288
        if (!constraint.usable || constraint.op == SQLITE_INDEX_CONSTRAINT_MATCH
182✔
2289
#ifdef SQLITE_INDEX_CONSTRAINT_OFFSET
2290
            || constraint.op == SQLITE_INDEX_CONSTRAINT_OFFSET
169✔
2291
            || constraint.op == SQLITE_INDEX_CONSTRAINT_LIMIT
169✔
2292
#endif
2293
        )
2294
        {
2295
            log_debug("  column %d: is not usable (usable=%d, op: %s)",
29✔
2296
                      lpc,
2297
                      constraint.usable,
2298
                      sql_constraint_op_name(constraint.op));
2299
            continue;
29✔
2300
        }
2301

2302
        auto col = constraint.iColumn;
153✔
2303
        auto op = constraint.op;
153✔
2304
        log_debug("  column %d: op: %s", col, sql_constraint_op_name(op));
153✔
2305
        switch (col) {
153✔
2306
            case VT_COL_LINE_NUMBER: {
76✔
2307
                argvInUse += 1;
76✔
2308
                indexes.push_back(constraint);
76✔
2309
                p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
76✔
2310
                index_desc.emplace_back(fmt::format(
76✔
2311
                    FMT_STRING("log_line {} ?"), sql_constraint_op_name(op)));
228✔
2312
                break;
76✔
2313
            }
2314
            case VT_COL_LOG_TIME: {
2✔
2315
                argvInUse += 1;
2✔
2316
                indexes.push_back(p_info->aConstraint[lpc]);
2✔
2317
                p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2318
                index_desc.emplace_back(fmt::format(
2✔
2319
                    FMT_STRING("log_time {} ?"), sql_constraint_op_name(op)));
6✔
2320
                break;
2✔
2321
            }
2322
            case VT_COL_LEVEL: {
8✔
2323
                if (log_cursor::level_constraint::op_is_supported(op)) {
8✔
2324
                    argvInUse += 1;
8✔
2325
                    indexes.push_back(constraint);
8✔
2326
                    p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
8✔
2327
                    index_desc.emplace_back(
8✔
2328
                        fmt::format(FMT_STRING("log_level {} ?"),
24✔
2329
                                    sql_constraint_op_name(op)));
16✔
2330
                }
2331
                break;
8✔
2332
            }
2333
            default: {
67✔
2334
                if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
67✔
2335
                    auto footer_column = static_cast<log_footer_columns>(
2336
                        col - (VT_COL_MAX + vt->vi->vi_column_count - 1) - 1);
25✔
2337

2338
                    switch (footer_column) {
25✔
2339
                        case log_footer_columns::time_msecs: {
1✔
2340
                            argvInUse += 1;
1✔
2341
                            indexes.push_back(p_info->aConstraint[lpc]);
1✔
2342
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
1✔
2343
                            index_desc.emplace_back(
1✔
2344
                                fmt::format(FMT_STRING("log_time_msecs {} ?"),
3✔
2345
                                            sql_constraint_op_name(op)));
1✔
2346
                            break;
1✔
2347
                        }
2348
                        case log_footer_columns::format: {
3✔
2349
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
3✔
2350
                                argvInUse += 1;
3✔
2351
                                indexes.push_back(constraint);
3✔
2352
                                p_info->aConstraintUsage[lpc].argvIndex
3✔
2353
                                    = argvInUse;
3✔
2354
                                index_desc.emplace_back("log_format = ?");
3✔
2355
                            }
2356
                            break;
3✔
2357
                        }
2358
                        case log_footer_columns::format_regex: {
×
2359
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
×
2360
                                argvInUse += 1;
×
2361
                                indexes.push_back(constraint);
×
2362
                                p_info->aConstraintUsage[lpc].argvIndex
×
2363
                                    = argvInUse;
×
2364
                                index_desc.emplace_back("log_format_regex = ?");
×
2365
                            }
2366
                            break;
×
2367
                        }
2368
                        case log_footer_columns::opid:
1✔
2369
                        case log_footer_columns::user_opid: {
2370
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
1✔
2371
                                argvInUse += 1;
1✔
2372
                                indexes.push_back(constraint);
1✔
2373
                                p_info->aConstraintUsage[lpc].argvIndex
1✔
2374
                                    = argvInUse;
1✔
2375
                                index_desc.emplace_back("log_opid = ?");
1✔
2376
                            }
2377
                            break;
1✔
2378
                        }
2379
                        case log_footer_columns::path: {
2✔
2380
                            argvInUse += 1;
2✔
2381
                            indexes.push_back(constraint);
2✔
2382
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2383
                            index_desc.emplace_back(
2✔
2384
                                fmt::format(FMT_STRING("log_path {} ?"),
6✔
2385
                                            sql_constraint_op_name(op)));
2✔
2386
                            break;
2✔
2387
                        }
2388
                        case log_footer_columns::unique_path: {
×
2389
                            argvInUse += 1;
×
2390
                            indexes.push_back(constraint);
×
2391
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
×
2392
                            index_desc.emplace_back(
×
2393
                                fmt::format(FMT_STRING("log_unique_path {} ?"),
×
2394
                                            sql_constraint_op_name(op)));
×
2395
                            break;
×
2396
                        }
2397
                        case log_footer_columns::partition:
16✔
2398
                        case log_footer_columns::actual_time:
2399
                        case log_footer_columns::idle_msecs:
2400
                        case log_footer_columns::mark:
2401
                        case log_footer_columns::sticky_mark:
2402
                        case log_footer_columns::comment:
2403
                        case log_footer_columns::tags:
2404
                        case log_footer_columns::annotations:
2405
                        case log_footer_columns::filters:
2406
                        case log_footer_columns::text:
2407
                        case log_footer_columns::body:
2408
                        case log_footer_columns::raw_text:
2409
                        case log_footer_columns::line_hash:
2410
                        case log_footer_columns::opid_definition:
2411
                        case log_footer_columns::src_file:
2412
                        case log_footer_columns::src_line:
2413
                            break;
16✔
2414
                        case log_footer_columns::line_link: {
2✔
2415
                            argvInUse += 1;
2✔
2416
                            indexes.push_back(constraint);
2✔
2417
                            p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
2✔
2418
                            index_desc.emplace_back("log_line_link = ?");
2✔
2419
                            break;
2✔
2420
                        }
2421
                        case log_footer_columns::thread_id: {
×
2422
                            if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
×
2423
                                argvInUse += 1;
×
2424
                                indexes.push_back(constraint);
×
2425
                                p_info->aConstraintUsage[lpc].argvIndex
×
2426
                                    = argvInUse;
×
2427
                                index_desc.emplace_back("log_thread_id = ?");
×
2428
                            }
2429
                            break;
×
2430
                        }
2431
                    }
2432
                } else if (op == SQLITE_INDEX_CONSTRAINT_EQ) {
42✔
2433
                    argvInUse += 1;
19✔
2434
                    indexes.push_back(constraint);
19✔
2435
                    p_info->aConstraintUsage[lpc].argvIndex = argvInUse;
19✔
2436
                    index_desc.emplace_back(
19✔
2437
                        fmt::format(FMT_STRING("col({}) {} ?"),
57✔
2438
                                    col,
2439
                                    sql_constraint_op_name(op)));
38✔
2440
                }
2441
                break;
67✔
2442
            }
2443
        }
2444
    }
2445

2446
    if (argvInUse) {
226✔
2447
        auto full_desc = fmt::format(FMT_STRING("SEARCH {} USING {}"),
202✔
2448
                                     vt->vi->get_name().get(),
101✔
2449
                                     fmt::join(index_desc, " AND "));
101✔
2450
        log_info("found index: %s", full_desc.c_str());
101✔
2451

2452
        sqlite3_index_info::sqlite3_index_constraint* index_copy;
2453
        auto index_len = indexes.size() * sizeof(*index_copy);
101✔
2454
        size_t len = full_desc.size() + 128 + index_len;
101✔
2455
        auto* storage = sqlite3_malloc(len);
101✔
2456
        if (!storage) {
101✔
2457
            return SQLITE_NOMEM;
×
2458
        }
2459
        auto* desc_storage = static_cast<char*>(storage);
101✔
2460
        memcpy(desc_storage, full_desc.c_str(), full_desc.size() + 1);
101✔
2461
        desc_storage[full_desc.size() + 1] = direction;
101✔
2462
        auto* remaining_storage
2463
            = static_cast<void*>(desc_storage + full_desc.size() + 1 + 1);
101✔
2464
        len -= 1 + full_desc.size() - 1;
101✔
2465
        auto* index_storage
2466
            = std::align(alignof(sqlite3_index_info::sqlite3_index_constraint),
101✔
2467
                         index_len,
2468
                         remaining_storage,
2469
                         len);
2470
        index_copy
2471
            = reinterpret_cast<sqlite3_index_info::sqlite3_index_constraint*>(
101✔
2472
                index_storage);
2473
        log_info("  index storage: %p", index_copy);
101✔
2474
        memcpy(index_copy, &indexes[0], index_len);
101✔
2475
        p_info->idxNum = argvInUse;
101✔
2476
        p_info->idxStr = static_cast<char*>(storage);
101✔
2477
        p_info->needToFreeIdxStr = 1;
101✔
2478
        p_info->estimatedCost = 10.0;
101✔
2479
    } else {
101✔
2480
        static char fullscan_asc[] = "fullscan\0\001";
2481
        static char fullscan_desc[] = "fullscan\0\377";
2482

2483
        p_info->idxStr = direction < 0 ? fullscan_desc : fullscan_asc;
125✔
2484
        p_info->estimatedCost = 1000000000.0;
125✔
2485
    }
2486

2487
    return SQLITE_OK;
226✔
2488
}
236✔
2489

2490
struct parsed_tags {
2491
    std::vector<std::string> pt_tags;
2492
};
2493

2494
static const struct json_path_container tags_handler = {
2495
    json_path_handler("#")
2496
        .with_synopsis("tag")
2497
        .with_description("A tag for the log line")
2498
        .with_pattern(R"(^#[^\s]+$)")
2499
        .for_field(&parsed_tags::pt_tags),
2500
};
2501

2502
static int
2503
vt_update(sqlite3_vtab* tab,
79✔
2504
          int argc,
2505
          sqlite3_value** argv,
2506
          sqlite_int64* rowid_out)
2507
{
2508
    auto* vt = (log_vtab*) tab;
79✔
2509
    int retval = SQLITE_READONLY;
79✔
2510

2511
    if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL
78✔
2512
        && sqlite3_value_int64(argv[0]) == sqlite3_value_int64(argv[1]))
157✔
2513
    {
2514
        int64_t rowid = sqlite3_value_int64(argv[0]) >> 8;
78✔
2515
        int val = sqlite3_value_int(
156✔
2516
            argv[2 + vt->footer_index(log_footer_columns::mark)]);
78✔
2517
        int sticky_val = sqlite3_value_int(
156✔
2518
            argv[2 + vt->footer_index(log_footer_columns::sticky_mark)]);
78✔
2519
        vis_line_t vrowid(rowid);
78✔
2520
        auto win = vt->lss->window_at(vrowid);
78✔
2521
        const auto msg_info = *win->begin();
78✔
2522
        const auto* part_name = sqlite3_value_text(
156✔
2523
            argv[2 + vt->footer_index(log_footer_columns::partition)]);
78✔
2524
        const auto* log_comment = sqlite3_value_text(
156✔
2525
            argv[2 + vt->footer_index(log_footer_columns::comment)]);
78✔
2526
        const auto log_tags = from_sqlite<std::optional<string_fragment>>()(
×
2527
            argc, argv, 2 + vt->footer_index(log_footer_columns::tags));
78✔
2528
        const auto log_annos = from_sqlite<std::optional<string_fragment>>()(
×
2529
            argc, argv, 2 + vt->footer_index(log_footer_columns::annotations));
78✔
2530
        auto log_opid = from_sqlite<std::optional<string_fragment>>()(
×
2531
            argc, argv, 2 + vt->footer_index(log_footer_columns::opid));
78✔
2532
        bookmark_metadata tmp_bm;
78✔
2533
        parsed_tags tmp_tags;
78✔
2534

2535
        if (log_tags) {
78✔
2536
            std::vector<lnav::console::user_message> errors;
4✔
2537
            yajlpp_parse_context ypc(vt->vi->get_tags_name(), &tags_handler);
4✔
2538
            auto_mem<yajl_handle_t> handle(yajl_free);
4✔
2539

2540
            handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
4✔
2541
            ypc.ypc_userdata = &errors;
4✔
2542
            ypc.ypc_line_number = log_vtab_data.lvd_location.sl_line_number;
4✔
2543
            ypc.with_handle(handle)
4✔
2544
                .with_error_reporter([](const yajlpp_parse_context& ypc,
8✔
2545
                                        auto msg) {
2546
                    auto& errors = *((std::vector<lnav::console::user_message>*)
2✔
2547
                                         ypc.ypc_userdata);
2548
                    errors.emplace_back(msg);
2✔
2549
                })
2✔
2550
                .with_obj(tmp_tags);
4✔
2551
            ypc.parse_doc(log_tags.value());
4✔
2552
            if (!errors.empty()) {
4✔
2553
                auto top_error
2554
                    = lnav::console::user_message::error(
×
2555
                          attr_line_t("invalid value for ")
2✔
2556
                              .append_quoted("log_tags"_symbol)
2✔
2557
                              .append(" column of table ")
2✔
2558
                              .append_quoted(lnav::roles::symbol(
4✔
2559
                                  vt->vi->get_name().to_string())))
4✔
2560
                          .with_reason(errors[0].to_attr_line(
4✔
2561
                              lnav::console::user_message::render_flags::none))
2562
                          .move();
2✔
2563
                set_vtable_errmsg(tab, top_error);
2✔
2564
                return SQLITE_ERROR;
2✔
2565
            }
2✔
2566
        }
8✔
2567
        if (log_annos) {
76✔
2568
            static const intern_string_t SRC
2569
                = intern_string::lookup("log_annotations");
9✔
2570

2571
            auto parse_res = logmsg_annotations_handlers.parser_for(SRC).of(
6✔
2572
                log_annos.value());
3✔
2573
            if (parse_res.isErr()) {
3✔
2574
                set_vtable_errmsg(tab, parse_res.unwrapErr()[0]);
2✔
2575
                return SQLITE_ERROR;
2✔
2576
            }
2577

2578
            tmp_bm.bm_annotations = parse_res.unwrap();
1✔
2579
        }
3✔
2580

2581
        auto& bv_meta = vt->tc->get_bookmarks()[&textview_curses::BM_META];
74✔
2582
        bool has_meta = log_comment != nullptr || log_tags.has_value()
72✔
2583
            || log_annos.has_value();
146✔
2584

2585
        if (bv_meta.bv_tree.exists(vrowid) && !has_meta) {
74✔
2586
            vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, false);
2✔
2587
            vt->lss->set_line_meta_changed();
2✔
2588
        }
2589

2590
        if (part_name) {
74✔
2591
            auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
3✔
2592
            line_meta.bm_name = std::string((const char*) part_name);
3✔
2593
            vt->tc->set_user_mark(&textview_curses::BM_PARTITION, vrowid, true);
3✔
2594
        } else {
2595
            vt->tc->set_user_mark(
71✔
2596
                &textview_curses::BM_PARTITION, vrowid, false);
2597
        }
2598

2599
        if (log_opid) {
74✔
2600
            auto& lvv = msg_info.get_values();
58✔
2601
            if (!lvv.lvv_opid_value
58✔
2602
                || lvv.lvv_opid_provenance
58✔
2603
                    == logline_value_vector::opid_provenance::user)
2604
            {
2605
                msg_info.get_file_ptr()->set_logline_opid(
70✔
2606
                    msg_info.get_file_line_number(), log_opid.value());
35✔
2607
                vt->lss->set_line_meta_changed();
35✔
2608
            }
2609
        } else if (msg_info.get_values().lvv_opid_provenance
16✔
2610
                   == logline_value_vector::opid_provenance::user)
16✔
2611
        {
2612
            msg_info.get_file_ptr()->clear_logline_opid(
3✔
2613
                msg_info.get_file_line_number());
2614
            vt->lss->set_line_meta_changed();
3✔
2615
        }
2616

2617
        if (!has_meta && part_name == nullptr
70✔
2618
            && (!log_opid
195✔
2619
                || msg_info.get_values().lvv_opid_provenance
51✔
2620
                    == logline_value_vector::opid_provenance::file))
2621
        {
2622
            vt->lss->erase_bookmark_metadata(vrowid);
32✔
2623
        }
2624

2625
        if (has_meta) {
74✔
2626
            auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
4✔
2627

2628
            vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, true);
4✔
2629
            if (part_name == nullptr) {
4✔
2630
                line_meta.bm_name.clear();
4✔
2631
            }
2632
            if (log_comment) {
4✔
2633
                line_meta.bm_comment = std::string((const char*) log_comment);
4✔
2634
            } else {
2635
                line_meta.bm_comment.clear();
2✔
2636
            }
2637
            if (log_tags) {
4✔
2638
                line_meta.bm_tags.clear();
2✔
2639
                for (const auto& tag : tmp_tags.pt_tags) {
5✔
2640
                    line_meta.add_tag(tag);
3✔
2641
                }
2642

2643
                for (const auto& entry : line_meta.bm_tags) {
4✔
2644
                    bookmark_metadata::KNOWN_TAGS.insert(entry.te_tag);
2✔
2645
                }
2646
            } else {
2647
                line_meta.bm_tags.clear();
2✔
2648
            }
2649
            if (log_annos) {
4✔
2650
                line_meta.bm_annotations = std::move(tmp_bm.bm_annotations);
1✔
2651
            } else if (!sqlite3_value_nochange(
3✔
2652
                           argv[2
3✔
2653
                                + vt->footer_index(
3✔
2654
                                    log_footer_columns::annotations)]))
3✔
2655
            {
2656
                line_meta.bm_annotations.la_pairs.clear();
×
2657
            }
2658
            vt->lss->set_line_meta_changed();
4✔
2659
        }
2660

2661
        vt->tc->set_user_mark(&textview_curses::BM_USER, vrowid, val);
74✔
2662
        vt->tc->set_user_mark(&textview_curses::BM_STICKY, vrowid, sticky_val);
74✔
2663
        rowid += 1;
74✔
2664
        while ((size_t) rowid < vt->lss->text_line_count()) {
77✔
2665
            vis_line_t vl(rowid);
54✔
2666
            auto cl = vt->lss->at(vl);
54✔
2667
            auto* ll = vt->lss->find_line(cl);
54✔
2668
            if (ll->is_message()) {
54✔
2669
                break;
51✔
2670
            }
2671
            vt->tc->set_user_mark(&textview_curses::BM_USER, vl, val);
3✔
2672
            rowid += 1;
3✔
2673
        }
2674

2675
        if (retval != SQLITE_ERROR) {
74✔
2676
            retval = SQLITE_OK;
74✔
2677
        }
2678
    }
90✔
2679

2680
    return retval;
75✔
2681
}
2682

2683
static const sqlite3_module generic_vtab_module = {
2684
    0, /* iVersion */
2685
    vt_create, /* xCreate       - create a vtable */
2686
    vt_connect, /* xConnect      - associate a vtable with a connection */
2687
    vt_best_index, /* xBestIndex    - best index */
2688
    vt_disconnect, /* xDisconnect   - disassociate a vtable with a connection */
2689
    vt_destroy, /* xDestroy      - destroy a vtable */
2690
    vt_open, /* xOpen         - open a cursor */
2691
    vt_close, /* xClose        - close a cursor */
2692
    vt_filter, /* xFilter       - configure scan constraints */
2693
    vt_next, /* xNext         - advance a cursor */
2694
    vt_eof, /* xEof          - inidicate end of result set*/
2695
    vt_column, /* xColumn       - read data */
2696
    vt_rowid, /* xRowid        - read data */
2697
    vt_update, /* xUpdate       - write data */
2698
    nullptr, /* xBegin        - begin transaction */
2699
    nullptr, /* xSync         - sync transaction */
2700
    nullptr, /* xCommit       - commit transaction */
2701
    nullptr, /* xRollback     - rollback transaction */
2702
    nullptr, /* xFindFunction - function overloading */
2703
};
2704

2705
static const sqlite3_module no_rowid_vtab_module = {
2706
    0, /* iVersion */
2707
    vt_create, /* xCreate       - create a vtable */
2708
    vt_connect, /* xConnect      - associate a vtable with a connection */
2709
    vt_best_index, /* xBestIndex    - best index */
2710
    vt_disconnect, /* xDisconnect   - disassociate a vtable with a connection */
2711
    vt_destroy, /* xDestroy      - destroy a vtable */
2712
    vt_open, /* xOpen         - open a cursor */
2713
    vt_close, /* xClose        - close a cursor */
2714
    vt_filter, /* xFilter       - configure scan constraints */
2715
    vt_next_no_rowid, /* xNext         - advance a cursor */
2716
    vt_eof, /* xEof          - inidicate end of result set*/
2717
    vt_column, /* xColumn       - read data */
2718
    nullptr, /* xRowid        - read data */
2719
    nullptr, /* xUpdate       - write data */
2720
    nullptr, /* xBegin        - begin transaction */
2721
    nullptr, /* xSync         - sync transaction */
2722
    nullptr, /* xCommit       - commit transaction */
2723
    nullptr, /* xRollback     - rollback transaction */
2724
    nullptr, /* xFindFunction - function overloading */
2725
};
2726

2727
static int
2728
progress_callback(void* ptr)
1,077,613✔
2729
{
2730
    int retval = 0;
1,077,613✔
2731

2732
    if (log_vtab_data.lvd_progress != nullptr) {
1,077,613✔
2733
        retval = log_vtab_data.lvd_progress(log_cursor_latest);
11,537✔
2734
    }
2735
    if (!log_vtab_data.lvd_looping) {
1,077,613✔
2736
        retval = 1;
×
2737
    }
2738

2739
    return retval;
1,077,613✔
2740
}
2741

2742
log_vtab_manager::log_vtab_manager(auto_sqlite3& memdb, logfile_sub_source& lss)
826✔
2743
    : vm_db(memdb), vm_source(lss)
826✔
2744
{
2745
    sqlite3_create_module(
826✔
2746
        this->vm_db, "log_vtab_impl", &generic_vtab_module, this);
826✔
2747
    sqlite3_create_module(
826✔
2748
        this->vm_db, "log_vtab_no_rowid_impl", &no_rowid_vtab_module, this);
826✔
2749
    sqlite3_progress_handler(memdb, 32, progress_callback, nullptr);
826✔
2750
}
826✔
2751

2752
log_vtab_manager::~log_vtab_manager()
826✔
2753
{
2754
    while (!this->vm_impls.empty()) {
64,992✔
2755
        auto first_name = this->vm_impls.begin()->first;
64,166✔
2756

2757
        this->unregister_vtab(first_name);
64,166✔
2758
    }
2759
}
826✔
2760

2761
std::string
2762
log_vtab_manager::register_vtab(std::shared_ptr<log_vtab_impl> vi)
64,344✔
2763
{
2764
    std::string retval;
64,344✔
2765

2766
    if (this->vm_impls.find(vi->get_name().to_string_fragment())
64,344✔
2767
        == this->vm_impls.end())
128,688✔
2768
    {
2769
        std::vector<std::string> primary_keys;
64,344✔
2770
        auto_mem<char, sqlite3_free> errmsg;
64,344✔
2771
        auto_mem<char, sqlite3_free> sql;
64,344✔
2772
        int rc;
2773

2774
        this->vm_impls[vi->get_name().to_string_fragment()] = vi;
64,344✔
2775

2776
        vi->get_primary_keys(primary_keys);
64,344✔
2777

2778
        sql = sqlite3_mprintf(
2779
            "CREATE VIRTUAL TABLE lnav_db.%s "
2780
            "USING %s(%s)",
2781
            vi->get_name().get(),
64,344✔
2782
            primary_keys.empty() ? "log_vtab_impl" : "log_vtab_no_rowid_impl",
64,344✔
2783
            vi->get_name().get());
193,032✔
2784
        rc = sqlite3_exec(this->vm_db, sql, nullptr, nullptr, errmsg.out());
64,344✔
2785
        if (rc != SQLITE_OK) {
64,344✔
2786
            retval = errmsg;
1✔
2787
        }
2788
    } else {
64,344✔
2789
        retval = "a table with the given name already exists";
×
2790
    }
2791

2792
    return retval;
64,344✔
2793
}
×
2794

2795
std::string
2796
log_vtab_manager::unregister_vtab(string_fragment name)
64,619✔
2797
{
2798
    std::string retval;
64,619✔
2799

2800
    if (this->vm_impls.find(name) == this->vm_impls.end()) {
64,619✔
2801
        retval = fmt::format(FMT_STRING("unknown table -- {}"), name);
1,100✔
2802
    } else {
2803
        auto_mem<char, sqlite3_free> sql;
64,344✔
2804
        __attribute((unused)) int rc;
2805

2806
        sql = sqlite3_mprintf(
2807
            "DROP TABLE IF EXISTS %.*s", name.length(), name.data());
64,344✔
2808
        log_debug("unregister_vtab: %s", sql.in());
64,344✔
2809
        rc = sqlite3_exec(this->vm_db, sql, nullptr, nullptr, nullptr);
64,344✔
2810

2811
        this->vm_impls.erase(name);
64,344✔
2812
    }
64,344✔
2813

2814
    return retval;
64,619✔
2815
}
×
2816

2817
std::shared_ptr<log_vtab_impl>
2818
log_vtab_manager::lookup_impl(string_fragment name) const
64,833✔
2819
{
2820
    const auto iter = this->vm_impls.find(name);
64,833✔
2821
    if (iter != this->vm_impls.end()) {
64,833✔
2822
        return iter->second;
64,824✔
2823
    }
2824
    return nullptr;
9✔
2825
}
2826

2827
log_format_vtab_impl::log_format_vtab_impl(
53,351✔
2828
    std::shared_ptr<const log_format> format)
53,351✔
2829
    : log_vtab_impl(format->get_name()), lfvi_format(format)
53,351✔
2830
{
2831
}
53,351✔
2832

2833
bool
2834
log_format_vtab_impl::next(log_cursor& lc, logfile_sub_source& lss)
50,276✔
2835
{
2836
    if (lc.is_eof()) {
50,276✔
2837
        return true;
×
2838
    }
2839

2840
    auto cl = content_line_t(lss.at(lc.lc_curr_line));
50,276✔
2841
    auto* lf = lss.find_file_ptr(cl);
50,276✔
2842
    auto lf_iter = lf->begin() + cl;
50,276✔
2843

2844
    if (!lf_iter->is_message()) {
50,276✔
2845
        return false;
×
2846
    }
2847

2848
    if (lf->get_format_name() == this->lfvi_format->get_name()) {
50,276✔
2849
        return true;
29,900✔
2850
    }
2851

2852
    return false;
20,376✔
2853
}
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