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

tstack / lnav / 11918362462-1766

19 Nov 2024 05:22PM UTC coverage: 70.222% (-0.01%) from 70.232%
11918362462-1766

push

github

tstack
[tests] update expected output

46302 of 65937 relevant lines covered (70.22%)

467211.82 hits per line

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

77.25
/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,
7✔
50
             std::string cmdline,
51
             std::vector<std::string>& args)
52
{
53
    static auto& lnav_db = injector::get<auto_sqlite3&>();
7✔
54
    static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
7✔
55

56
    std::string retval;
7✔
57

58
    if (args.empty()) {
7✔
59
        args.emplace_back("filename");
6✔
60
        args.emplace_back("tables");
6✔
61
        return Ok(retval);
12✔
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);
2✔
93
}
7✔
94

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

104
    std::string retval;
8✔
105

106
    if (args.empty()) {
8✔
107
        args.emplace_back("filename");
6✔
108
        return Ok(retval);
12✔
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.te_msg)
×
123
                  .with_snippet(lnav::console::snippet::from(
×
124
                      SRC, lexer.to_attr_line(split_err)))
×
125
                  .move();
×
126

127
        return Err(um);
×
128
    }
129

130
    auto split_args = split_args_res.unwrap()
2✔
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);
4✔
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);
2✔
171
}
8✔
172

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

180
    if (args.empty()) {
7✔
181
        args.emplace_back("sql-table");
6✔
182
        return Ok(retval);
12✔
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);
2✔
196
}
7✔
197

198
static Result<std::string, lnav::console::user_message>
199
sql_cmd_msgformats(exec_context& ec,
7✔
200
                   std::string cmdline,
201
                   std::vector<std::string>& args)
202
{
203
    static const std::string MSG_FORMAT_STMT = R"(
7✔
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;
7✔
218

219
    if (args.empty()) {
7✔
220
        return Ok(retval);
12✔
221
    }
222

223
    std::string alt;
1✔
224

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

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

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

240
    return Ok(retval);
×
241
}
54✔
242

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

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

255
    return Ok(retval);
×
256
}
6✔
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());
×
267

268
    if (lss == nullptr || lss->text_line_count() == 0) {
×
269
        return {};
×
270
    }
271

272
    auto line_pair = lss->find_line_with_file(lss->at(tc->get_selection()));
×
273
    if (!line_pair) {
×
274
        return {};
×
275
    }
276

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

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

292
    if (args.empty()) {
6✔
293
        args.emplace_back("prql-expr");
6✔
294
        return Ok(retval);
12✔
295
    }
296

297
    return Ok(retval);
×
298
}
6✔
299

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

307
    if (args.empty()) {
6✔
308
        args.emplace_back("prql-table");
6✔
309
        return Ok(retval);
12✔
310
    }
311

312
    return Ok(retval);
×
313
}
6✔
314

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

322
    if (args.empty()) {
6✔
323
        args.emplace_back("prql-expr");
6✔
324
        return Ok(retval);
12✔
325
    }
326

327
    return Ok(retval);
×
328
}
6✔
329

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

337
    if (args.empty()) {
6✔
338
        args.emplace_back("prql-expr");
6✔
339
        return Ok(retval);
12✔
340
    }
341

342
    return Ok(retval);
×
343
}
6✔
344

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

352
    if (args.empty()) {
6✔
353
        args.emplace_back("prql-expr");
6✔
354
        args.emplace_back("prql-source");
6✔
355
        return Ok(retval);
12✔
356
    }
357

358
    return Ok(retval);
×
359
}
6✔
360

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

368
    if (args.empty()) {
6✔
369
        args.emplace_back("prql-table");
6✔
370
        args.emplace_back("prql-expr");
6✔
371
        return Ok(retval);
12✔
372
    }
373

374
    return Ok(retval);
×
375
}
6✔
376

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

384
    if (args.empty()) {
6✔
385
        args.emplace_back("prql-expr");
6✔
386
        return Ok(retval);
12✔
387
    }
388

389
    return Ok(retval);
×
390
}
6✔
391

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

399
    if (args.empty()) {
42✔
400
        args.emplace_back("prql-expr");
42✔
401
        return Ok(retval);
84✔
402
    }
403

404
    return Ok(retval);
×
405
}
42✔
406

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

414
    if (args.empty()) {
6✔
415
        return Ok(retval);
12✔
416
    }
417

418
    return Ok(retval);
×
419
}
6✔
420

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

806
static readline_context::command_map_t sql_cmd_map;
807

808
static auto bound_sql_cmd_map
809
    = injector::bind<readline_context::command_map_t,
810
                     sql_cmd_map_tag>::to_instance(+[]() {
1,020✔
811
          for (auto& cmd : sql_commands) {
30,600✔
812
              sql_cmd_map[cmd.c_name] = &cmd;
29,580✔
813
              if (cmd.c_help.ht_name) {
29,580✔
814
                  cmd.c_help.index_tags();
20,400✔
815
              }
816
          }
817

818
          return &sql_cmd_map;
1,020✔
819
      });
820

821
namespace injector {
822
template<>
823
void
824
force_linking(sql_cmd_map_tag anno)
23✔
825
{
826
}
23✔
827
}  // 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

© 2025 Coveralls, Inc