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

tstack / lnav / 20281835752-2752

16 Dec 2025 08:31PM UTC coverage: 68.903% (+0.03%) from 68.87%
20281835752-2752

push

github

tstack
[tests] update test data

51677 of 75000 relevant lines covered (68.9%)

434192.37 hits per line

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

60.21
/src/db_sub_source.cc
1
/**
2
 * Copyright (c) 2014, 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 <iterator>
31
#include <map>
32

33
#include "db_sub_source.hh"
34

35
#include "base/ansi_scrubber.hh"
36
#include "base/date_time_scanner.hh"
37
#include "base/humanize.hh"
38
#include "base/itertools.enumerate.hh"
39
#include "base/itertools.hh"
40
#include "base/map_util.hh"
41
#include "base/math_util.hh"
42
#include "base/time_util.hh"
43
#include "base/types.hh"
44
#include "config.h"
45
#include "hist_source_T.hh"
46
#include "log_level.hh"
47
#include "scn/scan.h"
48
#include "yajlpp/json_ptr.hh"
49
#include "yajlpp/yajlpp_def.hh"
50

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

53
const unsigned char db_label_source::NULL_STR[] = "<NULL>";
54

55
constexpr ssize_t MAX_JSON_WIDTH = 16 * 1024;
56

57
struct user_row_style {
58
    lnav::map::small<std::string, style_config> urs_column_config;
59
};
60

61
static const typed_json_path_container<user_row_style>&
62
get_row_style_handlers()
9✔
63
{
64
    static const json_path_container col_style_handlers = {
65
        yajlpp::pattern_property_handler("(?<column_name>[^/]+)")
3✔
66
            .for_field(&user_row_style::urs_column_config)
3✔
67
            .with_children(style_config_handlers),
3✔
68
    };
18✔
69

70
    static const typed_json_path_container<user_row_style> retval
71
        = typed_json_path_container<user_row_style>{
9✔
72
            yajlpp::property_handler("columns")
9✔
73
                .with_children(col_style_handlers),
3✔
74
    }.with_schema_id2("row-style"_frag);
18✔
75

76
    return retval;
9✔
77
}
12✔
78

79
line_info
80
db_label_source::text_value_for_line(textview_curses& tc,
998✔
81
                                     int row,
82
                                     std::string& label_out,
83
                                     line_flags_t flags)
84
{
85
    /*
86
     * start_value is the result rowid, each bucket type is a column value
87
     * label_out should be the raw text output.
88
     */
89

90
    label_out.clear();
998✔
91
    this->dls_ansi_attrs.clear();
998✔
92
    label_out.reserve(this->dls_max_column_width * this->dls_headers.size());
998✔
93
    if (row < 0_vl || row >= (int) this->dls_row_cursors.size()) {
998✔
94
        return {};
×
95
    }
96
    std::optional<log_level_t> row_level;
998✔
97
    auto cell_cursor = this->dls_row_cursors[row].sync();
998✔
98
    for (size_t lpc = 0; lpc < this->dls_headers.size();
5,428✔
99
         lpc++, cell_cursor = cell_cursor->next())
4,430✔
100
    {
101
        if (lpc == this->dls_row_style_column
4,430✔
102
            && !this->dls_row_styles_have_errors)
9✔
103
        {
104
            continue;
9✔
105
        }
106
        const auto& hm = this->dls_headers[lpc];
4,424✔
107
        if (hm.hm_hidden) {
4,424✔
108
            continue;
3✔
109
        }
110
        ssize_t actual_col_size
111
            = std::min(this->dls_max_column_width, hm.hm_column_size);
4,421✔
112
        auto align = hm.hm_align;
4,421✔
113

114
        if (row < (int) this->dls_row_styles.size()) {
4,421✔
115
            auto style_opt
116
                = this->dls_row_styles[row].rs_column_config.value_for(lpc);
18✔
117
            if (style_opt && style_opt.value()->ta_align.has_value()) {
18✔
118
                align = style_opt.value()->ta_align.value();
3✔
119
            }
120
        }
121

122
        auto sf = cell_cursor->to_string_fragment(this->dls_cell_allocator);
4,421✔
123
        auto al = attr_line_t::from_table_cell_content(
124
            sf, this->dls_max_column_width);
4,421✔
125

126
        if (this->tss_view != nullptr
8,842✔
127
            && cell_cursor->get_type() == lnav::cell_type::CT_TEXT)
4,421✔
128
        {
129
            this->tss_view->apply_highlights(
1,086✔
130
                al, line_range::empty_at(0), line_range::empty_at(0));
2,172✔
131
        }
132
        if (this->dls_level_column && lpc == this->dls_level_column.value()) {
4,421✔
133
            row_level = string2level(sf.data(), sf.length());
119✔
134
        }
135

136
        auto cell_length = al.utf8_length_or_length();
4,421✔
137
        if (actual_col_size < cell_length) {
4,421✔
138
            log_warning(
×
139
                "invalid column size: actual_col_size=%zd < cell_length=%zd",
140
                actual_col_size,
141
                cell_length);
142
            cell_length = actual_col_size;
×
143
        }
144
        const auto padding = actual_col_size - cell_length;
4,421✔
145
        auto lpadding = 0;
4,421✔
146
        auto rpadding = padding;
4,421✔
147
        switch (align) {
4,421✔
148
            case text_align_t::start:
1,990✔
149
                break;
1,990✔
150
            case text_align_t::center: {
1✔
151
                lpadding = padding / 2;
1✔
152
                rpadding = padding - lpadding;
1✔
153
                break;
1✔
154
            }
155
            case text_align_t::end:
2,430✔
156
                lpadding = padding;
2,430✔
157
                rpadding = 0;
2,430✔
158
                break;
2,430✔
159
        }
160
        this->dls_cell_width[lpc] = al.al_string.length() + padding;
4,421✔
161
        label_out.append(lpadding, ' ');
4,421✔
162
        shift_string_attrs(al.al_attrs, 0, label_out.size());
4,421✔
163
        label_out.append(std::move(al.al_string));
4,421✔
164
        label_out.append(rpadding, ' ');
4,421✔
165
        label_out.push_back(' ');
4,421✔
166

167
        this->dls_ansi_attrs.insert(
8,842✔
168
            this->dls_ansi_attrs.end(),
4,421✔
169
            std::make_move_iterator(al.al_attrs.begin()),
170
            std::make_move_iterator(al.al_attrs.end()));
171
    }
4,421✔
172
    if (row_level.has_value()) {
998✔
173
        this->dls_ansi_attrs.emplace_back(line_range{0, -1},
119✔
174
                                          SA_LEVEL.value(row_level.value()));
238✔
175
    }
176
    this->dls_ansi_attrs.reserve(this->dls_ansi_attrs.size()
998✔
177
                                 + 3 * this->dls_headers.size());
998✔
178
    this->dls_cell_allocator.reset();
998✔
179

180
    return {};
998✔
181
}
182

183
void
184
db_label_source::text_attrs_for_line(textview_curses& tc,
998✔
185
                                     int row,
186
                                     string_attrs_t& sa)
187
{
188
    static const auto NUM_ATTR = VC_ROLE.value(role_t::VCR_NUMBER);
998✔
189
    static const auto VLINE_ATTR = VC_GRAPHIC.value(NCACS_VLINE);
998✔
190

191
    line_range lr(0, 0);
998✔
192
    const line_range lr2(0, -1);
998✔
193

194
    if (row < 0_vl || row >= (int) this->dls_row_cursors.size()) {
998✔
195
        return;
×
196
    }
197
    sa = std::move(this->dls_ansi_attrs);
998✔
198
    auto alt_row_index = row % 4;
998✔
199
    if (alt_row_index == 2 || alt_row_index == 3) {
998✔
200
        sa.emplace_back(lr2, VC_ROLE.value(role_t::VCR_ALT_ROW));
377✔
201
    }
202
    sa.emplace_back(line_range{0, 0}, SA_ORIGINAL_LINE.value());
998✔
203
    sa.emplace_back(line_range{0, 0}, SA_BODY.value());
998✔
204
    for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) {
4,430✔
205
        if (lpc == this->dls_row_style_column
3,432✔
206
            && !this->dls_row_styles_have_errors)
×
207
        {
208
            continue;
×
209
        }
210

211
        const auto& hm = this->dls_headers[lpc];
3,432✔
212
        if (hm.hm_hidden) {
3,432✔
213
            continue;
3✔
214
        }
215

216
        if (hm.is_graphable()) {
3,429✔
217
            lr.lr_end += this->dls_cell_width[lpc];
717✔
218
            sa.emplace_back(lr, NUM_ATTR);
717✔
219
        }
220
        lr.lr_start += this->dls_cell_width[lpc];
3,429✔
221
        lr.lr_end = lr.lr_start + 1;
3,429✔
222
        sa.emplace_back(lr, VLINE_ATTR);
3,429✔
223
        lr.lr_start += 1;
3,429✔
224
    }
225

226
    for (const auto& attr : sa) {
7,787✔
227
        require_ge(attr.sa_range.lr_start, 0);
6,789✔
228
    }
229
    int cell_start = 0;
998✔
230
    auto cursor = this->dls_row_cursors[row].sync();
998✔
231
    for (size_t lpc = 0; lpc < this->dls_headers.size();
5,428✔
232
         lpc++, cursor = cursor->next())
4,430✔
233
    {
234
        std::optional<text_attrs> user_attrs;
4,430✔
235

236
        if (lpc == this->dls_row_style_column) {
4,430✔
237
            if (!this->dls_row_styles_have_errors) {
9✔
238
                continue;
9✔
239
            }
240
        }
241

242
        const auto& hm = this->dls_headers[lpc];
4,424✔
243
        if (hm.hm_hidden) {
4,424✔
244
            continue;
3✔
245
        }
246
        if (row < (ssize_t) this->dls_row_styles.size()) {
4,421✔
247
            auto style_opt
248
                = this->dls_row_styles[row].rs_column_config.value_for(lpc);
18✔
249
            if (style_opt) {
18✔
250
                user_attrs = *style_opt.value();
6✔
251
            }
252
        }
253

254
        int left = cell_start;
4,421✔
255
        auto stlr = line_range{
256
            cell_start,
257
            (int) (cell_start + this->dls_cell_width[lpc]),
8,842✔
258
        };
4,421✔
259
        if (hm.is_graphable()) {
4,421✔
260
            std::optional<double> get_res;
1,075✔
261
            if (cursor->get_type() == lnav::cell_type::CT_INTEGER) {
1,075✔
262
                get_res = cursor->get_int();
768✔
263
            } else if (cursor->get_type() == lnav::cell_type::CT_FLOAT) {
307✔
264
                get_res = cursor->get_float();
307✔
265
            }
266
            if (get_res.has_value()) {
1,075✔
267
                hm.hm_chart.chart_attrs_for_value(tc,
1,075✔
268
                                                  left,
269
                                                  this->dls_cell_width[lpc],
1,075✔
270
                                                  hm.hm_name,
1,075✔
271
                                                  get_res.value(),
1,075✔
272
                                                  sa,
273
                                                  user_attrs);
274

275
                for (const auto& attr : sa) {
19,125✔
276
                    require_ge(attr.sa_range.lr_start, 0);
18,050✔
277
                }
278
            }
279
        } else if (user_attrs.has_value()) {
3,346✔
280
            sa.emplace_back(stlr, VC_STYLE.value(user_attrs.value()));
3✔
281
        }
282
        auto cell_sf = string_fragment::invalid();
4,421✔
283
        if (cursor->get_type() == lnav::cell_type::CT_TEXT) {
4,421✔
284
            cell_sf = cursor->get_text();
1,642✔
285
        } else if (cursor->get_type() == lnav::cell_type::CT_NULL) {
2,779✔
286
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_NULL));
989✔
287
        }
288
        if (lpc == this->dls_row_style_column) {
4,421✔
289
            sa.emplace_back(stlr, VC_ROLE.value(role_t::VCR_ERROR));
3✔
290
        } else if (cell_sf.is_valid() && cell_sf.length() > 2
6,057✔
291
                   && cell_sf.length() < MAX_JSON_WIDTH
1,398✔
292
                   && ((cell_sf.front() == '{' && cell_sf.back() == '}')
7,398✔
293
                       || (cell_sf.front() == '[' && cell_sf.back() == ']')))
1,341✔
294
        {
295
            auto cb = [&](const std::string& ptr, const scoped_value_t& sv) {
187✔
296
                auto val_opt = to_double(sv);
187✔
297
                if (val_opt) {
187✔
298
                    hm.hm_chart.chart_attrs_for_value(tc,
126✔
299
                                                      left,
300
                                                      this->dls_cell_width[lpc],
63✔
301
                                                      ptr,
302
                                                      val_opt.value(),
63✔
303
                                                      sa);
304
                    for (const auto& attr : sa) {
573✔
305
                        require_ge(attr.sa_range.lr_start, 0);
510✔
306
                    }
307
                }
308
            };
187✔
309
            json_ptr_walk jpw(cb);
112✔
310

311
            jpw.parse_fully(cell_sf);
112✔
312
        }
112✔
313
        cell_start += this->dls_cell_width[lpc] + 1;
4,421✔
314
    }
315

316
    for (const auto& attr : sa) {
9,605✔
317
        require_ge(attr.sa_range.lr_start, 0);
8,607✔
318
    }
319
}
320

321
void
322
db_label_source::set_col_as_graphable(int lpc)
711✔
323
{
324
    static auto& vc = view_colors::singleton();
711✔
325

326
    auto& hm = this->dls_headers[lpc];
711✔
327
    auto name_for_ident_attrs = hm.hm_name;
711✔
328
    auto attrs = vc.attrs_for_ident(name_for_ident_attrs);
711✔
329
    for (size_t attempt = 0; hm.hm_chart.attrs_in_use(attrs) && attempt < 3;
711✔
330
         attempt++)
331
    {
332
        name_for_ident_attrs += " ";
×
333
        attrs = vc.attrs_for_ident(name_for_ident_attrs);
×
334
    }
335
    hm.hm_graphable = true;
711✔
336
    hm.hm_chart.with_attrs_for_ident(hm.hm_name, attrs);
711✔
337
    hm.hm_title_attrs = attrs | text_attrs::with_reverse();
711✔
338
    hm.hm_column_size = std::max(hm.hm_column_size, size_t{10});
711✔
339
}
711✔
340

341
void
342
db_label_source::push_header(const std::string& colstr, int type)
3,041✔
343
{
344
    this->dls_headers.emplace_back(colstr);
3,041✔
345
    this->dls_cell_width.push_back(0);
3,041✔
346

347
    auto& hm = this->dls_headers.back();
3,041✔
348

349
    hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length());
3,041✔
350
    hm.hm_column_type = type;
3,041✔
351
    if (colstr == "log_time" || colstr == "min(log_time)"
6,014✔
352
        || colstr == "log_time_msecs")
6,014✔
353
    {
354
        this->dls_time_column_index = this->dls_headers.size() - 1;
74✔
355
    }
356
    if (colstr == "__lnav_style__") {
3,041✔
357
        this->dls_row_style_column = this->dls_headers.size() - 1;
3✔
358
    }
359
    if (colstr == "log_level") {
3,041✔
360
        this->dls_level_column = this->dls_headers.size() - 1;
54✔
361
    }
362
    hm.hm_chart.with_show_state(stacked_bar_chart_base::show_all{});
3,041✔
363
}
3,041✔
364

365
void
366
db_label_source::update_time_column(const timeval& tv)
317✔
367
{
368
    if (!this->dls_time_column.empty() && tv < this->dls_time_column.back()) {
317✔
369
        this->dls_time_column_invalidated_at = this->dls_time_column.size();
1✔
370
        this->dls_time_column_index = SIZE_MAX;
1✔
371
        this->dls_time_column.clear();
1✔
372
    } else {
373
        this->dls_time_column.push_back(tv);
316✔
374
    }
375
}
317✔
376

377
void
378
db_label_source::update_time_column(const string_fragment& sf)
296✔
379
{
380
    date_time_scanner dts;
296✔
381
    timeval tv;
382

383
    if (!dts.convert_to_timeval(sf.data(), sf.length(), nullptr, tv)) {
296✔
384
        tv.tv_sec = -1;
×
385
        tv.tv_usec = -1;
×
386
    }
387
    this->update_time_column(tv);
296✔
388
}
296✔
389

390
void
391
db_label_source::push_column(const column_value_t& sv)
9,164✔
392
{
393
    auto row_index = this->dls_row_cursors.size() - 1;
9,164✔
394
    auto& vc = view_colors::singleton();
9,164✔
395
    auto col = this->dls_push_column++;
9,164✔
396
    auto& hm = this->dls_headers[col];
9,164✔
397
    size_t width = 1;
9,164✔
398
    auto cv_sf = string_fragment::invalid();
9,164✔
399

400
    sv.match(
9,164✔
401
        [this, &col, &width, &cv_sf, &hm, &row_index](
×
402
            const string_fragment& sf) {
403
            if (this->dls_row_style_column == col) {
4,087✔
404
                return;
9✔
405
            }
406
            if (col == this->dls_time_column_index) {
4,078✔
407
                this->update_time_column(sf);
296✔
408
            } else if (this->dls_level_column
7,564✔
409
                       && this->dls_level_column.value() == col
5,094✔
410
                       && this->tss_view != nullptr)
5,094✔
411
            {
412
                auto& bm = this->tss_view->get_bookmarks();
216✔
413
                auto lev = string2level(sf.data(), sf.length());
216✔
414
                switch (lev) {
216✔
415
                    case log_level_t::LEVEL_FATAL:
36✔
416
                    case log_level_t::LEVEL_CRITICAL:
417
                    case log_level_t::LEVEL_ERROR:
418
                        bm[&textview_curses::BM_ERRORS].insert_once(
72✔
419
                            vis_line_t(row_index));
36✔
420
                        break;
36✔
421
                    case log_level_t::LEVEL_WARNING:
6✔
422
                        bm[&textview_curses::BM_WARNINGS].insert_once(
12✔
423
                            vis_line_t(row_index));
6✔
424
                        break;
6✔
425
                    default:
174✔
426
                        break;
174✔
427
                }
428
            }
429
            width = utf8_string_length(sf.data(), sf.length())
4,078✔
430
                        .unwrapOr(sf.length());
4,078✔
431
            if (hm.is_graphable()
4,078✔
432
                && sf.length() < lnav::cell_container::SHORT_TEXT_LENGTH)
4,078✔
433
            {
434
                auto from_res = humanize::try_from<double>(sf);
382✔
435
                if (from_res.has_value()) {
382✔
436
                    this->dls_cell_container.push_float_with_units_cell(
764✔
437
                        from_res.value(), sf);
382✔
438
                } else {
439
                    this->dls_cell_container.push_text_cell(sf);
×
440
                }
441
            } else {
442
                this->dls_cell_container.push_text_cell(sf);
3,696✔
443
            }
444
            cv_sf = sf;
4,078✔
445
        },
446
        [this, col, &width](int64_t i) {
×
447
            width = count_digits(i);
2,690✔
448
            this->dls_cell_container.push_int_cell(i);
2,690✔
449
            if (col == this->dls_time_column_index) {
2,690✔
450
                auto ms = std::chrono::milliseconds{i};
21✔
451
                auto us
452
                    = std::chrono::duration_cast<std::chrono::microseconds>(ms);
21✔
453

454
                this->update_time_column(to_timeval(us));
21✔
455
            }
456
        },
2,690✔
457
        [this, &width](double d) {
×
458
            char buffer[1];
459
            auto fmt_res = fmt::format_to_n(buffer, 0, FMT_STRING("{}"), d);
693✔
460
            width = fmt_res.size;
231✔
461
            this->dls_cell_container.push_float_cell(d);
231✔
462
        },
231✔
463
        [this, &width](null_value_t) {
9,164✔
464
            width = 6;
2,156✔
465
            this->dls_cell_container.push_null_cell();
2,156✔
466
        });
2,156✔
467

468
    if (col == this->dls_row_style_column) {
9,164✔
469
        auto col_sf = string_fragment::invalid();
9✔
470
        if (sv.is<null_value_t>()) {
9✔
471
            this->dls_row_styles.emplace_back(row_style{});
×
472
        } else if (sv.is<string_fragment>()) {
9✔
473
            static const intern_string_t SRC
474
                = intern_string::lookup("__lnav_style__");
15✔
475
            auto frag = sv.get<string_fragment>();
9✔
476
            if (frag.empty()) {
9✔
477
                this->dls_row_styles.emplace_back(row_style{});
×
478
            } else {
479
                auto parse_res
480
                    = get_row_style_handlers().parser_for(SRC).of(frag);
9✔
481
                if (parse_res.isErr()) {
9✔
482
                    log_error("DB row %zu JSON is invalid:", row_index);
×
483
                    auto errors = parse_res.unwrapErr();
×
484
                    for (const auto& err : errors) {
×
485
                        log_error("  %s", err.to_attr_line().al_string.c_str());
×
486
                    }
487
                    col_sf = string_fragment::from_str(
×
488
                                 errors[0].to_attr_line().al_string)
×
489
                                 .to_owned(this->dls_cell_allocator);
×
490
                    this->dls_row_styles_have_errors = true;
×
491
                } else {
×
492
                    auto urs = parse_res.unwrap();
9✔
493
                    auto rs = row_style{};
9✔
494
                    for (const auto& [col_name, col_style] :
18✔
495
                         urs.urs_column_config)
27✔
496
                    {
497
                        auto col_index_opt
498
                            = this->column_name_to_index(col_name);
9✔
499
                        if (!col_index_opt) {
9✔
500
                            log_error("DB row %zu column name '%s' not found",
3✔
501
                                      row_index,
502
                                      col_name.c_str());
503
                            col_sf = string_fragment::from_str(
×
504
                                         fmt::format(
3✔
505
                                             FMT_STRING(
9✔
506
                                                 "column name '{}' not found"),
507
                                             col_name))
508
                                         .to_owned(this->dls_cell_allocator);
3✔
509
                            this->dls_row_styles_have_errors = true;
3✔
510
                        } else {
511
                            text_attrs ta;
6✔
512

513
                            auto fg_res = styling::color_unit::from_str(
514
                                col_style.sc_color);
6✔
515
                            if (fg_res.isErr()) {
6✔
516
                                log_error("DB row %zu color is invalid: %s",
×
517
                                          row_index,
518
                                          fg_res.unwrapErr().c_str());
519
                                col_sf
520
                                    = string_fragment::from_str(
×
521
                                          fmt::format(
×
522
                                              FMT_STRING("invalid color: {}"),
×
523
                                              fg_res.unwrapErr()))
×
524
                                          .to_owned(this->dls_cell_allocator);
×
525
                                this->dls_row_styles_have_errors = true;
×
526
                            } else {
527
                                ta.ta_fg_color
528
                                    = vc.match_color(fg_res.unwrap());
6✔
529
                            }
530
                            auto bg_res = styling::color_unit::from_str(
531
                                col_style.sc_background_color);
6✔
532
                            if (bg_res.isErr()) {
6✔
533
                                log_error(
×
534
                                    "DB row %zu background-color is invalid: "
535
                                    "%s",
536
                                    row_index,
537
                                    bg_res.unwrapErr().c_str());
538
                                col_sf
539
                                    = string_fragment::from_str(
×
540
                                          fmt::format(
×
541
                                              FMT_STRING(
×
542
                                                  "invalid "
543
                                                  "background-color: {}"),
544
                                              fg_res.unwrapErr()))
×
545
                                          .to_owned(this->dls_cell_allocator);
×
546
                                this->dls_row_styles_have_errors = true;
×
547
                            } else {
548
                                ta.ta_bg_color
549
                                    = vc.match_color(bg_res.unwrap());
6✔
550
                            }
551
                            ta.ta_align = col_style.sc_text_align;
6✔
552
                            if (col_style.sc_underline) {
6✔
553
                                ta |= text_attrs::style::underline;
×
554
                            }
555
                            if (col_style.sc_bold) {
6✔
556
                                ta |= text_attrs::style::bold;
×
557
                            }
558
                            if (col_style.sc_italic) {
6✔
559
                                ta |= text_attrs::style::italic;
×
560
                            }
561
                            if (col_style.sc_strike) {
6✔
562
                                ta |= text_attrs::style::struck;
×
563
                            }
564
                            if (this->dls_headers[col_index_opt.value()]
6✔
565
                                    .is_graphable())
6✔
566
                            {
567
                                this->dls_headers[col_index_opt.value()]
3✔
568
                                    .hm_title_attrs
569
                                    = text_attrs::with_underline();
6✔
570
                            }
571
                            rs.rs_column_config[col_index_opt.value()] = ta;
6✔
572
                        }
6✔
573
                    }
574
                    this->dls_row_styles.emplace_back(std::move(rs));
9✔
575
                }
9✔
576
            }
9✔
577
        } else {
578
            log_error("DB row %zu is not a string -- %s",
×
579
                      row_index,
580
                      mapbox::util::apply_visitor(type_visitor(), sv));
581

582
            col_sf
583
                = string_fragment::from_str("expecting a JSON object for style")
×
584
                      .to_owned(this->dls_cell_allocator);
×
585
            this->dls_row_styles_have_errors = true;
×
586
        }
587

588
        if (col_sf.empty()) {
9✔
589
            this->dls_cell_container.push_null_cell();
6✔
590
        } else {
591
            this->dls_cell_container.push_text_cell(col_sf);
3✔
592
            width = utf8_string_length(col_sf.data(), col_sf.length())
3✔
593
                        .unwrapOr(col_sf.length());
3✔
594
            this->dls_cell_allocator.reset();
3✔
595
        }
596
    }
597

598
    hm.hm_column_size = std::max(this->dls_headers[col].hm_column_size, width);
9,164✔
599
    if (hm.is_graphable()) {
9,164✔
600
        if (sv.is<int64_t>()) {
2,018✔
601
            hm.hm_chart.add_value(hm.hm_name, sv.get<int64_t>());
1,409✔
602
            hm.hm_tdigest.insert(sv.get<int64_t>());
1,409✔
603
        } else if (sv.is<double>()) {
609✔
604
            hm.hm_chart.add_value(hm.hm_name, sv.get<double>());
221✔
605
            hm.hm_tdigest.insert(sv.get<double>());
221✔
606
        } else if (sv.is<string_fragment>()) {
388✔
607
            auto sf = sv.get<string_fragment>();
382✔
608
            auto num_from_res = humanize::try_from<double>(sf);
382✔
609
            if (num_from_res) {
382✔
610
                hm.hm_chart.add_value(hm.hm_name, num_from_res.value());
382✔
611
                hm.hm_tdigest.insert(num_from_res.value());
382✔
612
            }
613
        }
614
    } else if (cv_sf.is_valid() && cv_sf.length() > 2
10,842✔
615
               && ((cv_sf.startswith("{") && cv_sf.endswith("}"))
13,881✔
616
                   || (cv_sf.startswith("[") && cv_sf.endswith("]"))))
3,039✔
617
    {
618
        auto cb = [this, &hm, &vc](const std::string& ptr,
1,078✔
619
                                   const scoped_value_t& sv) {
620
            auto ptr_sf = string_fragment::from_str(ptr);
1,078✔
621
            auto iter = hm.hm_json_columns.find(ptr_sf);
1,078✔
622
            if (iter == hm.hm_json_columns.end()) {
1,078✔
623
                auto owned_ptr_sf = ptr_sf.to_owned(this->dls_header_allocator);
896✔
624
                iter = hm.hm_json_columns
1,792✔
625
                           .emplace(owned_ptr_sf, hm.hm_json_columns.size())
896✔
626
                           .first;
627
            }
628
            if (sv.is<int64_t>()) {
1,078✔
629
                auto val = sv.get<int64_t>();
334✔
630
                auto& ci = hm.hm_chart.add_value(ptr, val);
334✔
631
                if (ci.ci_attrs.empty()) {
334✔
632
                    ci.ci_attrs = vc.attrs_for_ident(ptr);
312✔
633
                }
634
            } else if (sv.is<double>()) {
744✔
635
                auto val = sv.get<double>();
35✔
636
                auto& ci = hm.hm_chart.add_value(ptr, val);
35✔
637
                if (ci.ci_attrs.empty()) {
35✔
638
                    ci.ci_attrs = vc.attrs_for_ident(ptr);
35✔
639
                }
640
            }
641
        };
1,078✔
642
        json_ptr_walk jpw(cb);
388✔
643

644
        jpw.parse_fully(cv_sf);
388✔
645
    }
388✔
646
    hm.hm_chart.next_row();
9,164✔
647
}
9,164✔
648

649
void
650
db_label_source::clear()
2,655✔
651
{
652
    this->dls_generation += 1;
2,655✔
653
    this->dls_query_start = std::nullopt;
2,655✔
654
    this->dls_query_end = std::nullopt;
2,655✔
655
    this->dls_headers.clear();
2,655✔
656
    this->dls_row_cursors.clear();
2,655✔
657
    this->dls_cell_container.reset();
2,655✔
658
    this->dls_time_column.clear();
2,655✔
659
    this->dls_time_column_index = SIZE_MAX;
2,655✔
660
    this->dls_cell_width.clear();
2,655✔
661
    this->dls_row_styles.clear();
2,655✔
662
    this->dls_row_styles_have_errors = false;
2,655✔
663
    this->dls_row_style_column = SIZE_MAX;
2,655✔
664
    this->dls_level_column = std::nullopt;
2,655✔
665
    this->dls_cell_allocator.reset();
2,655✔
666
    this->dls_header_allocator.reset();
2,655✔
667
    this->dls_step_rc = SQLITE_OK;
2,655✔
668
    if (this->tss_view != nullptr) {
2,655✔
669
        this->tss_view->get_bookmarks().clear();
333✔
670
    }
671
}
2,655✔
672

673
std::optional<size_t>
674
db_label_source::column_name_to_index(const std::string& name) const
15✔
675
{
676
    return this->dls_headers | lnav::itertools::find(name);
15✔
677
}
678

679
std::optional<vis_line_t>
680
db_label_source::row_for_time(timeval time_bucket)
5✔
681
{
682
    const auto iter = std::lower_bound(this->dls_time_column.begin(),
5✔
683
                                       this->dls_time_column.end(),
684
                                       time_bucket);
685
    if (iter != this->dls_time_column.end()) {
5✔
686
        return vis_line_t(std::distance(this->dls_time_column.begin(), iter));
8✔
687
    }
688
    return std::nullopt;
1✔
689
}
690

691
std::optional<text_time_translator::row_info>
692
db_label_source::time_for_row(vis_line_t row)
306✔
693
{
694
    if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) {
306✔
695
        return std::nullopt;
250✔
696
    }
697

698
    return row_info{this->dls_time_column[row], row};
56✔
699
}
700

701
bool
702
db_label_source::text_handle_mouse(
×
703
    textview_curses& tc,
704
    const listview_curses::display_line_content_t&,
705
    mouse_event& me)
706
{
707
    if (tc.get_overlay_selection()) {
×
708
        auto nci = ncinput{};
×
709
        if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 0, 3)) {
×
710
            nci.id = ' ';
×
711
            nci.eff_text[0] = ' ';
×
712
            this->list_input_handle_key(tc, nci);
×
713
        }
714
    }
715
    return true;
×
716
}
717

718
static constexpr string_attr_type<std::string> DBA_DETAILS("details");
719
static constexpr string_attr_type<std::string> DBA_COLUMN_NAME("column-name");
720

721
bool
722
db_label_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
×
723
{
724
    switch (ch.eff_text[0]) {
×
725
        case ' ': {
×
726
            auto ov_sel = lv.get_overlay_selection();
×
727
            if (ov_sel) {
×
728
                std::vector<attr_line_t> rows;
×
729
                auto* ov_source = lv.get_overlay_source();
×
730
                ov_source->list_value_for_overlay(
×
731
                    lv, lv.get_selection().value(), rows);
×
732
                if (ov_sel.value() < (ssize_t) rows.size()) {
×
733
                    auto& row_al = rows[ov_sel.value()];
×
734
                    auto col_attr
735
                        = get_string_attr(row_al.al_attrs, DBA_COLUMN_NAME);
×
736
                    if (col_attr) {
×
737
                        auto col_name = col_attr.value().get();
×
738
                        auto col_opt = this->column_name_to_index(col_name);
×
739
                        if (col_opt) {
×
740
                            this->dls_headers[col_opt.value()].hm_hidden
×
741
                                = !this->dls_headers[col_opt.value()].hm_hidden;
×
742
                        }
743
                    }
744
                }
745
                lv.set_needs_update();
×
746

747
                return true;
×
748
            }
749
            break;
×
750
        }
751
    }
752

753
    return false;
×
754
}
755

756
std::optional<json_string>
757
db_label_source::text_row_details(const textview_curses& tc)
3✔
758
{
759
    if (this->dls_row_cursors.empty()) {
3✔
760
        log_trace("db_label_source::text_row_details: empty");
×
761
        return std::nullopt;
×
762
    }
763
    if (!this->dls_query_end.has_value()) {
3✔
764
        log_trace("db_label_source::text_row_details: query in progress");
2✔
765
        return std::nullopt;
2✔
766
    }
767

768
    auto ov_sel = tc.get_overlay_selection();
1✔
769

770
    if (ov_sel.has_value()) {
1✔
771
        std::vector<attr_line_t> rows;
×
772
        auto* ov_source = tc.get_overlay_source();
×
773
        ov_source->list_value_for_overlay(tc, tc.get_selection().value(), rows);
×
774
        if (ov_sel.value() < (ssize_t) rows.size()) {
×
775
            auto& row_al = rows[ov_sel.value()];
×
776
            auto deets_attr = get_string_attr(row_al.al_attrs, DBA_DETAILS);
×
777
            if (deets_attr) {
×
778
                auto deets = deets_attr->get();
×
779
                if (!deets.empty()) {
×
780
                    return json_string(
×
781
                        auto_buffer::from(deets.c_str(), deets.length()));
×
782
                }
783
            }
784
        }
785
    } else {
×
786
        yajlpp_gen gen;
1✔
787

788
        {
789
            yajlpp_map root(gen);
1✔
790

791
            auto sel = tc.get_selection();
1✔
792
            if (sel) {
1✔
793
                root.gen("value");
1✔
794

795
                {
796
                    yajlpp_map value_map(gen);
1✔
797

798
                    auto cursor
799
                        = this->dls_row_cursors[tc.get_selection().value()]
1✔
800
                              .sync();
1✔
801
                    for (const auto& [col, hm] :
22✔
802
                         lnav::itertools::enumerate(this->dls_headers))
23✔
803
                    {
804
                        value_map.gen(hm.hm_name);
21✔
805

806
                        switch (cursor->get_type()) {
21✔
807
                            case lnav::cell_type::CT_NULL:
9✔
808
                                value_map.gen();
9✔
809
                                break;
9✔
810
                            case lnav::cell_type::CT_INTEGER:
5✔
811
                                value_map.gen(cursor->get_int());
5✔
812
                                break;
5✔
813
                            case lnav::cell_type::CT_FLOAT:
×
814
                                if (cursor->get_sub_value() == 0) {
×
815
                                    value_map.gen(cursor->get_float());
×
816
                                } else {
817
                                    value_map.gen(cursor->get_float_as_text());
×
818
                                }
819
                                break;
×
820
                            case lnav::cell_type::CT_TEXT:
7✔
821
                                value_map.gen(cursor->get_text());
7✔
822
                                break;
7✔
823
                        }
824
                        cursor = cursor->next();
21✔
825
                    }
826
                }
1✔
827
            }
828
        }
1✔
829

830
        return json_string{gen};
1✔
831
    }
1✔
832

833
    return std::nullopt;
×
834
}
835

836
std::string
837
db_label_source::get_row_as_string(vis_line_t row)
1,480✔
838
{
839
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())) {
1,480✔
840
        return "";
×
841
    }
842

843
    if (this->dls_headers.size() == 1) {
1,480✔
844
        return this->dls_row_cursors[row]
1,472✔
845
            .sync()
1,472✔
846
            .value()
1,472✔
847
            .to_string_fragment(this->dls_cell_allocator)
2,944✔
848
            .to_string();
1,472✔
849
    }
850

851
    std::string retval;
8✔
852
    size_t lpc = 0;
8✔
853
    auto cursor = this->dls_row_cursors[row].sync();
8✔
854
    while (lpc < this->dls_headers.size() && cursor.has_value()) {
32✔
855
        const auto& hm = this->dls_headers[lpc];
24✔
856

857
        if (!retval.empty()) {
24✔
858
            retval.append("; ");
16✔
859
        }
860
        retval.append(hm.hm_name);
24✔
861
        retval.push_back('=');
24✔
862
        auto sf = cursor->to_string_fragment(this->dls_cell_allocator);
24✔
863
        retval += sf;
24✔
864

865
        cursor = cursor->next();
24✔
866
        lpc += 1;
24✔
867
    }
868
    this->dls_cell_allocator.reset();
8✔
869

870
    return retval;
8✔
871
}
8✔
872

873
std::string
874
db_label_source::get_cell_as_string(vis_line_t row, size_t col)
×
875
{
876
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
877
        || col >= this->dls_headers.size())
×
878
    {
879
        return "";
×
880
    }
881

882
    this->dls_cell_allocator.reset();
×
883
    size_t lpc = 0;
×
884
    auto cursor = this->dls_row_cursors[row].sync();
×
885
    while (cursor.has_value()) {
×
886
        if (lpc == col) {
×
887
            return cursor->to_string_fragment(this->dls_cell_allocator)
×
888
                .to_string();
×
889
        }
890

891
        cursor = cursor->next();
×
892
        lpc += 1;
×
893
    }
894

895
    return "";
×
896
}
897

898
std::optional<int64_t>
899
db_label_source::get_cell_as_int64(vis_line_t row, size_t col) const
×
900
{
901
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
902
        || col >= this->dls_headers.size())
×
903
    {
904
        return std::nullopt;
×
905
    }
906

907
    size_t lpc = 0;
×
908
    auto cursor = this->dls_row_cursors[row].sync();
×
909
    while (cursor.has_value()) {
×
910
        if (lpc == col) {
×
911
            if (cursor->get_type() == lnav::cell_type::CT_INTEGER) {
×
912
                return cursor->get_int();
×
913
            }
914
            return std::nullopt;
×
915
        }
916

917
        cursor = cursor->next();
×
918
        lpc += 1;
×
919
    }
920

921
    return std::nullopt;
×
922
}
923

924
std::optional<double>
925
db_label_source::get_cell_as_double(vis_line_t row, size_t col) const
×
926
{
927
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
928
        || col >= this->dls_headers.size())
×
929
    {
930
        return std::nullopt;
×
931
    }
932

933
    size_t lpc = 0;
×
934
    auto cursor = this->dls_row_cursors[row].sync();
×
935
    while (cursor.has_value()) {
×
936
        if (lpc == col) {
×
937
            switch (cursor->get_type()) {
×
938
                case lnav::cell_type::CT_INTEGER:
×
939
                    return cursor->get_int();
×
940
                case lnav::cell_type::CT_FLOAT:
×
941
                    return cursor->get_float();
×
942
                default:
×
943
                    return std::nullopt;
×
944
            }
945
        }
946

947
        cursor = cursor->next();
×
948
        lpc += 1;
×
949
    }
950

951
    return std::nullopt;
×
952
}
953

954
mapbox::util::variant<int64_t, double>
955
db_label_source::get_cell_as_numeric(vis_line_t row, size_t col) const
×
956
{
957
    if (row < 0_vl || (((size_t) row) >= this->dls_row_cursors.size())
×
958
        || col >= this->dls_headers.size())
×
959
    {
960
        return numeric_cell_t{mapbox::util::no_init{}};
×
961
    }
962

963
    size_t lpc = 0;
×
964
    auto cursor = this->dls_row_cursors[row].sync();
×
965
    while (cursor.has_value()) {
×
966
        if (lpc == col) {
×
967
            switch (cursor->get_type()) {
×
968
                case lnav::cell_type::CT_INTEGER:
×
969
                    return cursor->get_int();
×
970
                case lnav::cell_type::CT_FLOAT:
×
971
                    return cursor->get_float();
×
972
                default:
×
973
                    return numeric_cell_t{mapbox::util::no_init{}};
×
974
            }
975
        }
976

977
        cursor = cursor->next();
×
978
        lpc += 1;
×
979
    }
980

981
    return numeric_cell_t{mapbox::util::no_init{}};
×
982
}
983

984
void
985
db_label_source::reset_user_state()
6✔
986
{
987
    for (auto& hm : this->dls_headers) {
6✔
988
        hm.hm_hidden = false;
×
989
    }
990
}
6✔
991

992
std::optional<attr_line_t>
993
db_overlay_source::list_header_for_overlay(const listview_curses& lv,
×
994
                                           media_t media,
995
                                           vis_line_t line)
996
{
997
    attr_line_t retval;
×
998

999
    retval.append("  Details for row ")
×
1000
        .append(
×
1001
            lnav::roles::number(fmt::format(FMT_STRING("{:L}"), (int) line)))
×
1002
        .append(". Press ")
×
1003
        .append("p"_hotkey)
×
1004
        .append(" to hide this panel.");
×
1005
    if (lv.get_overlay_selection()) {
×
1006
        retval.append(" Controls: ")
×
1007
            .append("c"_hotkey)
×
1008
            .append(" to copy a column value; ")
×
1009
            .append("SPC"_hotkey)
×
1010
            .append(" to hide/show a column");
×
1011
    } else {
1012
        retval.append("  Press ")
×
1013
            .append("CTRL-]"_hotkey)
×
1014
            .append(" to focus on this panel");
×
1015
    }
1016
    return retval;
×
1017
}
1018

1019
void
1020
db_overlay_source::list_value_for_overlay(const listview_curses& lv,
700✔
1021
                                          vis_line_t row,
1022
                                          std::vector<attr_line_t>& value_out)
1023
{
1024
    if (!this->dos_active || lv.get_inner_height() == 0) {
700✔
1025
        return;
700✔
1026
    }
1027

1028
    auto sel = lv.get_selection();
×
1029
    if (!sel || row != sel.value()) {
×
1030
        return;
×
1031
    }
1032

1033
    auto& vc = view_colors::singleton();
×
1034
    unsigned long width;
1035
    vis_line_t height;
×
1036

1037
    lv.get_dimensions(height, width);
×
1038

1039
    auto max_name_width = this->dos_labels->dls_headers
×
1040
        | lnav::itertools::map([](const auto& hm) { return hm.hm_name.size(); })
×
1041
        | lnav::itertools::max();
×
1042

1043
    auto cursor = this->dos_labels->dls_row_cursors[row].sync();
×
1044
    for (const auto& [col, hm] :
×
1045
         lnav::itertools::enumerate(this->dos_labels->dls_headers))
×
1046
    {
1047
        auto al = attr_line_t()
×
1048
                      .append(lnav::roles::h3(hm.hm_name))
×
1049
                      .right_justify(max_name_width.value_or(0) + 2);
×
1050

1051
        if (hm.hm_hidden) {
×
1052
            al.insert(1, "\u25c7"_comment);
×
1053
        } else {
1054
            al.insert(1, "\u25c6"_ok);
×
1055
        }
1056

1057
        auto sf
1058
            = cursor->to_string_fragment(this->dos_labels->dls_cell_allocator);
×
1059

1060
        al.al_attrs.emplace_back(line_range{0, -1},
×
1061
                                 DBA_COLUMN_NAME.value(hm.hm_name));
×
1062
        if (cursor->get_type() == lnav::cell_type::CT_TEXT
×
1063
            && (sf.startswith("[") || sf.startswith("{")))
×
1064
        {
1065
            auto parse_res = json_walk_collector::parse_fully(sf);
×
1066

1067
            if (parse_res.isOk()) {
×
1068
                auto jwc = parse_res.unwrap();
×
1069
                {
1070
                    yajlpp_gen gen;
×
1071

1072
                    {
1073
                        yajlpp_map root(gen);
×
1074

1075
                        root.gen("key");
×
1076
                        root.gen(hm.hm_name);
×
1077
                        root.gen("value");
×
1078
                        root.gen(sf);
×
1079
                    }
1080
                    al.al_attrs.emplace_back(
×
1081
                        line_range{0, -1},
×
1082
                        DBA_DETAILS.value(
×
1083
                            gen.to_string_fragment().to_string()));
×
1084
                }
1085
                value_out.emplace_back(al);
×
1086
                al.clear();
×
1087

1088
                stacked_bar_chart<std::string> chart;
×
1089
                int start_line = value_out.size();
×
1090

1091
                auto indent = 3 + max_name_width.value() - hm.hm_name.size();
×
1092
                chart.with_stacking_enabled(false)
×
1093
                    .with_margins(indent + 2, 0)
×
1094
                    .with_show_state(stacked_bar_chart_base::show_all{});
×
1095

1096
                for (const auto& [walk_index, jpw_value] :
×
1097
                     lnav::itertools::enumerate(jwc.jwc_values))
×
1098
                {
1099
                    {
1100
                        yajlpp_gen gen;
×
1101

1102
                        {
1103
                            yajlpp_map root(gen);
×
1104

1105
                            root.gen("key");
×
1106
                            root.gen(jpw_value.first);
×
1107
                            root.gen("value");
×
1108
                            root.gen(fmt::to_string(jpw_value.second));
×
1109
                        }
1110
                        al.al_attrs.emplace_back(
×
1111
                            line_range{0, -1},
×
1112
                            DBA_DETAILS.value(
×
1113
                                gen.to_string_fragment().to_string()));
×
1114
                    }
1115

1116
                    al.append(indent + 2, ' ')
×
1117
                        .append(lnav::roles::h5(jpw_value.first))
×
1118
                        .append(" = ")
×
1119
                        .append(fmt::to_string(jpw_value.second));
×
1120

1121
                    auto& sa = al.al_attrs;
×
1122
                    line_range lr(indent, indent + 1);
×
1123

1124
                    sa.emplace_back(
×
1125
                        lr,
1126
                        VC_GRAPHIC.value(walk_index < jwc.jwc_values.size() - 1
×
1127
                                             ? NCACS_LTEE
1128
                                             : NCACS_LLCORNER));
1129
                    lr.lr_start = indent + 2 + jpw_value.first.size() + 3;
×
1130
                    lr.lr_end = -1;
×
1131

1132
                    auto val_opt = to_double(jpw_value.second);
×
1133
                    if (val_opt) {
×
1134
                        auto attrs = vc.attrs_for_ident(jpw_value.first);
×
1135

1136
                        chart.add_value(jpw_value.first, val_opt.value());
×
1137
                        chart.with_attrs_for_ident(jpw_value.first, attrs);
×
1138
                        sa.emplace_back(lr, VC_ROLE.value(role_t::VCR_NUMBER));
×
1139
                    }
1140
                    value_out.emplace_back(al);
×
1141
                    al.clear();
×
1142
                }
1143

1144
                int curr_line = start_line;
×
1145
                for (auto iter = jwc.jwc_values.begin();
×
1146
                     iter != jwc.jwc_values.end();
×
1147
                     ++iter, curr_line++)
×
1148
                {
1149
                    auto val_opt = to_double(iter->second);
×
1150
                    if (!val_opt) {
×
1151
                        continue;
×
1152
                    }
1153

1154
                    auto& sa = value_out[curr_line].get_attrs();
×
1155
                    int left = indent + 2;
×
1156
                    chart.chart_attrs_for_value(
×
1157
                        lv, left, width, iter->first, val_opt.value(), sa);
×
1158
                }
1159
            } else {
×
1160
                yajlpp_gen gen;
×
1161

1162
                {
1163
                    yajlpp_map root(gen);
×
1164

1165
                    root.gen("key");
×
1166
                    root.gen(hm.hm_name);
×
1167
                    root.gen("value");
×
1168
                    root.gen(sf);
×
1169
                }
1170
                al.append(": ").append(sf);
×
1171
                al.al_attrs.emplace_back(
×
1172
                    line_range{0, -1},
×
1173
                    DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1174
            }
1175
        } else {
×
1176
            yajlpp_gen gen;
×
1177

1178
            {
1179
                yajlpp_map root(gen);
×
1180

1181
                root.gen("key");
×
1182
                root.gen(hm.hm_name);
×
1183
                root.gen("value");
×
1184
                switch (cursor->get_type()) {
×
1185
                    case lnav::cell_type::CT_NULL:
×
1186
                        root.gen();
×
1187
                        break;
×
1188
                    case lnav::cell_type::CT_INTEGER:
×
1189
                        root.gen(cursor->get_int());
×
1190
                        break;
×
1191
                    case lnav::cell_type::CT_FLOAT:
×
1192
                        if (cursor->get_sub_value() == 0) {
×
1193
                            root.gen(cursor->get_float());
×
1194
                        } else {
1195
                            root.gen(cursor->get_float_as_text());
×
1196
                        }
1197
                        break;
×
1198
                    case lnav::cell_type::CT_TEXT:
×
1199
                        root.gen(cursor->get_text());
×
1200
                        break;
×
1201
                }
1202
            }
1203

1204
            auto value_al = attr_line_t::from_table_cell_content(sf, 1000);
×
1205
            al.append(": ").append(value_al);
×
1206
            al.al_attrs.emplace_back(
×
1207
                line_range{0, -1},
×
1208
                DBA_DETAILS.value(gen.to_string_fragment().to_string()));
×
1209
        }
1210

1211
        if (!al.empty()) {
×
1212
            value_out.emplace_back(al);
×
1213
        }
1214
        cursor = cursor->next();
×
1215
    }
1216

1217
    this->dos_labels->dls_cell_allocator.reset();
×
1218
}
1219

1220
bool
1221
db_overlay_source::list_static_overlay(const listview_curses& lv,
319✔
1222
                                       media_t media,
1223
                                       int y,
1224
                                       int bottom,
1225
                                       attr_line_t& value_out)
1226
{
1227
    if (y != 0) {
319✔
1228
        return false;
99✔
1229
    }
1230

1231
    auto& line = value_out.get_string();
220✔
1232
    const auto* dls = this->dos_labels;
220✔
1233
    auto& sa = value_out.get_attrs();
220✔
1234

1235
    for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++) {
1,235✔
1236
        if (lpc == this->dos_labels->dls_row_style_column
1,015✔
1237
            && !this->dos_labels->dls_row_styles_have_errors)
3✔
1238
        {
1239
            continue;
3✔
1240
        }
1241

1242
        const auto& hm = dls->dls_headers[lpc];
1,013✔
1243
        if (hm.hm_hidden) {
1,013✔
1244
            continue;
1✔
1245
        }
1246
        auto actual_col_size
1247
            = std::min(dls->dls_max_column_width, hm.hm_column_size);
1,012✔
1248
        auto cell_title = hm.hm_name;
1,012✔
1249
        string_attrs_t cell_attrs;
1,012✔
1250
        scrub_ansi_string(cell_title, &cell_attrs);
1,012✔
1251
        truncate_to(cell_title, dls->dls_max_column_width);
1,012✔
1252

1253
        auto cell_length
1254
            = utf8_string_length(cell_title).unwrapOr(actual_col_size);
1,012✔
1255
        int total_fill = actual_col_size - cell_length;
1,012✔
1256
        auto line_len_before = line.length();
1,012✔
1257

1258
        int before = total_fill / 2;
1,012✔
1259
        total_fill -= before;
1,012✔
1260
        line.append(before, ' ');
1,012✔
1261
        shift_string_attrs(cell_attrs, 0, line.size());
1,012✔
1262
        line.append(cell_title);
1,012✔
1263
        line.append(total_fill, ' ');
1,012✔
1264
        auto header_range = line_range(line_len_before, line.length());
1,012✔
1265

1266
        line.append(1, ' ');
1,012✔
1267

1268
        require_ge(header_range.lr_start, 0);
1,012✔
1269

1270
        sa.emplace_back(header_range, VC_STYLE.value(hm.hm_title_attrs));
1,012✔
1271
        sa.insert(sa.end(), cell_attrs.begin(), cell_attrs.end());
1,012✔
1272
    }
1,012✔
1273

1274
    line_range lr(0);
220✔
1275

1276
    sa.emplace_back(
220✔
1277
        lr,
1278
        VC_STYLE.value(text_attrs::with_styles(text_attrs::style::bold,
440✔
1279
                                               text_attrs::style::underline)));
1280
    return true;
220✔
1281
}
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