• 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

39.66
/src/sql_commands.cc
1
/**
2
 * Copyright (c) 2021, 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 "base/auto_mem.hh"
31
#include "base/fs_util.hh"
32
#include "base/injector.bind.hh"
33
#include "base/itertools.hh"
34
#include "base/lnav_log.hh"
35
#include "base/opt_util.hh"
36
#include "bound_tags.hh"
37
#include "command_executor.hh"
38
#include "config.h"
39
#include "fmt/chrono.h"
40
#include "lnav.hh"
41
#include "readline_context.hh"
42
#include "shlex.hh"
43
#include "sql_help.hh"
44
#include "sqlite-extension-func.hh"
45
#include "sqlitepp.hh"
46
#include "view_helpers.hh"
47

48
static Result<std::string, lnav::console::user_message>
49
sql_cmd_dump(exec_context& ec,
1✔
50
             std::string cmdline,
51
             std::vector<std::string>& args)
52
{
53
    static auto& lnav_db = injector::get<auto_sqlite3&>();
1✔
54
    static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
1✔
55

56
    std::string retval;
1✔
57

58
    if (args.empty()) {
1✔
UNCOV
59
        args.emplace_back("filename");
×
UNCOV
60
        args.emplace_back("tables");
×
UNCOV
61
        return Ok(retval);
×
62
    }
63

64
    if (args.size() < 2) {
1✔
65
        return ec.make_error("expecting a file name to write to");
×
66
    }
67

68
    if (args.size() < 3) {
1✔
69
        return ec.make_error("expecting a table name to dump");
×
70
    }
71

72
    if (lnav_flags & LNF_SECURE_MODE) {
1✔
73
        return ec.make_error("{} -- unavailable in secure mode", args[0]);
×
74
    }
75

76
    auto_mem<FILE> file(fclose);
1✔
77

78
    if ((file = fopen(args[1].c_str(), "w+")) == nullptr) {
1✔
79
        return ec.make_error(
80
            "unable to open '{}' for writing: {}", args[1], strerror(errno));
×
81
    }
82

83
    for (size_t lpc = 2; lpc < args.size(); lpc++) {
2✔
84
        sqlite3_db_dump(lnav_db.in(),
1✔
85
                        "main",
86
                        args[lpc].c_str(),
1✔
87
                        (int (*)(const char*, void*)) fputs,
88
                        file.in());
1✔
89
    }
90

91
    retval = "generated";
1✔
92
    return Ok(retval);
1✔
93
}
1✔
94

95
static Result<std::string, lnav::console::user_message>
96
sql_cmd_read(exec_context& ec,
2✔
97
             std::string cmdline,
98
             std::vector<std::string>& args)
99
{
100
    static const intern_string_t SRC = intern_string::lookup("cmdline");
6✔
101
    static auto& lnav_db = injector::get<auto_sqlite3&>();
2✔
102
    static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
2✔
103

104
    std::string retval;
2✔
105

106
    if (args.empty()) {
2✔
UNCOV
107
        args.emplace_back("filename");
×
UNCOV
108
        return Ok(retval);
×
109
    }
110

111
    if (lnav_flags & LNF_SECURE_MODE) {
2✔
112
        return ec.make_error("{} -- unavailable in secure mode", args[0]);
×
113
    }
114

115
    shlex lexer(cmdline);
2✔
116

117
    auto split_args_res = lexer.split(ec.create_resolver());
2✔
118
    if (split_args_res.isErr()) {
2✔
119
        auto split_err = split_args_res.unwrapErr();
×
120
        auto um
121
            = lnav::console::user_message::error("unable to parse file name")
×
122
                  .with_reason(split_err.se_error.te_msg)
×
123
                  .with_snippet(lnav::console::snippet::from(
×
124
                      SRC, lexer.to_attr_line(split_err.se_error)))
×
125
                  .move();
×
126

127
        return Err(um);
×
128
    }
129

130
    auto split_args = split_args_res.unwrap()
4✔
131
        | lnav::itertools::map([](const auto& elem) { return elem.se_value; });
8✔
132

133
    for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
3✔
134
        auto read_res = lnav::filesystem::read_file(split_args[lpc]);
2✔
135

136
        if (read_res.isErr()) {
2✔
137
            return ec.make_error("unable to read script file: {} -- {}",
138
                                 split_args[lpc],
1✔
139
                                 read_res.unwrapErr());
2✔
140
        }
141

142
        auto script = read_res.unwrap();
1✔
143
        auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
1✔
144
        const char* start = script.c_str();
1✔
145

146
        do {
147
            const char* tail;
148
            auto rc = sqlite3_prepare_v2(
2✔
149
                lnav_db.in(), start, -1, stmt.out(), &tail);
150

151
            if (rc != SQLITE_OK) {
2✔
152
                const char* errmsg = sqlite3_errmsg(lnav_db.in());
×
153

154
                return ec.make_error("{}", errmsg);
×
155
            }
156

157
            if (stmt.in() != nullptr) {
2✔
158
                std::string alt_msg;
2✔
159
                auto exec_res = execute_sql(
160
                    ec, std::string(start, tail - start), alt_msg);
2✔
161
                if (exec_res.isErr()) {
2✔
162
                    return exec_res;
×
163
                }
164
            }
2✔
165

166
            start = tail;
2✔
167
        } while (start[0]);
2✔
168
    }
2✔
169

170
    return Ok(retval);
1✔
171
}
2✔
172

173
static Result<std::string, lnav::console::user_message>
174
sql_cmd_schema(exec_context& ec,
1✔
175
               std::string cmdline,
176
               std::vector<std::string>& args)
177
{
178
    std::string retval;
1✔
179

180
    if (args.empty()) {
1✔
UNCOV
181
        args.emplace_back("sql-table");
×
UNCOV
182
        return Ok(retval);
×
183
    }
184

185
    ensure_view(LNV_SCHEMA);
1✔
186
    if (args.size() == 2) {
1✔
187
        const auto id = text_anchors::to_anchor_string(args[1]);
×
188
        auto* tss = lnav_data.ld_views[LNV_SCHEMA].get_sub_source();
×
189
        if (auto* ta = dynamic_cast<text_anchors*>(tss); ta != nullptr) {
×
190
            ta->row_for_anchor(id) |
×
191
                [](auto row) { lnav_data.ld_views[LNV_SCHEMA].set_top(row); };
×
192
        }
193
    }
194

195
    return Ok(retval);
1✔
196
}
1✔
197

198
static Result<std::string, lnav::console::user_message>
199
sql_cmd_msgformats(exec_context& ec,
1✔
200
                   std::string cmdline,
201
                   std::vector<std::string>& args)
202
{
203
    static const std::string MSG_FORMAT_STMT = R"(
3✔
204
SELECT count(*) AS total,
205
       min(log_line) AS log_line,
206
       min(log_time) AS log_time,
207
       humanize_duration(timediff(max(log_time), min(log_time))) AS duration,
208
       group_concat(DISTINCT log_format) AS log_formats,
209
       log_msg_format
210
    FROM all_logs
211
    WHERE log_msg_format != ''
212
    GROUP BY log_msg_format
213
    HAVING total > 1
214
    ORDER BY total DESC, log_line ASC
215
)";
216

217
    std::string retval;
1✔
218

219
    if (args.empty()) {
1✔
UNCOV
220
        return Ok(retval);
×
221
    }
222

223
    std::string alt;
1✔
224

225
    return execute_sql(ec, MSG_FORMAT_STMT, alt);
1✔
226
}
1✔
227

228
static Result<std::string, lnav::console::user_message>
UNCOV
229
sql_cmd_generic(exec_context& ec,
×
230
                std::string cmdline,
231
                std::vector<std::string>& args)
232
{
UNCOV
233
    std::string retval;
×
234

UNCOV
235
    if (args.empty()) {
×
UNCOV
236
        args.emplace_back("*");
×
UNCOV
237
        return Ok(retval);
×
238
    }
239

240
    return Ok(retval);
×
241
}
242

243
static Result<std::string, lnav::console::user_message>
UNCOV
244
prql_cmd_from(exec_context& ec,
×
245
              std::string cmdline,
246
              std::vector<std::string>& args)
247
{
UNCOV
248
    std::string retval;
×
249

UNCOV
250
    if (args.empty()) {
×
UNCOV
251
        args.emplace_back("prql-table");
×
UNCOV
252
        return Ok(retval);
×
253
    }
254

255
    return Ok(retval);
×
256
}
257

258
static readline_context::prompt_result_t
259
prql_cmd_from_prompt(exec_context& ec, const std::string& cmdline)
×
260
{
261
    if (!endswith(cmdline, "from ")) {
×
262
        return {};
×
263
    }
264

265
    auto* tc = *lnav_data.ld_view_stack.top();
×
266
    auto* lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
×
UNCOV
267
    auto sel = tc->get_selection();
×
268

269
    if (!sel || lss == nullptr || lss->text_line_count() == 0) {
×
UNCOV
270
        return {};
×
271
    }
272

273
    auto line_pair = lss->find_line_with_file(lss->at(sel.value()));
×
274
    if (!line_pair) {
×
UNCOV
275
        return {};
×
276
    }
277

278
    auto format_name
UNCOV
279
        = line_pair->first->get_format_ptr()->get_name().to_string();
×
280
    return {
281
        "",
UNCOV
282
        lnav::prql::quote_ident(format_name) + " ",
×
283
    };
284
}
285

286
static Result<std::string, lnav::console::user_message>
UNCOV
287
prql_cmd_aggregate(exec_context& ec,
×
288
                   std::string cmdline,
289
                   std::vector<std::string>& args)
290
{
UNCOV
291
    std::string retval;
×
292

UNCOV
293
    if (args.empty()) {
×
UNCOV
294
        args.emplace_back("prql-expr");
×
UNCOV
295
        return Ok(retval);
×
296
    }
297

UNCOV
298
    return Ok(retval);
×
299
}
300

301
static Result<std::string, lnav::console::user_message>
UNCOV
302
prql_cmd_append(exec_context& ec,
×
303
                std::string cmdline,
304
                std::vector<std::string>& args)
305
{
UNCOV
306
    std::string retval;
×
307

UNCOV
308
    if (args.empty()) {
×
UNCOV
309
        args.emplace_back("prql-table");
×
UNCOV
310
        return Ok(retval);
×
311
    }
312

UNCOV
313
    return Ok(retval);
×
314
}
315

316
static Result<std::string, lnav::console::user_message>
UNCOV
317
prql_cmd_derive(exec_context& ec,
×
318
                std::string cmdline,
319
                std::vector<std::string>& args)
320
{
UNCOV
321
    std::string retval;
×
322

UNCOV
323
    if (args.empty()) {
×
UNCOV
324
        args.emplace_back("prql-expr");
×
UNCOV
325
        return Ok(retval);
×
326
    }
327

UNCOV
328
    return Ok(retval);
×
329
}
330

331
static Result<std::string, lnav::console::user_message>
UNCOV
332
prql_cmd_filter(exec_context& ec,
×
333
                std::string cmdline,
334
                std::vector<std::string>& args)
335
{
UNCOV
336
    std::string retval;
×
337

UNCOV
338
    if (args.empty()) {
×
UNCOV
339
        args.emplace_back("prql-expr");
×
UNCOV
340
        return Ok(retval);
×
341
    }
342

UNCOV
343
    return Ok(retval);
×
344
}
345

346
static Result<std::string, lnav::console::user_message>
UNCOV
347
prql_cmd_group(exec_context& ec,
×
348
               std::string cmdline,
349
               std::vector<std::string>& args)
350
{
UNCOV
351
    std::string retval;
×
352

UNCOV
353
    if (args.empty()) {
×
UNCOV
354
        args.emplace_back("prql-expr");
×
UNCOV
355
        args.emplace_back("prql-source");
×
UNCOV
356
        return Ok(retval);
×
357
    }
358

UNCOV
359
    return Ok(retval);
×
360
}
361

362
static Result<std::string, lnav::console::user_message>
UNCOV
363
prql_cmd_join(exec_context& ec,
×
364
              std::string cmdline,
365
              std::vector<std::string>& args)
366
{
UNCOV
367
    std::string retval;
×
368

UNCOV
369
    if (args.empty()) {
×
UNCOV
370
        args.emplace_back("prql-table");
×
UNCOV
371
        args.emplace_back("prql-expr");
×
UNCOV
372
        return Ok(retval);
×
373
    }
374

UNCOV
375
    return Ok(retval);
×
376
}
377

378
static Result<std::string, lnav::console::user_message>
UNCOV
379
prql_cmd_select(exec_context& ec,
×
380
                std::string cmdline,
381
                std::vector<std::string>& args)
382
{
UNCOV
383
    std::string retval;
×
384

UNCOV
385
    if (args.empty()) {
×
UNCOV
386
        args.emplace_back("prql-expr");
×
UNCOV
387
        return Ok(retval);
×
388
    }
389

UNCOV
390
    return Ok(retval);
×
391
}
392

393
static Result<std::string, lnav::console::user_message>
UNCOV
394
prql_cmd_sort(exec_context& ec,
×
395
              std::string cmdline,
396
              std::vector<std::string>& args)
397
{
UNCOV
398
    std::string retval;
×
399

UNCOV
400
    if (args.empty()) {
×
UNCOV
401
        args.emplace_back("prql-expr");
×
UNCOV
402
        return Ok(retval);
×
403
    }
404

UNCOV
405
    return Ok(retval);
×
406
}
407

408
static Result<std::string, lnav::console::user_message>
UNCOV
409
prql_cmd_take(exec_context& ec,
×
410
              std::string cmdline,
411
              std::vector<std::string>& args)
412
{
UNCOV
413
    std::string retval;
×
414

UNCOV
415
    if (args.empty()) {
×
UNCOV
416
        return Ok(retval);
×
417
    }
418

UNCOV
419
    return Ok(retval);
×
420
}
421

422
static readline_context::command_t sql_commands[] = {
423
    {
424
        ".dump",
425
        sql_cmd_dump,
426
        help_text(".dump", "Dump the contents of the database")
427
            .sql_command()
428
            .with_parameter({"path", "The path to the file to write"})
429
            .with_parameter(help_text{"table", "The name of the table to dump"}
430
                                .one_or_more())
431
            .with_tags({
432
                "io",
433
            }),
434
    },
435
    {
436
        ".msgformats",
437
        sql_cmd_msgformats,
438
        help_text(".msgformats",
439
                  "Executes a query that will summarize the different message "
440
                  "formats found in the logs")
441
            .sql_command(),
442
    },
443
    {
444
        ".read",
445
        sql_cmd_read,
446
        help_text(".read", "Execute the SQLite statements in the given file")
447
            .sql_command()
448
            .with_parameter({"path", "The path to the file to write"})
449
            .with_tags({
450
                "io",
451
            }),
452
    },
453
    {
454
        ".schema",
455
        sql_cmd_schema,
456
        help_text(".schema",
457
                  "Switch to the SCHEMA view that contains a dump of the "
458
                  "current database schema")
459
            .sql_command()
460
            .with_parameter({"name", "The name of a table to jump to"}),
461
    },
462
    {
463
        "ATTACH",
464
        sql_cmd_generic,
465
    },
466
    {
467
        "CREATE",
468
        sql_cmd_generic,
469
    },
470
    {
471
        "DELETE",
472
        sql_cmd_generic,
473
    },
474
    {
475
        "DETACH",
476
        sql_cmd_generic,
477
    },
478
    {
479
        "DROP",
480
        sql_cmd_generic,
481
    },
482
    {
483
        "INSERT",
484
        sql_cmd_generic,
485
    },
486
    {
487
        "SELECT",
488
        sql_cmd_generic,
489
    },
490
    {
491
        "UPDATE",
492
        sql_cmd_generic,
493
    },
494
    {
495
        "WITH",
496
        sql_cmd_generic,
497
    },
498
    {
499
        "from",
500
        prql_cmd_from,
501
        help_text("from")
502
            .prql_transform()
503
            .with_tags({"prql"})
504
            .with_summary("PRQL command to specify a data source")
505
            .with_parameter({"table", "The table to use as a source"})
506
            .with_example({
507
                "To pull data from the 'http_status_codes' database table",
508
                "from http_status_codes | take 3",
509
                help_example::language::prql,
510
            })
511
            .with_example({
512
                "To use an array literal as a source",
513
                "from [{ col1=1, col2='abc' }, { col1=2, col2='def' }]",
514
                help_example::language::prql,
515
            }),
516
        prql_cmd_from_prompt,
517
        "prql-source",
518
    },
519
    {
520
        "aggregate",
521
        prql_cmd_aggregate,
522
        help_text("aggregate")
523
            .prql_transform()
524
            .with_tags({"prql"})
525
            .with_summary("PRQL transform to summarize many rows into one")
526
            .with_parameter(
527
                help_text{"expr", "The aggregate expression(s)"}.with_grouping(
528
                    "{", "}"))
529
            .with_example({
530
                "To group values into a JSON array",
531
                "from [{a=1}, {a=2}] | aggregate { arr = json.group_array a }",
532
                help_example::language::prql,
533
            }),
534
        nullptr,
535
        "prql-source",
536
        {"prql-source"},
537
    },
538
    {
539
        "append",
540
        prql_cmd_append,
541
        help_text("append")
542
            .prql_transform()
543
            .with_tags({"prql"})
544
            .with_summary("PRQL transform to concatenate tables together")
545
            .with_parameter({"table", "The table to use as a source"}),
546
        nullptr,
547
        "prql-source",
548
        {"prql-source"},
549
    },
550
    {
551
        "derive",
552
        prql_cmd_derive,
553
        help_text("derive")
554
            .prql_transform()
555
            .with_tags({"prql"})
556
            .with_summary("PRQL transform to derive one or more columns")
557
            .with_parameter(
558
                help_text{"column", "The new column"}.with_grouping("{", "}"))
559
            .with_example({
560
                "To add a column that is a multiplication of another",
561
                "from [{a=1}, {a=2}] | derive b = a * 2",
562
                help_example::language::prql,
563
            }),
564
        nullptr,
565
        "prql-source",
566
        {"prql-source"},
567
    },
568
    {
569
        "filter",
570
        prql_cmd_filter,
571
        help_text("filter")
572
            .prql_transform()
573
            .with_tags({"prql"})
574
            .with_summary("PRQL transform to pick rows based on their values")
575
            .with_parameter(
576
                {"expr", "The expression to evaluate over each row"})
577
            .with_example({
578
                "To pick rows where 'a' is greater than one",
579
                "from [{a=1}, {a=2}] | filter a > 1",
580
                help_example::language::prql,
581
            }),
582
        nullptr,
583
        "prql-source",
584
        {"prql-source"},
585
    },
586
    {
587
        "group",
588
        prql_cmd_group,
589
        help_text("group")
590
            .prql_transform()
591
            .with_tags({"prql"})
592
            .with_summary("PRQL transform to partition rows into groups")
593
            .with_parameter(
594
                help_text{"key_columns", "The columns that define the group"}
595
                    .with_grouping("{", "}"))
596
            .with_parameter(
597
                help_text{"pipeline", "The pipeline to execute over a group"}
598
                    .with_grouping("(", ")"))
599
            .with_example({
600
                "To group by log_level and count the rows in each partition",
601
                "from lnav_example_log | group { log_level } (aggregate { "
602
                "count this })",
603
                help_example::language::prql,
604
            }),
605
        nullptr,
606
        "prql-source",
607
        {"prql-source"},
608
    },
609
    {
610
        "join",
611
        prql_cmd_join,
612
        help_text("join")
613
            .prql_transform()
614
            .with_tags({"prql"})
615
            .with_summary("PRQL transform to add columns from another table")
616
            .with_parameter(help_text{"side", "Specifies which rows to include"}
617
                                .with_enum_values({
618
                                    "inner"_frag,
619
                                    "left"_frag,
620
                                    "right"_frag,
621
                                    "full"_frag,
622
                                })
623
                                .with_default_value("inner")
624
                                .optional())
625
            .with_parameter(
626
                {"table", "The other table to join with the current rows"})
627
            .with_parameter(
628
                help_text{"condition", "The condition used to join rows"}
629
                    .with_grouping("(", ")")),
630
        nullptr,
631
        "prql-source",
632
        {"prql-source"},
633
    },
634
    {
635
        "select",
636
        prql_cmd_select,
637
        help_text("select")
638
            .prql_transform()
639
            .with_tags({"prql"})
640
            .with_summary("PRQL transform to pick and compute columns")
641
            .with_parameter(
642
                help_text{"expr", "The columns to include in the result set"}
643
                    .with_grouping("{", "}"))
644
            .with_example({
645
                "To pick the 'b' column from the rows",
646
                "from [{a=1, b='abc'}, {a=2, b='def'}] | select b",
647
                help_example::language::prql,
648
            })
649
            .with_example({
650
                "To compute a new column from an input",
651
                "from [{a=1}, {a=2}] | select b = a * 2",
652
                help_example::language::prql,
653
            }),
654
        nullptr,
655
        "prql-source",
656
        {"prql-source"},
657
    },
658
    {
659
        "stats.average_of",
660
        prql_cmd_sort,
661
        help_text("stats.average_of", "Compute the average of col")
662
            .prql_function()
663
            .with_tags({"prql"})
664
            .with_parameter(help_text{"col", "The column to average"})
665
            .with_example({
666
                "To get the average of a",
667
                "from [{a=1}, {a=1}, {a=2}] | stats.average_of a",
668
                help_example::language::prql,
669
            }),
670
        nullptr,
671
        "prql-source",
672
        {"prql-source"},
673
    },
674
    {
675
        "stats.count_by",
676
        prql_cmd_sort,
677
        help_text(
678
            "stats.count_by",
679
            "Partition rows and count the number of rows in each partition")
680
            .prql_function()
681
            .with_tags({"prql"})
682
            .with_parameter(help_text{"column", "The columns to group by"}
683
                                .one_or_more()
684
                                .with_grouping("{", "}"))
685
            .with_example({
686
                "To count rows for a particular value of column 'a'",
687
                "from [{a=1}, {a=1}, {a=2}] | stats.count_by a",
688
                help_example::language::prql,
689
            }),
690
        nullptr,
691
        "prql-source",
692
        {"prql-source"},
693
    },
694
    {
695
        "stats.hist",
696
        prql_cmd_sort,
697
        help_text("stats.hist", "Count the top values per bucket of time")
698
            .prql_function()
699
            .with_tags({"prql"})
700
            .with_parameter(help_text{"col", "The column to count"})
701
            .with_parameter(help_text{"slice", "The time slice"}
702
                                .optional()
703
                                .with_default_value("'1h'"))
704
            .with_parameter(
705
                help_text{"top", "The limit on the number of values to report"}
706
                    .optional()
707
                    .with_default_value("10"))
708
            .with_example({
709
                "To chart the values of ex_procname over time",
710
                "from lnav_example_log | stats.hist ex_procname",
711
                help_example::language::prql,
712
            }),
713
        nullptr,
714
        "prql-source",
715
        {"prql-source"},
716
    },
717
    {
718
        "stats.sum_of",
719
        prql_cmd_sort,
720
        help_text("stats.sum_of", "Compute the sum of col")
721
            .prql_function()
722
            .with_tags({"prql"})
723
            .with_parameter(help_text{"col", "The column to sum"})
724
            .with_example({
725
                "To get the sum of a",
726
                "from [{a=1}, {a=1}, {a=2}] | stats.sum_of a",
727
                help_example::language::prql,
728
            }),
729
        nullptr,
730
        "prql-source",
731
        {"prql-source"},
732
    },
733
    {
734
        "stats.by",
735
        prql_cmd_sort,
736
        help_text("stats.by", "A shorthand for grouping and aggregating")
737
            .prql_function()
738
            .with_tags({"prql"})
739
            .with_parameter(help_text{"col", "The column to sum"})
740
            .with_parameter(help_text{"values", "The aggregations to perform"})
741
            .with_example({
742
                "To partition by a and get the sum of b",
743
                "from [{a=1, b=1}, {a=1, b=1}, {a=2, b=1}] | stats.by a "
744
                "{sum b}",
745
                help_example::language::prql,
746
            }),
747
        nullptr,
748
        "prql-source",
749
        {"prql-source"},
750
    },
751
    {
752
        "sort",
753
        prql_cmd_sort,
754
        help_text("sort")
755
            .prql_transform()
756
            .with_tags({"prql"})
757
            .with_summary("PRQL transform to sort rows")
758
            .with_parameter(help_text{
759
                "expr", "The values to use when ordering the result set"}
760
                                .with_grouping("{", "}"))
761
            .with_example({
762
                "To sort the rows in descending order",
763
                "from [{a=1}, {a=2}] | sort {-a}",
764
                help_example::language::prql,
765
            }),
766
        nullptr,
767
        "prql-source",
768
        {"prql-source"},
769
    },
770
    {
771
        "take",
772
        prql_cmd_take,
773
        help_text("take")
774
            .prql_transform()
775
            .with_tags({"prql"})
776
            .with_summary("PRQL command to pick rows based on their position")
777
            .with_parameter({"n_or_range", "The number of rows or range"})
778
            .with_example({
779
                "To pick the first row",
780
                "from [{a=1}, {a=2}, {a=3}] | take 1",
781
                help_example::language::prql,
782
            })
783
            .with_example({
784
                "To pick the second and third rows",
785
                "from [{a=1}, {a=2}, {a=3}] | take 2..3",
786
                help_example::language::prql,
787
            }),
788
        nullptr,
789
        "prql-source",
790
        {"prql-source"},
791
    },
792
    {
793
        "utils.distinct",
794
        prql_cmd_sort,
795
        help_text("utils.distinct",
796
                  "A shorthand for getting distinct values of col")
797
            .prql_function()
798
            .with_tags({"prql"})
799
            .with_parameter(help_text{"col", "The column to sum"})
800
            .with_example({
801
                "To get the distinct values of a",
802
                "from [{a=1}, {a=1}, {a=2}] | utils.distinct a",
803
                help_example::language::prql,
804
            }),
805
        nullptr,
806
        "prql-source",
807
        {"prql-source"},
808
    },
809
};
810

811
static readline_context::command_map_t sql_cmd_map;
812

813
static auto bound_sql_cmd_map
814
    = injector::bind<readline_context::command_map_t,
815
                     sql_cmd_map_tag>::to_instance(+[]() {
1,091✔
816
          for (auto& cmd : sql_commands) {
32,730✔
817
              sql_cmd_map[cmd.c_name] = &cmd;
63,278✔
818
              if (cmd.c_help.ht_name) {
31,639✔
819
                  cmd.c_help.index_tags();
21,820✔
820
              }
821
          }
822

823
          return &sql_cmd_map;
1,091✔
824
      });
825

826
namespace injector {
827
template<>
828
void
829
force_linking(sql_cmd_map_tag anno)
17✔
830
{
831
}
17✔
832
}  // namespace injector
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