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

tstack / lnav / 17589970077-2502

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

push

github

tstack
[format] add fields for source file/line

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

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

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

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

92.28
/src/vtab_module.hh
1
/**
2
 * Copyright (c) 2017, 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
#ifndef vtab_module_hh
31
#define vtab_module_hh
32

33
#include <exception>
34
#include <map>
35
#include <optional>
36
#include <string>
37
#include <type_traits>
38
#include <vector>
39

40
#include <sqlite3.h>
41

42
#include "base/auto_mem.hh"
43
#include "base/intern_string.hh"
44
#include "base/lnav.console.hh"
45
#include "base/lnav_log.hh"
46
#include "base/string_util.hh"
47
#include "base/types.hh"
48
#include "fmt/format.h"
49
#include "help_text_formatter.hh"
50
#include "mapbox/variant.hpp"
51
#include "sqlite-extension-func.hh"
52

53
lnav::console::user_message sqlite3_error_to_user_message(sqlite3*);
54

55
struct from_sqlite_conversion_error : std::exception {
56
    from_sqlite_conversion_error(const char* type, int argi)
6✔
57
        : e_type(type), e_argi(argi)
6✔
58
    {
59
    }
6✔
60

61
    const char* e_type;
62
    int e_argi;
63
};
64

65
struct sqlite_func_error : std::exception {
66
    template<typename... Args>
67
    explicit sqlite_func_error(fmt::string_view format_str, const Args&... args)
9✔
68
        : e_what(fmt::vformat(format_str, fmt::make_format_args(args...)))
11✔
69
    {
70
    }
9✔
71

72
    const char* what() const noexcept override { return this->e_what.c_str(); }
9✔
73

74
    const std::string e_what;
75
};
76

77
namespace vtab_types {
78

79
template<typename T>
80
struct nullable {
81
    T* n_value{nullptr};
82
};
83

84
template<typename>
85
struct is_nullable : std::false_type {};
86

87
template<typename T>
88
struct is_nullable<nullable<T>> : std::true_type {};
89

90
}  // namespace vtab_types
91

92
template<typename T>
93
struct from_sqlite {
94
    using U = std::remove_reference_t<T>;
95

96
    U operator()(int argc, sqlite3_value** val, int argi) const { return U(); }
97
};
98

99
template<>
100
struct from_sqlite<bool> {
101
    bool operator()(int argc, sqlite3_value** val, int argi) const
105✔
102
    {
103
        if (sqlite3_value_numeric_type(val[argi]) != SQLITE_INTEGER) {
105✔
UNCOV
104
            throw from_sqlite_conversion_error("integer", argi);
×
105
        }
106

107
        return sqlite3_value_int64(val[argi]);
105✔
108
    }
109
};
110

111
template<>
112
struct from_sqlite<int64_t> {
113
    int64_t operator()(int argc, sqlite3_value** val, int argi) const
454✔
114
    {
115
        if (sqlite3_value_numeric_type(val[argi]) != SQLITE_INTEGER) {
454✔
116
            throw from_sqlite_conversion_error("integer", argi);
1✔
117
        }
118

119
        return sqlite3_value_int64(val[argi]);
453✔
120
    }
121
};
122

123
template<>
124
struct from_sqlite<sqlite3_value*> {
125
    sqlite3_value* operator()(int argc, sqlite3_value** val, int argi) const
1,495✔
126
    {
127
        return val[argi];
1,495✔
128
    }
129
};
130

131
template<>
132
struct from_sqlite<int> {
133
    int operator()(int argc, sqlite3_value** val, int argi) const
134
    {
135
        if (sqlite3_value_numeric_type(val[argi]) != SQLITE_INTEGER) {
136
            throw from_sqlite_conversion_error("integer", argi);
137
        }
138

139
        return sqlite3_value_int(val[argi]);
140
    }
141
};
142

143
template<>
144
struct from_sqlite<const char*> {
145
    const char* operator()(int argc, sqlite3_value** val, int argi) const
12,851✔
146
    {
147
        return (const char*) sqlite3_value_text(val[argi]);
12,851✔
148
    }
149
};
150

151
template<>
152
struct from_sqlite<string_fragment> {
153
    string_fragment operator()(int argc, sqlite3_value** val, int argi) const
5,949✔
154
    {
155
        const auto ptr = (const char*) sqlite3_value_blob(val[argi]);
5,949✔
156

157
        if (ptr == nullptr) {
5,949✔
UNCOV
158
            return string_fragment::invalid();
×
159
        }
160
        return string_fragment::from_bytes(ptr, sqlite3_value_bytes(val[argi]));
5,949✔
161
    }
162
};
163

164
template<>
165
struct from_sqlite<std::string> {
166
    std::string operator()(int argc, sqlite3_value** val, int argi) const
9,080✔
167
    {
168
        return {
169
            (const char*) sqlite3_value_blob(val[argi]),
9,080✔
170
            (size_t) sqlite3_value_bytes(val[argi]),
9,080✔
171
        };
27,240✔
172
    }
173
};
174

175
template<>
176
struct from_sqlite<double> {
177
    double operator()(int argc, sqlite3_value** val, int argi) const
13✔
178
    {
179
        return sqlite3_value_double(val[argi]);
13✔
180
    }
181
};
182

183
template<typename T>
184
struct from_sqlite<std::optional<T>> {
185
    std::optional<T> operator()(int argc, sqlite3_value** val, int argi) const
1,897✔
186
    {
187
        if (argi >= argc || sqlite3_value_type(val[argi]) == SQLITE_NULL) {
1,897✔
188
            return std::nullopt;
244✔
189
        }
190

191
        return std::optional<T>(from_sqlite<T>()(argc, val, argi));
1,653✔
192
    }
193
};
194

195
template<typename T>
196
struct from_sqlite<const std::vector<T>&> {
197
    std::vector<T> operator()(int argc, sqlite3_value** val, int argi) const
5,571✔
198
    {
199
        std::vector<T> retval;
5,571✔
200

201
        for (int lpc = argi; lpc < argc; lpc++) {
16,711✔
202
            retval.emplace_back(from_sqlite<T>()(argc, val, lpc));
11,140✔
203
        }
204

205
        return retval;
5,571✔
UNCOV
206
    }
×
207
};
208

209
template<typename T>
210
struct from_sqlite<vtab_types::nullable<T>> {
211
    vtab_types::nullable<T> operator()(int argc,
30✔
212
                                       sqlite3_value** val,
213
                                       int argi) const
214
    {
215
        return {from_sqlite<T*>()(argc, val, argi)};
30✔
216
    }
217
};
218

219
void to_sqlite(sqlite3_context* ctx, const lnav::console::user_message& um);
220

221
void set_vtable_errmsg(sqlite3_vtab* vtab,
222
                       const lnav::console::user_message& um);
223

224
inline void
225
to_sqlite(sqlite3_context* ctx, null_value_t)
1✔
226
{
227
    sqlite3_result_null(ctx);
1✔
228
}
1✔
229

230
inline void
231
to_sqlite(sqlite3_context* ctx, const char* str)
70✔
232
{
233
    if (str == nullptr) {
70✔
234
        sqlite3_result_null(ctx);
30✔
235
    } else {
236
        sqlite3_result_text(ctx, str, -1, SQLITE_STATIC);
40✔
237
    }
238
}
70✔
239

240
inline void
241
to_sqlite(sqlite3_context* ctx, text_auto_buffer buf)
1,385✔
242
{
243
    auto pair = buf.inner.release();
1,385✔
244
    sqlite3_result_text(ctx, pair.first, pair.second, free);
1,385✔
245
}
1,385✔
246

247
inline void
248
to_sqlite(sqlite3_context* ctx, blob_auto_buffer buf)
10✔
249
{
250
    auto pair = buf.inner.release();
10✔
251
    sqlite3_result_blob(ctx, pair.first, pair.second, free);
10✔
252
}
10✔
253

254
inline void
255
to_sqlite(sqlite3_context* ctx, const std::string& str)
6,155✔
256
{
257
    sqlite3_result_text(ctx, str.c_str(), str.length(), SQLITE_TRANSIENT);
6,155✔
258
}
6,155✔
259

260
inline void
261
to_sqlite(sqlite3_context* ctx, const string_fragment& sf)
6,137✔
262
{
263
    if (sf.is_valid()) {
6,137✔
264
        sqlite3_result_text(
6,137✔
265
            ctx, &sf.sf_string[sf.sf_begin], sf.length(), SQLITE_TRANSIENT);
6,137✔
266
    } else {
267
        sqlite3_result_null(ctx);
×
268
    }
269
}
6,137✔
270

271
inline void
272
to_sqlite(sqlite3_context* ctx, bool val)
2,256✔
273
{
274
    sqlite3_result_int(ctx, val);
2,256✔
275
}
2,256✔
276

277
template<typename T>
278
void
279
to_sqlite(
108✔
280
    sqlite3_context* ctx,
281
    T val,
282
    std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>* dummy
283
    = 0)
284
{
285
    sqlite3_result_int64(ctx, val);
108✔
286
}
108✔
287

288
inline void
289
to_sqlite(sqlite3_context* ctx, double val)
16✔
290
{
291
    sqlite3_result_double(ctx, val);
16✔
292
}
16✔
293

294
inline void
295
to_sqlite(sqlite3_context* ctx, auto_mem<char> str)
30✔
296
{
297
    const auto free_func = str.get_free_func<void (*)(void*)>();
30✔
298
    sqlite3_result_text(ctx, str.release(), -1, free_func);
30✔
299
}
30✔
300

301
#define JSON_SUBTYPE    74 /* Ascii for "J" */
302
#define FLATTEN_SUBTYPE 0x5f
303

304
template<typename T>
305
void
306
to_sqlite(sqlite3_context* ctx, std::optional<T>& val)
307
{
308
    if (val.has_value()) {
309
        to_sqlite(ctx, val.value());
310
    } else {
311
        sqlite3_result_null(ctx);
312
    }
313
}
314

315
template<typename T>
316
void
317
to_sqlite(sqlite3_context* ctx, std::optional<T> val)
1,504✔
318
{
319
    if (val.has_value()) {
1,504✔
320
        to_sqlite(ctx, std::move(val.value()));
1,465✔
321
    } else {
322
        sqlite3_result_null(ctx);
39✔
323
    }
324
}
1,504✔
325

326
struct ToSqliteVisitor {
327
    ToSqliteVisitor(sqlite3_context* vctx) : tsv_context(vctx) {}
623✔
328

329
    template<typename T>
330
    void operator()(T&& t) const
623✔
331
    {
332
        to_sqlite(this->tsv_context, std::move(t));
623✔
333
    }
623✔
334

335
    sqlite3_context* tsv_context;
336
};
337

338
template<typename... Types>
339
void
340
to_sqlite(sqlite3_context* ctx, mapbox::util::variant<Types...>&& val)
623✔
341
{
342
    ToSqliteVisitor visitor(ctx);
623✔
343

344
    mapbox::util::apply_visitor(visitor, val);
623✔
345
}
623✔
346

347
template<typename... Args>
348
struct optional_counter {
349
    constexpr static int value = 0;
350
};
351

352
template<typename T>
353
struct optional_counter<std::optional<T>> {
354
    constexpr static int value = 1;
355
};
356

357
template<typename T, typename U>
358
struct optional_counter<std::optional<T>, const std::vector<U>&> {
359
    constexpr static int value = 1;
360
};
361

362
template<typename T, typename... Rest>
363
struct optional_counter<std::optional<T>, Rest...> {
364
    constexpr static int value = 1 + sizeof...(Rest);
365
};
366

367
template<typename Arg>
368
struct optional_counter<Arg> {
369
    constexpr static int value = 0;
370
};
371

372
template<typename Arg1, typename... Args>
373
struct optional_counter<Arg1, Args...> : optional_counter<Args...> {};
374

375
template<typename... Args>
376
struct variadic_counter {
377
    constexpr static int value = 0;
378
};
379

380
template<typename T>
381
struct variadic_counter<const std::vector<T>&> {
382
    constexpr static int value = 1;
383
};
384

385
template<typename Arg>
386
struct variadic_counter<Arg> {
387
    constexpr static int value = 0;
388
};
389

390
template<typename Arg1, typename... Args>
391
struct variadic_counter<Arg1, Args...> : variadic_counter<Args...> {};
392

393
template<typename F, F f>
394
struct sqlite_func_adapter;
395

396
template<typename Return, typename... Args, Return (*f)(Args...)>
397
struct sqlite_func_adapter<Return (*)(Args...), f> {
398
    constexpr static size_t OPT_COUNT = optional_counter<Args...>::value;
399
    constexpr static size_t VAR_COUNT = variadic_counter<Args...>::value;
400
    constexpr static size_t REQ_COUNT = sizeof...(Args) - OPT_COUNT - VAR_COUNT;
401

402
    template<size_t... Idx>
403
    static void func2(sqlite3_context* context,
10,529✔
404
                      int argc,
405
                      sqlite3_value** argv,
406
                      std::index_sequence<Idx...>)
407
    {
408
        try {
409
            Return retval = f(from_sqlite<Args>()(argc, argv, Idx)...);
10,532✔
410

411
            to_sqlite(context, std::move(retval));
10,495✔
412
        } catch (const lnav::console::user_message& um) {
2,696✔
413
            to_sqlite(context, um);
25✔
414
        } catch (from_sqlite_conversion_error& e) {
4✔
415
            char buffer[256];
416

417
            snprintf(buffer,
2✔
418
                     sizeof(buffer),
419
                     "Expecting an %s for argument number %d",
420
                     e.e_type,
421
                     e.e_argi);
422
            sqlite3_result_error(context, buffer, -1);
2✔
423
        } catch (const std::exception& e) {
14✔
424
            const auto* fd = (const FuncDef*) sqlite3_user_data(context);
7✔
425
            attr_line_t error_al;
7✔
426
            error_al.append("call to ");
7✔
427
            format_help_text_for_term(
7✔
428
                fd->fd_help, 40, error_al, help_text_content::synopsis);
7✔
429
            error_al.append(" failed");
7✔
430
            auto um = lnav::console::user_message::error(error_al).with_reason(
14✔
431
                e.what());
7✔
432

433
            to_sqlite(context, um);
7✔
434
        } catch (...) {
7✔
UNCOV
435
            sqlite3_result_error(
×
436
                context, "Function threw an unexpected exception", -1);
437
        }
438
    }
10,529✔
439

440
    static void func1(sqlite3_context* context, int argc, sqlite3_value** argv)
11,052✔
441
    {
442
        if ((size_t) argc < REQ_COUNT && VAR_COUNT == 0) {
5,418✔
443
            const auto* fd = (const FuncDef*) sqlite3_user_data(context);
1✔
444
            char buffer[128];
445

446
            if (OPT_COUNT == 0) {
UNCOV
447
                snprintf(buffer,
×
448
                         sizeof(buffer),
449
                         "%s() expects exactly %ld argument%s",
450
                         fd->fd_help.ht_name,
×
451
                         REQ_COUNT,
452
                         REQ_COUNT == 1 ? "s" : "");
453
            } else {
454
                snprintf(buffer,
1✔
455
                         sizeof(buffer),
456
                         "%s() expects between %ld and %ld arguments",
457
                         fd->fd_help.ht_name,
1✔
458
                         REQ_COUNT,
459
                         REQ_COUNT + OPT_COUNT);
460
            }
461
            sqlite3_result_error(context, buffer, -1);
1✔
462
            return;
1✔
463
        }
464

465
        if constexpr (REQ_COUNT > 0) {
466
            const static bool IS_NULLABLE[]
467
                = {vtab_types::is_nullable<Args>::value...};
468
            const static bool IS_SQLITE3_VALUE[]
469
                = {std::is_same_v<Args, sqlite3_value*>...};
470

471
            for (size_t lpc = 0; lpc < REQ_COUNT; lpc++) {
13,713✔
472
                if (!IS_NULLABLE[lpc] && !IS_SQLITE3_VALUE[lpc]
8,788✔
473
                    && sqlite3_value_type(argv[lpc]) == SQLITE_NULL)
17,606✔
474
                {
475
                    sqlite3_result_null(context);
522✔
476
                    return;
522✔
477
                }
478
            }
479
        }
480

481
        func2(context, argc, argv, std::make_index_sequence<sizeof...(Args)>{});
10,529✔
482
    }
483

484
    static FuncDef builder(help_text ht)
43,386✔
485
    {
486
        require(ht.ht_parameters.size() == sizeof...(Args));
43,386✔
487

488
        return {
489
            ht.ht_name,
43,386✔
490
            (OPT_COUNT > 0 || VAR_COUNT > 0) ? -1 : (int) REQ_COUNT,
491
            SQLITE_UTF8 | SQLITE_DETERMINISTIC,
492
            0,
493
            func1,
494
            ht,
495
        };
43,386✔
496
    }
43,386✔
497
};
498

499
extern std::string vtab_module_schemas;
500
extern std::map<intern_string_t, std::string> vtab_module_ddls;
501

502
class vtab_index_constraints {
503
public:
504
    vtab_index_constraints(const sqlite3_index_info* index_info)
75✔
505
        : vic_index_info(*index_info)
75✔
506
    {
507
    }
75✔
508

509
    struct const_iterator {
510
        const_iterator(vtab_index_constraints* parent, int index = 0)
286✔
511
            : i_parent(parent), i_index(index)
286✔
512
        {
513
            while (this->i_index < this->i_parent->vic_index_info.nConstraint
286✔
514
                   && !this->i_parent->vic_index_info.aConstraint[this->i_index]
290✔
515
                           .usable)
77✔
516
            {
517
                this->i_index += 1;
4✔
518
            }
519
        }
286✔
520

521
        const_iterator& operator++()
136✔
522
        {
523
            do {
524
                this->i_index += 1;
138✔
525
            } while (
526
                this->i_index < this->i_parent->vic_index_info.nConstraint
138✔
527
                && !this->i_parent->vic_index_info.aConstraint[this->i_index]
138✔
528
                        .usable);
65✔
529

530
            return *this;
136✔
531
        }
532

533
        const sqlite3_index_info::sqlite3_index_constraint& operator*() const
534
        {
535
            return this->i_parent->vic_index_info.aConstraint[this->i_index];
536
        }
537

538
        const sqlite3_index_info::sqlite3_index_constraint* operator->() const
538✔
539
        {
540
            return &this->i_parent->vic_index_info.aConstraint[this->i_index];
538✔
541
        }
542

543
        bool operator!=(const const_iterator& rhs) const
211✔
544
        {
545
            return this->i_parent != rhs.i_parent
211✔
546
                || this->i_index != rhs.i_index;
211✔
547
        }
548

549
        const vtab_index_constraints* i_parent;
550
        int i_index;
551
    };
552

553
    const_iterator begin() { return {this}; }
75✔
554

555
    const_iterator end() { return {this, this->vic_index_info.nConstraint}; }
211✔
556

557
private:
558
    const sqlite3_index_info& vic_index_info;
559
};
560

561
class vtab_index_usage {
562
public:
563
    vtab_index_usage(sqlite3_index_info* index_info)
75✔
564
        : viu_index_info(*index_info)
75✔
565
    {
566
    }
75✔
567

568
    void column_used(const vtab_index_constraints::const_iterator& iter);
569

570
    void allocate_args(int low, int high, int required);
571

572
private:
573
    sqlite3_index_info& viu_index_info;
574
    int viu_used_column_count{0};
575
    int viu_min_column{INT_MAX};
576
    int viu_max_column{0};
577
};
578

579
struct vtab_module_base {
580
    virtual int create(sqlite3* db) = 0;
581

582
    virtual ~vtab_module_base() = default;
8,169✔
583
};
584

585
template<typename T>
586
struct vtab_module : public vtab_module_base {
587
    struct vtab {
588
        explicit vtab(sqlite3* db, T& impl) : v_db(db), v_impl(impl) {}
8,175✔
589

590
        explicit operator sqlite3_vtab*() { return &this->v_base; }
591

592
        sqlite3_vtab v_base{};
593
        sqlite3* v_db;
594
        T& v_impl;
595
    };
596

597
    static int tvt_create(sqlite3* db,
8,175✔
598
                          void* pAux,
599
                          int argc,
600
                          const char* const* argv,
601
                          sqlite3_vtab** pp_vt,
602
                          char** pzErr)
603
    {
604
        auto* mod = static_cast<vtab_module<T>*>(pAux);
8,175✔
605
        auto vt = new vtab(db, mod->vm_impl);
8,175✔
606

607
        *pp_vt = (sqlite3_vtab*) &vt->v_base;
8,175✔
608

609
        return sqlite3_declare_vtab(db, T::CREATE_STMT);
8,175✔
610
    }
611

612
    template<typename... Args, size_t... Idx>
613
    static int apply_impl(T& obj,
101✔
614
                          int (T::*func)(sqlite3_vtab*,
615
                                         sqlite3_int64&,
616
                                         Args...),
617
                          sqlite3_vtab* tab,
618
                          sqlite3_int64& rowid,
619
                          sqlite3_value** argv,
620
                          std::index_sequence<Idx...>)
621
    {
622
        return (obj.*func)(
127✔
623
            tab, rowid, from_sqlite<Args>()(sizeof...(Args), argv, Idx)...);
127✔
624
    }
625

626
    template<typename... Args>
627
    static int apply(T& obj,
101✔
628
                     int (T::*func)(sqlite3_vtab*, sqlite3_int64&, Args...),
629
                     sqlite3_vtab* tab,
630
                     sqlite3_int64& rowid,
631
                     int argc,
632
                     sqlite3_value** argv)
633
    {
634
        require(sizeof...(Args) == 0 || argc == sizeof...(Args));
98✔
635

636
        try {
637
            return apply_impl(obj,
101✔
638
                              func,
639
                              tab,
640
                              rowid,
641
                              argv,
642
                              std::make_index_sequence<sizeof...(Args)>{});
92✔
643
        } catch (const from_sqlite_conversion_error& e) {
9✔
644
            tab->zErrMsg = sqlite3_mprintf(
8✔
645
                "Expecting an %s for column number %d", e.e_type, e.e_argi);
4✔
646
            return SQLITE_ERROR;
4✔
647
        } catch (const std::exception& e) {
5✔
648
            tab->zErrMsg = sqlite3_mprintf("%s", e.what());
5✔
649
            return SQLITE_ERROR;
5✔
UNCOV
650
        } catch (...) {
×
651
            tab->zErrMsg
UNCOV
652
                = sqlite3_mprintf("Encountered an unexpected exception");
×
UNCOV
653
            return SQLITE_ERROR;
×
654
        }
655
    }
656

657
    static int tvt_destructor(sqlite3_vtab* p_svt)
8,175✔
658
    {
659
        vtab* vt = (vtab*) p_svt;
8,175✔
660

661
        delete vt;
8,175✔
662

663
        return SQLITE_OK;
8,175✔
664
    }
665

666
    static int tvt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor)
766✔
667
    {
668
        p_svt->zErrMsg = nullptr;
766✔
669

670
        auto* p_cur = new (typename T::cursor)(p_svt);
766✔
671
        if (p_cur == nullptr) {
766✔
UNCOV
672
            return SQLITE_NOMEM;
×
673
        }
674
        *pp_cursor = (sqlite3_vtab_cursor*) p_cur;
766✔
675

676
        return SQLITE_OK;
766✔
677
    }
678

679
    static int tvt_next(sqlite3_vtab_cursor* cur)
5,968✔
680
    {
681
        auto* p_cur = (typename T::cursor*) cur;
5,968✔
682

683
        return p_cur->next();
5,968✔
684
    }
685

686
    static int tvt_eof(sqlite3_vtab_cursor* cur)
6,736✔
687
    {
688
        auto* p_cur = (typename T::cursor*) cur;
6,736✔
689

690
        return p_cur->eof();
6,736✔
691
    }
692

693
    static int tvt_close(sqlite3_vtab_cursor* cur)
766✔
694
    {
695
        auto* p_cur = (typename T::cursor*) cur;
766✔
696

697
        delete p_cur;
766✔
698

699
        return SQLITE_OK;
766✔
700
    }
701

702
    static int tvt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid)
125✔
703
    {
704
        auto* p_cur = (typename T::cursor*) cur;
125✔
705

706
        return p_cur->get_rowid(*p_rowid);
125✔
707
    }
708

709
    static int tvt_column(sqlite3_vtab_cursor* cur,
44,047✔
710
                          sqlite3_context* ctx,
711
                          int col)
712
    {
713
        auto* mod_vt = (typename vtab_module<T>::vtab*) cur->pVtab;
44,047✔
714
        auto* p_cur = (typename T::cursor*) cur;
44,047✔
715

716
        return mod_vt->v_impl.get_column(*p_cur, ctx, col);
44,047✔
717
    }
718

719
    static int vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
697✔
720
    {
721
        return SQLITE_OK;
697✔
722
    }
723

724
    static int vt_filter(sqlite3_vtab_cursor* p_vtc,
697✔
725
                         int idxNum,
726
                         const char* idxStr,
727
                         int argc,
728
                         sqlite3_value** argv)
729
    {
730
        auto* p_cur = (typename T::cursor*) p_vtc;
697✔
731

732
        return p_cur->reset();
697✔
733
    }
734

735
    static int tvt_update(sqlite3_vtab* tab,
106✔
736
                          int argc,
737
                          sqlite3_value** argv,
738
                          sqlite_int64* rowid)
739
    {
740
        auto* mod_vt = (typename vtab_module<T>::vtab*) tab;
106✔
741

742
        if (argc <= 1) {
106✔
743
            sqlite3_int64 rowid = sqlite3_value_int64(argv[0]);
5✔
744

745
            return mod_vt->v_impl.delete_row(tab, rowid);
5✔
746
        }
747

748
        if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
101✔
749
            sqlite3_int64* rowid2 = rowid;
71✔
750
            return vtab_module<T>::apply(mod_vt->v_impl,
71✔
751
                                         &T::insert_row,
752
                                         tab,
753
                                         *rowid2,
754
                                         argc - 2,
755
                                         argv + 2);
71✔
756
        }
757

758
        sqlite3_int64 index = sqlite3_value_int64(argv[0]);
30✔
759

760
        if (index != sqlite3_value_int64(argv[1])) {
30✔
761
            tab->zErrMsg = sqlite3_mprintf(
×
762
                "The rowids in the lnav_views table cannot be changed");
UNCOV
763
            return SQLITE_ERROR;
×
764
        }
765

766
        return vtab_module<T>::apply(
30✔
767
            mod_vt->v_impl, &T::update_row, tab, index, argc - 2, argv + 2);
30✔
768
    }
769

770
    template<typename U>
771
    auto addUpdate(U u) -> decltype(&U::delete_row, void())
8,169✔
772
    {
773
        this->vm_module.xUpdate = tvt_update;
8,169✔
774
    }
8,169✔
775

776
    template<typename U>
777
    void addUpdate(...)
778
    {
779
    }
780

781
    template<typename... Args>
782
    vtab_module(Args&... args) noexcept : vm_impl(args...)
8,169✔
783
    {
784
        memset(&this->vm_module, 0, sizeof(this->vm_module));
8,169✔
785
        this->vm_module.iVersion = 0;
8,169✔
786
        this->vm_module.xCreate = tvt_create;
8,169✔
787
        this->vm_module.xConnect = tvt_create;
8,169✔
788
        this->vm_module.xOpen = tvt_open;
8,169✔
789
        this->vm_module.xNext = tvt_next;
8,169✔
790
        this->vm_module.xEof = tvt_eof;
8,169✔
791
        this->vm_module.xClose = tvt_close;
8,169✔
792
        this->vm_module.xDestroy = tvt_destructor;
8,169✔
793
        this->vm_module.xRowid = tvt_rowid;
8,169✔
794
        this->vm_module.xDisconnect = tvt_destructor;
8,169✔
795
        this->vm_module.xBestIndex = vt_best_index;
8,169✔
796
        this->vm_module.xFilter = vt_filter;
8,169✔
797
        this->vm_module.xColumn = tvt_column;
8,169✔
798
        this->addUpdate<T>(this->vm_impl);
8,169✔
799
    }
8,169✔
800

801
    ~vtab_module() override = default;
8,169✔
802

803
    int create(sqlite3* db, const char* name)
8,164✔
804
    {
805
        auto impl_name = std::string(name);
8,164✔
806
        vtab_module_schemas += T::CREATE_STMT;
8,164✔
807
        vtab_module_ddls[intern_string::lookup(name)] = trim(T::CREATE_STMT);
24,492✔
808

809
        // XXX Eponymous tables don't seem to work in older sqlite versions
810
        impl_name += "_impl";
8,164✔
811
        int rc = sqlite3_create_module(
8,164✔
812
            db, impl_name.c_str(), &this->vm_module, this);
8,164✔
813
        ensure(rc == SQLITE_OK);
8,164✔
UNCOV
814
        auto create_stmt = fmt::format(
×
815
            FMT_STRING("CREATE VIRTUAL TABLE {} USING {}()"), name, impl_name);
24,492✔
816
        return sqlite3_exec(db, create_stmt.c_str(), nullptr, nullptr, nullptr);
16,328✔
817
    }
8,164✔
818

819
    int create(sqlite3* db) override { return this->create(db, T::NAME); }
4,851✔
820

821
    sqlite3_module vm_module;
822
    T vm_impl;
823
};
824

825
template<typename T>
826
struct tvt_iterator_cursor {
827
    struct cursor {
828
        sqlite3_vtab_cursor base{};
829

830
        typename T::iterator iter;
831

832
        explicit cursor(sqlite3_vtab* vt)
692✔
833
        {
692✔
834
            auto* mod_vt = (typename vtab_module<T>::vtab*) vt;
692✔
835

836
            this->base.pVtab = vt;
692✔
837
            this->iter = mod_vt->v_impl.begin();
692✔
838
        }
692✔
839

840
        int reset()
692✔
841
        {
842
            this->iter = get_handler().begin();
692✔
843

844
            return SQLITE_OK;
692✔
845
        }
846

847
        int next()
5,786✔
848
        {
849
            if (this->iter != get_handler().end()) {
5,786✔
850
                ++this->iter;
5,786✔
851
            }
852

853
            return SQLITE_OK;
5,786✔
854
        }
855

856
        int eof() { return this->iter == get_handler().end(); }
6,484✔
857

858
        template<bool cond, typename U>
859
        using resolvedType = typename std::enable_if<cond, U>::type;
860

861
        template<typename U = int>
862
        resolvedType<
863
            std::is_same<std::random_access_iterator_tag,
864
                         typename std::iterator_traits<
865
                             typename T::iterator>::iterator_category>::value,
866
            U>
867
        get_rowid(sqlite_int64& rowid_out)
116✔
868
        {
869
            rowid_out = std::distance(get_handler().begin(), this->iter);
116✔
870

871
            return SQLITE_OK;
116✔
872
        }
873

874
        template<typename U = int>
875
        resolvedType<
876
            !std::is_same<std::random_access_iterator_tag,
877
                          typename std::iterator_traits<
878
                              typename T::iterator>::iterator_category>::value,
879
            U>
880
        get_rowid(sqlite_int64& rowid_out)
9✔
881
        {
882
            rowid_out = get_handler().get_rowid(this->iter);
9✔
883

884
            return SQLITE_OK;
9✔
885
        }
886

887
    protected:
888
        T& get_handler()
13,099✔
889
        {
890
            auto* mod_vt = (typename vtab_module<T>::vtab*) this->base.pVtab;
13,099✔
891

892
            return mod_vt->v_impl;
13,099✔
893
        }
894
    };
895
};
896

897
template<typename T>
898
struct tvt_no_update : public T {
899
    using T::T;
900

901
    int delete_row(sqlite3_vtab* vt, sqlite3_int64 rowid)
×
902
    {
UNCOV
903
        vt->zErrMsg = sqlite3_mprintf("Rows cannot be deleted from this table");
×
UNCOV
904
        return SQLITE_ERROR;
×
905
    }
906

907
    int insert_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out)
×
908
    {
909
        tab->zErrMsg
UNCOV
910
            = sqlite3_mprintf("Rows cannot be inserted into this table");
×
UNCOV
911
        return SQLITE_ERROR;
×
912
    }
913

UNCOV
914
    int update_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out)
×
915
    {
UNCOV
916
        tab->zErrMsg = sqlite3_mprintf("Rows cannot be updated in this table");
×
UNCOV
917
        return SQLITE_ERROR;
×
918
    }
919
};
920

921
#endif
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