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

tstack / lnav / 18577582398-2579

16 Oct 2025 11:24PM UTC coverage: 69.212% (-0.9%) from 70.063%
18577582398-2579

push

github

tstack
[build] fix type

1 of 1 new or added line in 1 file covered. (100.0%)

2573 existing lines in 65 files now uncovered.

50113 of 72405 relevant lines covered (69.21%)

415651.95 hits per line

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

99.89
/src/sqlite-extension-func.cc
1
/**
2
 * Copyright (c) 2013, 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
 * @file sqlite-extension-func.c
30
 */
31

32
#include <map>
33
#include <string>
34

35
#include "sqlite-extension-func.hh"
36

37
#include "base/auto_mem.hh"
38
#include "base/itertools.hh"
39
#include "base/lnav_log.hh"
40
#include "base/string_util.hh"
41
#include "config.h"
42
#include "help_text.hh"
43
#include "sql_help.hh"
44

45
#ifdef HAVE_RUST_DEPS
46
#    include "lnav_rs_ext.cxx.hh"
47
#endif
48

49
extern "C"
50
{
51
struct sqlite3_api_routines;
52

53
int sqlite3_series_init(sqlite3* db,
54
                        char** pzErrMsg,
55
                        const sqlite3_api_routines* pApi);
56
}
57

58
#ifdef HAVE_RUST_DEPS
59
rust::Vec<lnav_rs_ext::SourceTreeElement> sqlite_extension_prql;
60
#endif
61

62
namespace lnav::sql {
63
std::multimap<std::string, const help_text*> prql_functions;
64

65
}
66

67
sqlite_registration_func_t sqlite_registration_funcs[] = {
68
    common_extension_functions,
69
    state_extension_functions,
70
    string_extension_functions,
71
    network_extension_functions,
72
    fs_extension_functions,
73
    json_extension_functions,
74
    yaml_extension_functions,
75
    time_extension_functions,
76

77
    nullptr,
78
};
79

80
struct prql_hier {
81
    std::map<std::string, prql_hier> ph_modules;
82
    std::map<std::string, std::string> ph_declarations;
83

84
    void to_string(std::string& accum) const
12,816✔
85
    {
86
        for (const auto& mod_pair : this->ph_modules) {
13,884✔
87
            accum.append("module ");
1,068✔
88
            accum.append(mod_pair.first);
1,068✔
89
            accum.append(" {\n");
1,068✔
90
            mod_pair.second.to_string(accum);
1,068✔
91
            accum.append("}\n");
1,068✔
92
        }
93
        for (const auto& decl_pair : this->ph_declarations) {
51,264✔
94
            accum.append(decl_pair.second);
38,448✔
95
            accum.append("\n");
38,448✔
96
        }
97
    }
12,816✔
98
};
99

100
static void
101
register_help(prql_hier& phier, const help_text& ht)
37,380✔
102
{
103
    auto prql_fqid
104
        = fmt::format(FMT_STRING("{}"), fmt::join(ht.ht_prql_path, "."));
112,140✔
105
    lnav::sql::prql_functions.emplace(prql_fqid, &ht);
37,380✔
106

107
    auto* curr_hier = &phier;
37,380✔
108
    for (size_t name_index = 0; name_index < ht.ht_prql_path.size();
116,412✔
109
         name_index++)
110
    {
111
        const auto& prql_name = ht.ht_prql_path[name_index];
79,032✔
112
        if (name_index == ht.ht_prql_path.size() - 1) {
79,032✔
113
            auto param_names
114
                = ht.ht_parameters | lnav::itertools::map([](const auto& elem) {
37,380✔
115
                      if (elem.ht_nargs == help_nargs_t::HN_OPTIONAL
48,060✔
116
                          || elem.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE)
43,788✔
117
                      {
118
                          return fmt::format(FMT_STRING("{}:null"),
8,544✔
119
                                             elem.ht_name);
8,544✔
120
                      }
121
                      return fmt::format(FMT_STRING("p_{}"), elem.ht_name);
175,152✔
122
                  });
37,380✔
123
            auto func_args
124
                = ht.ht_parameters | lnav::itertools::map([](const auto& elem) {
74,760✔
125
                      if (elem.ht_nargs == help_nargs_t::HN_OPTIONAL
48,060✔
126
                          || elem.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE)
43,788✔
127
                      {
128
                          return fmt::format(FMT_STRING("{{{}:0}}"),
8,544✔
129
                                             elem.ht_name);
8,544✔
130
                      }
131
                      return fmt::format(FMT_STRING("{{p_{}:0}}"),
87,576✔
132
                                         elem.ht_name);
87,576✔
133
                  });
37,380✔
134
            curr_hier->ph_declarations[prql_name]
74,760✔
135
                = fmt::format(FMT_STRING("let {} = func {} -> s\"{}({})\""),
186,900✔
136
                              prql_name,
137
                              fmt::join(param_names, " "),
37,380✔
138
                              ht.ht_name,
37,380✔
139
                              fmt::join(func_args, ", "));
74,760✔
140
        } else {
37,380✔
141
            curr_hier = &curr_hier->ph_modules[prql_name];
124,956✔
142
        }
143
    }
144
}
37,380✔
145

146
static void
147
insert_sql_help(help_text& root, const help_text& curr)
88,644✔
148
{
149
    if (curr.ht_flag_name) {
88,644✔
150
        sqlite_function_help.insert(
45,924✔
151
            std::make_pair(toupper(curr.ht_flag_name), &root));
91,848✔
152
    } else if (isupper(curr.ht_name[0])) {
42,720✔
153
        sqlite_function_help.insert(
34,176✔
154
            std::make_pair(toupper(curr.ht_name), &root));
68,352✔
155
    }
156
    for (const auto& param : curr.ht_parameters) {
177,288✔
157
        if (param.ht_flag_name) {
88,644✔
158
            insert_sql_help(root, param);
43,788✔
159
        }
160
        if (param.ht_format == help_parameter_format_t::HPF_NONE
88,644✔
161
            && param.ht_nargs == help_nargs_t::HN_OPTIONAL)
14,952✔
162
        {
163
            insert_sql_help(root, param);
14,952✔
164
        }
165
        for (const auto& eval : param.ht_enum_values) {
99,324✔
166
            sqlite_function_help.emplace(eval.to_string(), &root);
10,680✔
167
        }
168
    }
169
}
88,644✔
170

171
int
172
register_sqlite_funcs(sqlite3* db, sqlite_registration_func_t* reg_funcs)
1,672✔
173
{
174
    static bool help_registration_done = false;
175
    prql_hier phier;
1,672✔
176
    int lpc;
177

178
    phier.ph_modules["sqlite"].ph_declarations["strftime"]
6,688✔
179
        = "let strftime = func p_format p_timestring -> "
180
          "s\"strftime({p_format:0}, {p_timestring:0})\"";
1,672✔
181

182
    require(db != nullptr);
1,672✔
183
    require(reg_funcs != nullptr);
1,672✔
184

185
    {
186
        auto_mem<char> errmsg(sqlite3_free);
1,672✔
187

188
        sqlite3_series_init(db, errmsg.out(), nullptr);
1,672✔
189
    }
1,672✔
190

191
    for (lpc = 0; reg_funcs[lpc]; lpc++) {
15,048✔
192
        struct FuncDef* basic_funcs = nullptr;
13,376✔
193
        struct FuncDefAgg* agg_funcs = nullptr;
13,376✔
194
        int i;
195

196
        reg_funcs[lpc](&basic_funcs, &agg_funcs);
13,376✔
197

198
        for (i = 0; basic_funcs && basic_funcs[i].zName; i++) {
152,152✔
199
            struct FuncDef& fd = basic_funcs[i];
138,776✔
200

201
            // sqlite3CreateFunc
202
            /* LMH no error checking */
203
            sqlite3_create_function(db,
138,776✔
204
                                    basic_funcs[i].zName,
138,776✔
205
                                    basic_funcs[i].nArg,
138,776✔
206
                                    basic_funcs[i].eTextRep,
138,776✔
207
                                    (void*) &fd,
208
                                    basic_funcs[i].xFunc,
138,776✔
209
                                    nullptr,
210
                                    nullptr);
211

212
            if (!help_registration_done
138,776✔
213
                && fd.fd_help.ht_context != help_context_t::HC_NONE)
88,644✔
214
            {
215
                auto& ht = fd.fd_help;
76,896✔
216

217
                sqlite_function_help.insert(std::make_pair(ht.ht_name, &ht));
76,896✔
218
                ht.index_tags();
76,896✔
219
                if (!ht.ht_prql_path.empty()) {
76,896✔
220
                    register_help(phier, ht);
33,108✔
221
                }
222
            }
223
        }
224

225
        for (i = 0; agg_funcs && agg_funcs[i].zName; i++) {
35,112✔
226
            struct FuncDefAgg& fda = agg_funcs[i];
21,736✔
227

228
            // sqlite3CreateFunc
229
            sqlite3_create_function(db,
21,736✔
230
                                    agg_funcs[i].zName,
21,736✔
231
                                    agg_funcs[i].nArg,
21,736✔
232
                                    agg_funcs[i].eTextRep,
21,736✔
233
                                    (void*) &agg_funcs[i],
21,736✔
234
                                    nullptr,
235
                                    agg_funcs[i].xStep,
21,736✔
236
                                    agg_funcs[i].xFinalize);
21,736✔
237

238
            if (!help_registration_done
21,736✔
239
                && fda.fda_help.ht_context != help_context_t::HC_NONE)
13,884✔
240
            {
241
                auto& ht = fda.fda_help;
5,340✔
242

243
                sqlite_function_help.insert(std::make_pair(ht.ht_name, &ht));
5,340✔
244
                ht.index_tags();
5,340✔
245
                if (!ht.ht_prql_path.empty()) {
5,340✔
246
                    register_help(phier, ht);
4,272✔
247
                }
248
            }
249
        }
250
    }
251

252
    static help_text builtin_funcs[] = {
253
        help_text("abs", "Return the absolute value of the argument")
1,068✔
254
            .sql_function()
1,068✔
255
            .with_parameter({"x", "The number to convert"})
2,136✔
256
            .with_tags({"math"})
1,068✔
257
            .with_example(
1,068✔
258
                {"To get the absolute value of -1", "SELECT abs(-1)"}),
259

260
        help_text("changes",
1,068✔
261
                  "The number of database rows that were changed, inserted, or "
262
                  "deleted by the most recent statement.")
263
            .sql_function(),
1,068✔
264

265
        help_text("char",
1,068✔
266
                  "Returns a string composed of characters having the given "
267
                  "unicode code point values")
268
            .sql_function()
1,068✔
269
            .with_parameter(
1,068✔
270
                help_text("X", "The unicode code point values").zero_or_more())
2,136✔
271
            .with_tags({"string"})
1,068✔
272
            .with_example({"To get a string with the code points 0x48 and 0x49",
1,068✔
273
                           "SELECT char(0x48, 0x49)"}),
274

275
        help_text("coalesce",
1,068✔
276
                  "Returns a copy of its first non-NULL argument, or NULL if "
277
                  "all arguments are NULL")
278
            .sql_function()
1,068✔
279
            .with_parameter({"X", "A value to check for NULL-ness"})
2,136✔
280
            .with_parameter(
1,068✔
281
                help_text("Y", "A value to check for NULL-ness").one_or_more())
2,136✔
282
            .with_example(
1,068✔
283
                {"To get the first non-null value from three parameters",
284
                 "SELECT coalesce(null, 0, null)"}),
285

286
        help_text("concat",
1,068✔
287
                  "Returns a string that is the concatenation of all non-NULL "
288
                  "arguments")
289
            .sql_function()
1,068✔
290
            .with_parameter(help_text("X", "The values to concatenate together")
3,204✔
291
                                .one_or_more())
1,068✔
292
            .with_tags({"string"})
1,068✔
293
            .with_example({"To concatenate a label and number",
1,068✔
294
                           "SELECT concat('Size: ', 1234)"}),
295

296
        help_text("concat_ws",
1,068✔
297
                  "Returns a string that is the concatenation of all non-NULL "
298
                  "arguments separated by the first argument")
299
            .sql_function()
1,068✔
300
            .with_parameter(help_text("sep", "The separator"))
2,136✔
301
            .with_parameter(help_text("X", "The values to concatenate together")
3,204✔
302
                                .one_or_more())
1,068✔
303
            .with_tags({"string"})
1,068✔
304
            .with_example({"To separate numbers with a comma",
1,068✔
305
                           "SELECT concat_ws(',', 1, 2, 3, 4)"}),
306

307
        help_text("glob", "Match a string against Unix glob pattern")
1,068✔
308
            .sql_function()
1,068✔
309
            .with_parameter({"pattern", "The glob pattern"})
2,136✔
310
            .with_parameter({"str", "The string to match"})
2,136✔
311
            .with_example({"To test if the string 'abc' matches the glob 'a*'",
1,068✔
312
                           "SELECT glob('a*', 'abc')"}),
313

314
        help_text("hex",
1,068✔
315
                  "Returns a string which is the upper-case hexadecimal "
316
                  "rendering of the content of its argument.")
317
            .sql_function()
1,068✔
318
            .with_parameter({"X", "The blob to convert to hexadecimal"})
2,136✔
319
            .with_example(
1,068✔
320
                {"To get the hexadecimal rendering of the string 'abc'",
321
                 "SELECT hex('abc')"}),
322

323
        help_text("ifnull",
1,068✔
324
                  "Returns a copy of its first non-NULL argument, or NULL if "
325
                  "both arguments are NULL")
326
            .sql_function()
1,068✔
327
            .with_parameter({"X", "A value to check for NULL-ness"})
2,136✔
328
            .with_parameter({"Y", "A value to check for NULL-ness"})
2,136✔
329
            .with_example(
1,068✔
330
                {"To get the first non-null value between null and zero",
331
                 "SELECT ifnull(null, 0)"}),
332

333
        help_text("instr",
1,068✔
334
                  "Finds the first occurrence of the needle within the "
335
                  "haystack and returns the number of prior characters plus 1, "
336
                  "or 0 if the needle was not found")
337
            .sql_function()
1,068✔
338
            .with_parameter({"haystack", "The string to search within"})
2,136✔
339
            .with_parameter(
2,136✔
340
                {"needle", "The string to look for in the haystack"})
341
            .with_tags({"string"})
1,068✔
342
            .with_example(
1,068✔
343
                {"To test get the position of 'b' in the string 'abc'",
344
                 "SELECT instr('abc', 'b')"}),
345

346
        help_text("last_insert_rowid",
1,068✔
347
                  "Returns the ROWID of the last row insert from the database "
348
                  "connection which invoked the function")
349
            .sql_function(),
1,068✔
350

351
        help_text("length",
1,068✔
352
                  "Returns the number of characters (not bytes) in the given "
353
                  "string prior to the first NUL character")
354
            .sql_function()
1,068✔
355
            .with_parameter({"str", "The string to determine the length of"})
2,136✔
356
            .with_tags({"string"})
1,068✔
357
            .with_example({"To get the length of the string 'abc'",
1,068✔
358
                           "SELECT length('abc')"}),
359

360
        help_text("like", "Match a string against a pattern")
1,068✔
361
            .sql_function()
1,068✔
362
            .with_parameter(
2,136✔
363
                {"pattern",
364
                 "The pattern to match.  "
365
                 "A percent symbol (%) will match zero or more characters "
366
                 "and an underscore (_) will match a single character."})
367
            .with_parameter({"str", "The string to match"})
2,136✔
368
            .with_parameter(
1,068✔
369
                help_text("escape",
2,136✔
370
                          "The escape character that can be used to prefix a "
371
                          "literal percent or underscore in the pattern.")
372
                    .optional())
1,068✔
373
            .with_example(
1,068✔
374
                {"To test if the string 'aabcc' contains the letter 'b'",
375
                 "SELECT like('%b%', 'aabcc')"})
376
            .with_example({"To test if the string 'aab%' ends with 'b%'",
1,068✔
377
                           "SELECT like('%b:%', 'aab%', ':')"}),
378

379
        help_text(
1,068✔
380
            "likelihood",
381
            "Provides a hint to the query planner that the first argument is a "
382
            "boolean that is true with the given probability")
383
            .sql_function()
1,068✔
384
            .with_parameter({"value", "The boolean value to return"})
2,136✔
385
            .with_parameter({"probability",
2,136✔
386
                             "A floating point constant between 0.0 and 1.0"}),
387

388
        help_text("likely", "Short-hand for likelihood(X,0.9375)")
1,068✔
389
            .sql_function()
1,068✔
390
            .with_parameter({"value", "The boolean value to return"}),
2,136✔
391

392
        help_text("load_extension",
1,068✔
393
                  "Loads SQLite extensions out of the given shared library "
394
                  "file using the given entry point.")
395
            .sql_function()
1,068✔
396
            .with_parameter(
2,136✔
397
                {"path",
398
                 "The path to the shared library containing the extension."})
399
            .with_parameter(help_text("entry-point", "").optional()),
2,136✔
400

401
        help_text("lower",
1,068✔
402
                  "Returns a copy of the given string with all ASCII "
403
                  "characters converted to lower case.")
404
            .sql_function()
1,068✔
405
            .with_parameter({"str", "The string to convert."})
2,136✔
406
            .with_tags({"string"})
1,068✔
407
            .with_example(
1,068✔
408
                {"To lowercase the string 'AbC'", "SELECT lower('AbC')"}),
409

410
        help_text(
1,068✔
411
            "ltrim",
412
            "Returns a string formed by removing any and all characters that "
413
            "appear in the second argument from the left side of the first.")
414
            .sql_function()
1,068✔
415
            .with_parameter(
2,136✔
416
                {"str", "The string to trim characters from the left side"})
417
            .with_parameter(
1,068✔
418
                help_text("chars",
2,136✔
419
                          "The characters to trim.  Defaults to spaces.")
420
                    .optional())
1,068✔
421
            .with_tags({"string"})
1,068✔
422
            .with_example({
1,068✔
423
                "To trim the leading space characters from the string '   abc'",
424
                "SELECT ltrim('   abc')",
425
            })
426
            .with_example({
1,068✔
427
                "To trim the characters 'a' or 'b' from the left side of the "
428
                "string 'aaaabbbc'",
429
                "SELECT ltrim('aaaabbbc', 'ab')",
430
            }),
431

432
        help_text("max",
1,068✔
433
                  "Returns the argument with the maximum value, or return NULL "
434
                  "if any argument is NULL.")
435
            .sql_function()
1,068✔
436
            .with_parameter(help_text("X",
3,204✔
437
                                      "The numbers to find the maximum of.  "
438
                                      "If only one argument is given, this "
439
                                      "function operates as an aggregate.")
440
                                .one_or_more())
1,068✔
441
            .with_tags({"math"})
1,068✔
442
            .with_example({"To get the largest value from the parameters",
1,068✔
443
                           "SELECT max(2, 1, 3)"})
444
            .with_example({"To get the largest value from an aggregate",
1,068✔
445
                           "SELECT max(status) FROM http_status_codes"}),
446

447
        help_text("min",
1,068✔
448
                  "Returns the argument with the minimum value, or return NULL "
449
                  "if any argument is NULL.")
450
            .sql_function()
1,068✔
451
            .with_parameter(help_text("X",
3,204✔
452
                                      "The numbers to find the minimum of.  "
453
                                      "If only one argument is given, this "
454
                                      "function operates as an aggregate.")
455
                                .one_or_more())
1,068✔
456
            .with_tags({"math"})
1,068✔
457
            .with_example({"To get the smallest value from the parameters",
1,068✔
458
                           "SELECT min(2, 1, 3)"})
459
            .with_example({"To get the smallest value from an aggregate",
1,068✔
460
                           "SELECT min(status) FROM http_status_codes"}),
461

462
        help_text("nullif",
1,068✔
463
                  "Returns its first argument if the arguments are different "
464
                  "and NULL if the arguments are the same.")
465
            .sql_function()
1,068✔
466
            .with_parameter({"X", "The first argument to compare."})
2,136✔
467
            .with_parameter({"Y", "The argument to compare against the first."})
2,136✔
468
            .with_example(
1,068✔
469
                {"To test if 1 is different from 1", "SELECT nullif(1, 1)"})
470
            .with_example(
1,068✔
471
                {"To test if 1 is different from 2", "SELECT nullif(1, 2)"}),
472

473
        help_text("octet_length",
1,068✔
474
                  "Returns the number of bytes in the given string as encoded "
475
                  "in the database")
476
            .sql_function()
1,068✔
477
            .with_parameter({"X", "The value to examine"})
2,136✔
478
            .with_example({
1,068✔
479
                "To get the number of bytes for a string",
480
                "SELECT octet_length('Hello, World!')",
481
            })
482
            .with_example({
1,068✔
483
                "To get the number of bytes for a number",
484
                "SELECT octet_length(42)",
485
            }),
486

487
        help_text("printf",
1,068✔
488
                  "Returns a string with this functions arguments substituted "
489
                  "into the given format.  "
490
                  "Substitution points are specified using percent (%) "
491
                  "options, much like the standard C printf() function.")
492
            .sql_function()
1,068✔
493
            .with_parameter({"format", "The format of the string to return."})
2,136✔
494
            .with_parameter(help_text("X",
2,136✔
495
                                      "The argument to substitute at a given "
496
                                      "position in the format."))
497
            .with_tags({"string"})
1,068✔
498
            .with_example({"To substitute 'World' into the string 'Hello, %s!'",
1,068✔
499
                           "SELECT printf('Hello, %s!', 'World')"})
500
            .with_example({"To right-align 'small' in the string 'align:' with "
1,068✔
501
                           "a column width of 10",
502
                           "SELECT printf('align: % 10s', 'small')"})
503
            .with_example({"To format 11 with a width of five characters and "
1,068✔
504
                           "leading zeroes",
505
                           "SELECT printf('value: %05d', 11)"}),
506

507
        help_text("quote",
1,068✔
508
                  "Returns the text of an SQL literal which is the value of "
509
                  "its argument suitable for inclusion into an SQL statement.")
510
            .sql_function()
1,068✔
511
            .with_parameter({"X", "The string to quote."})
2,136✔
512
            .with_example({"To quote the string 'abc'", "SELECT quote('abc')"})
1,068✔
513
            .with_example(
1,068✔
514
                {"To quote the string 'abc'123'", "SELECT quote('abc''123')"}),
515

516
        help_text("random",
1,068✔
517
                  "Returns a pseudo-random integer between "
518
                  "-9223372036854775808 and +9223372036854775807.")
519
            .sql_function(),
1,068✔
520

521
        help_text("randomblob",
1,068✔
522
                  "Return an N-byte blob containing pseudo-random bytes.")
523
            .sql_function()
1,068✔
524
            .with_parameter({"N", "The size of the blob in bytes."}),
2,136✔
525

526
        help_text(
1,068✔
527
            "replace",
528
            "Returns a string formed by substituting the replacement string "
529
            "for every occurrence of the old string in the given string.")
530
            .sql_function()
1,068✔
531
            .with_parameter({"str", "The string to perform substitutions on."})
2,136✔
532
            .with_parameter({"old", "The string to be replaced."})
2,136✔
533
            .with_parameter({"replacement",
2,136✔
534
                             "The string to replace any occurrences of the old "
535
                             "string with."})
536
            .with_tags({"string"})
1,068✔
537
            .with_example({"To replace the string 'x' with 'z' in 'abc'",
1,068✔
538
                           "SELECT replace('abc', 'x', 'z')"})
539
            .with_example({"To replace the string 'a' with 'z' in 'abc'",
1,068✔
540
                           "SELECT replace('abc', 'a', 'z')"}),
541

542
        help_text("round",
1,068✔
543
                  "Returns a floating-point value rounded to the given number "
544
                  "of digits to the right of the decimal point.")
545
            .sql_function()
1,068✔
546
            .with_parameter({"num", "The value to round."})
2,136✔
547
            .with_parameter(help_text("digits",
3,204✔
548
                                      "The number of digits to the right of "
549
                                      "the decimal to round to.")
550
                                .optional())
1,068✔
551
            .with_tags({"math"})
1,068✔
552
            .with_example({"To round the number 123.456 to an integer",
1,068✔
553
                           "SELECT round(123.456)"})
554
            .with_example({"To round the number 123.456 to a precision of 1",
1,068✔
555
                           "SELECT round(123.456, 1)"})
556
            .with_example({"To round the number 123.456 to a precision of 5",
1,068✔
557
                           "SELECT round(123.456, 5)"}),
558

559
        help_text(
1,068✔
560
            "rtrim",
561
            "Returns a string formed by removing any and all characters that "
562
            "appear in the second argument from the right side of the first.")
563
            .sql_function()
1,068✔
564
            .with_parameter(
2,136✔
565
                {"str", "The string to trim characters from the right side"})
566
            .with_parameter(
1,068✔
567
                help_text("chars",
2,136✔
568
                          "The characters to trim.  Defaults to spaces.")
569
                    .optional())
1,068✔
570
            .with_tags({"string"})
1,068✔
571
            .with_example({
1,068✔
572
                "To trim the space characters from the end of the string 'abc  "
573
                " '",
574
                "SELECT rtrim('abc   ')",
575
            })
576
            .with_example({
1,068✔
577
                "To trim the characters 'b' and 'c' from the string "
578
                "'abbbbcccc'",
579
                "SELECT rtrim('abbbbcccc', 'bc')",
580
            }),
581

582
        help_text("sqlite_compileoption_get",
1,068✔
583
                  "Returns the N-th compile-time option used to build SQLite "
584
                  "or NULL if N is out of range.")
585
            .sql_function()
1,068✔
586
            .with_parameter({"N", "The option number to get"}),
2,136✔
587

588
        help_text("sqlite_compileoption_used",
1,068✔
589
                  "Returns true (1) or false (0) depending on whether or not "
590
                  "that compile-time option was used during the build.")
591
            .sql_function()
1,068✔
592
            .with_parameter({"option", "The name of the compile-time option."})
2,136✔
593
            .with_example(
1,068✔
594
                {"To check if the SQLite library was compiled with ENABLE_FTS3",
595
                 "SELECT sqlite_compileoption_used('ENABLE_FTS3')"}),
596

597
        help_text("sqlite_source_id",
1,068✔
598
                  "Returns a string that identifies the specific version of "
599
                  "the source code that was used to build the SQLite library.")
600
            .sql_function(),
1,068✔
601

602
        help_text("sqlite_version",
1,068✔
603
                  "Returns the version string for the SQLite library that is "
604
                  "running.")
605
            .sql_function(),
1,068✔
606

607
        help_text("substr",
1,068✔
608
                  "Returns a substring of input string X that begins with the "
609
                  "Y-th character and which is Z characters long.")
610
            .sql_function()
1,068✔
611
            .with_parameter({"str", "The string to extract a substring from."})
2,136✔
612
            .with_parameter(
2,136✔
613
                {"start",
614
                 "The index within 'str' that is the start of the substring.  "
615
                 "Indexes begin at 1.  "
616
                 "A negative value means that the substring is found by "
617
                 "counting from the right rather than the left.  "})
618
            .with_parameter(
1,068✔
619
                help_text("size",
2,136✔
620
                          "The size of the substring.  "
621
                          "If not given, then all characters through the end "
622
                          "of the string are returned.  "
623
                          "If the value is negative, then the characters "
624
                          "before the start are returned.")
625
                    .optional())
1,068✔
626
            .with_tags({"string"})
1,068✔
627
            .with_example({"To get the substring starting at the second "
1,068✔
628
                           "character until the end of the string 'abc'",
629
                           "SELECT substr('abc', 2)"})
630
            .with_example({"To get the substring of size one starting at the "
1,068✔
631
                           "second character of the string 'abc'",
632
                           "SELECT substr('abc', 2, 1)"})
633
            .with_example({"To get the substring starting at the last "
1,068✔
634
                           "character until the end of the string 'abc'",
635
                           "SELECT substr('abc', -1)"})
636
            .with_example(
1,068✔
637
                {"To get the substring starting at the last character and "
638
                 "going backwards one step of the string 'abc'",
639
                 "SELECT substr('abc', -1, -1)"}),
640

641
        help_text("total_changes",
1,068✔
642
                  "Returns the number of row changes caused by INSERT, UPDATE "
643
                  "or DELETE statements since the current database connection "
644
                  "was opened.")
645
            .sql_function(),
1,068✔
646

647
        help_text("trim",
1,068✔
648
                  "Returns a string formed by removing any and all characters "
649
                  "that appear in the second argument from the left and right "
650
                  "sides of the first.")
651
            .sql_function()
1,068✔
652
            .with_parameter({"str",
2,136✔
653
                             "The string to trim characters from the left and "
654
                             "right sides."})
655
            .with_parameter(
1,068✔
656
                help_text("chars",
2,136✔
657
                          "The characters to trim.  Defaults to spaces.")
658
                    .optional())
1,068✔
659
            .with_tags({"string"})
1,068✔
660
            .with_example({
1,068✔
661
                "To trim spaces from the start and end of the string '    abc  "
662
                " '",
663
                "SELECT trim('    abc   ')",
664
            })
665
            .with_example({
1,068✔
666
                "To trim the characters '-' and '+' from the string '-+abc+-'",
667
                "SELECT trim('-+abc+-', '-+')",
668
            }),
669

670
        help_text(
1,068✔
671
            "typeof",
672
            "Returns a string that indicates the datatype of the expression X: "
673
            "\"null\", \"integer\", \"real\", \"text\", or \"blob\".")
674
            .sql_function()
1,068✔
675
            .with_parameter({"X", "The expression to check."})
2,136✔
676
            .with_example(
1,068✔
677
                {"To get the type of the number 1", "SELECT typeof(1)"})
678
            .with_example({"To get the type of the string 'abc'",
1,068✔
679
                           "SELECT typeof('abc')"}),
680

681
        help_text("unhex")
1,068✔
682
            .with_summary("Returns a blob value that is a decoding of the "
1,068✔
683
                          "given hex string")
684
            .sql_function()
1,068✔
685
            .with_tags({"string"})
1,068✔
686
            .with_parameter({"X", "The hex string to decode"})
2,136✔
687
            .with_example({
1,068✔
688
                "To decode the string 'Hello' encoded in hex",
689
                "SELECT unhex('48656c6c6f')",
690
            }),
691

692
        help_text("unicode",
1,068✔
693
                  "Returns the numeric unicode code point corresponding to the "
694
                  "first character of the string X.")
695
            .sql_function()
1,068✔
696
            .with_parameter({"X", "The string to examine."})
2,136✔
697
            .with_tags({"string"})
1,068✔
698
            .with_example({"To get the unicode code point for the first "
1,068✔
699
                           "character of 'abc'",
700
                           "SELECT unicode('abc')"}),
701

702
        help_text("unlikely", "Short-hand for likelihood(X, 0.0625)")
1,068✔
703
            .sql_function()
1,068✔
704
            .with_parameter({"value", "The boolean value to return"}),
2,136✔
705

706
        help_text("upper",
1,068✔
707
                  "Returns a copy of the given string with all ASCII "
708
                  "characters converted to upper case.")
709
            .sql_function()
1,068✔
710
            .with_parameter({"str", "The string to convert."})
2,136✔
711
            .with_tags({"string"})
1,068✔
712
            .with_example(
1,068✔
713
                {"To uppercase the string 'aBc'", "SELECT upper('aBc')"}),
714

715
        help_text("zeroblob", "Returns a BLOB consisting of N bytes of 0x00.")
1,068✔
716
            .sql_function()
1,068✔
717
            .with_parameter({"N", "The size of the BLOB."}),
2,136✔
718

719
        help_text("date", "Returns the date in this format: YYYY-MM-DD.")
1,068✔
720
            .sql_function()
1,068✔
721
            .with_parameter({"timestring", "The string to convert to a date."})
2,136✔
722
            .with_parameter(help_text("modifier",
3,204✔
723
                                      "A transformation that is applied to the "
724
                                      "value to the left.")
725
                                .zero_or_more())
1,068✔
726
            .with_tags({"datetime"})
1,068✔
727
            .with_example({"To get the date portion of the timestamp "
1,068✔
728
                           "'2017-01-02T03:04:05'",
729
                           "SELECT date('2017-01-02T03:04:05')"})
730
            .with_example({"To get the date portion of the timestamp "
1,068✔
731
                           "'2017-01-02T03:04:05' plus one day",
732
                           "SELECT date('2017-01-02T03:04:05', '+1 day')"})
733
            .with_example(
1,068✔
734
                {"To get the date portion of the epoch timestamp 1491341842",
735
                 "SELECT date(1491341842, 'unixepoch')"}),
736

737
        help_text("time", "Returns the time in this format: HH:MM:SS.")
1,068✔
738
            .sql_function()
1,068✔
739
            .with_parameter({"timestring", "The string to convert to a time."})
2,136✔
740
            .with_parameter(help_text("modifier",
3,204✔
741
                                      "A transformation that is applied to the "
742
                                      "value to the left.")
743
                                .zero_or_more())
1,068✔
744
            .with_tags({"datetime"})
1,068✔
745
            .with_example({"To get the time portion of the timestamp "
1,068✔
746
                           "'2017-01-02T03:04:05'",
747
                           "SELECT time('2017-01-02T03:04:05')"})
748
            .with_example({"To get the time portion of the timestamp "
1,068✔
749
                           "'2017-01-02T03:04:05' plus one minute",
750
                           "SELECT time('2017-01-02T03:04:05', '+1 minute')"})
751
            .with_example(
1,068✔
752
                {"To get the time portion of the epoch timestamp 1491341842",
753
                 "SELECT time(1491341842, 'unixepoch')"}),
754

755
        help_text(
1,068✔
756
            "datetime",
757
            "Returns the date and time in this format: YYYY-MM-DD HH:MM:SS.")
758
            .sql_function()
1,068✔
759
            .with_parameter(
2,136✔
760
                {"timestring", "The string to convert to a date with time."})
761
            .with_parameter(help_text("modifier",
3,204✔
762
                                      "A transformation that is applied to the "
763
                                      "value to the left.")
764
                                .zero_or_more())
1,068✔
765
            .with_tags({"datetime"})
1,068✔
766
            .with_example({"To get the date and time portion of the timestamp "
1,068✔
767
                           "'2017-01-02T03:04:05'",
768
                           "SELECT datetime('2017-01-02T03:04:05')"})
769
            .with_example(
1,068✔
770
                {"To get the date and time portion of the timestamp "
771
                 "'2017-01-02T03:04:05' plus one minute",
772
                 "SELECT datetime('2017-01-02T03:04:05', '+1 minute')"})
773
            .with_example({"To get the date and time portion of the epoch "
1,068✔
774
                           "timestamp 1491341842",
775
                           "SELECT datetime(1491341842, 'unixepoch')"}),
776

777
        help_text("julianday",
1,068✔
778
                  "Returns the number of days since noon in Greenwich on "
779
                  "November 24, 4714 B.C.")
780
            .sql_function()
1,068✔
781
            .with_parameter(
2,136✔
782
                {"timestring", "The string to convert to a date with time."})
783
            .with_parameter(help_text("modifier",
3,204✔
784
                                      "A transformation that is applied to the "
785
                                      "value to the left.")
786
                                .zero_or_more())
1,068✔
787
            .with_tags({"datetime"})
1,068✔
788
            .with_example({"To get the julian day from the timestamp "
1,068✔
789
                           "'2017-01-02T03:04:05'",
790
                           "SELECT julianday('2017-01-02T03:04:05')"})
791
            .with_example(
1,068✔
792
                {"To get the julian day from the timestamp "
793
                 "'2017-01-02T03:04:05' plus one minute",
794
                 "SELECT julianday('2017-01-02T03:04:05', '+1 minute')"})
795
            .with_example(
1,068✔
796
                {"To get the julian day from the timestamp 1491341842",
797
                 "SELECT julianday(1491341842, 'unixepoch')"}),
798

799
        help_text("strftime",
1,068✔
800
                  "Returns the date formatted according to the format string "
801
                  "specified as the first argument.")
802
            .sql_function()
1,068✔
803
            .with_parameter(
2,136✔
804
                {"format",
805
                 "A format string with substitutions similar to those found in "
806
                 "the strftime() standard C library."})
807
            .with_parameter(
2,136✔
808
                {"timestring", "The string to convert to a date with time."})
809
            .with_parameter(help_text("modifier",
3,204✔
810
                                      "A transformation that is applied to the "
811
                                      "value to the left.")
812
                                .zero_or_more())
1,068✔
813
            .with_tags({"datetime"})
1,068✔
814
            .with_example(
1,068✔
815
                {"To get the year from the timestamp '2017-01-02T03:04:05'",
816
                 "SELECT strftime('%Y', '2017-01-02T03:04:05')"})
817
            .with_example({"To create a string with the time from the "
1,068✔
818
                           "timestamp '2017-01-02T03:04:05' plus one minute",
819
                           "SELECT strftime('The time is: %H:%M:%S', "
820
                           "'2017-01-02T03:04:05', '+1 minute')"})
821
            .with_example(
1,068✔
822
                {"To create a string with the Julian day from the epoch "
823
                 "timestamp 1491341842",
824
                 "SELECT strftime('Julian day: %J', 1491341842, 'unixepoch')"}),
825

826
        help_text(
1,068✔
827
            "avg",
828
            "Returns the average value of all non-NULL numbers within a group.")
829
            .sql_function()
1,068✔
830
            .with_parameter({"X", "The value to compute the average of."})
2,136✔
831
            .with_tags({"math"})
1,068✔
832
            .with_example({"To get the average of the column 'ex_duration' "
1,068✔
833
                           "from the table 'lnav_example_log'",
834
                           "SELECT avg(ex_duration) FROM lnav_example_log"})
835
            .with_example(
1,068✔
836
                {"To get the average of the column 'ex_duration' from the "
837
                 "table 'lnav_example_log' when grouped by 'ex_procname'",
838
                 "SELECT ex_procname, avg(ex_duration) FROM lnav_example_log "
839
                 "GROUP BY ex_procname"}),
840

841
        help_text("count",
1,068✔
842
                  "If the argument is '*', the total number of rows in the "
843
                  "group is returned.  "
844
                  "Otherwise, the number of times the argument is non-NULL.")
845
            .sql_function()
1,068✔
846
            .with_parameter({"X", "The value to count."})
2,136✔
847
            .with_example(
1,068✔
848
                {"To get the count of the non-NULL rows of 'lnav_example_log'",
849
                 "SELECT count(*) FROM lnav_example_log"})
850
            .with_example({"To get the count of the non-NULL values of "
1,068✔
851
                           "'log_part' from 'lnav_example_log'",
852
                           "SELECT count(log_part) FROM lnav_example_log"}),
853

854
        help_text("group_concat",
1,068✔
855
                  "Returns a string which is the concatenation of all non-NULL "
856
                  "values of X separated by a comma or the given separator.")
857
            .sql_function()
1,068✔
858
            .with_parameter({"X", "The value to concatenate."})
2,136✔
859
            .with_parameter(
1,068✔
860
                help_text("sep", "The separator to place between the values.")
2,136✔
861
                    .optional())
1,068✔
862
            .with_tags({"string"})
1,068✔
863
            .with_example(
1,068✔
864
                {"To concatenate the values of the column 'ex_procname' from "
865
                 "the table 'lnav_example_log'",
866
                 "SELECT group_concat(ex_procname) FROM lnav_example_log"})
867
            .with_example({"To join the values of the column 'ex_procname' "
1,068✔
868
                           "using the string ', '",
869
                           "SELECT group_concat(ex_procname, ', ') FROM "
870
                           "lnav_example_log"})
871
            .with_example({"To concatenate the distinct values of the column "
1,068✔
872
                           "'ex_procname' from the table 'lnav_example_log'",
873
                           "SELECT group_concat(DISTINCT ex_procname) FROM "
874
                           "lnav_example_log"}),
875

876
        help_text("sum",
1,068✔
877
                  "Returns the sum of the values in the group as an integer.")
878
            .sql_function()
1,068✔
879
            .with_parameter({"X", "The values to add."})
2,136✔
880
            .with_tags({"math"})
1,068✔
881
            .with_example({
1,068✔
882
                "To sum all of the values in the column "
883
                "'ex_duration' from the table 'lnav_example_log'",
884
                "SELECT sum(ex_duration) FROM lnav_example_log",
885
            }),
886

887
        help_text(
1,068✔
888
            "total",
889
            "Returns the sum of the values in the group as a floating-point.")
890
            .sql_function()
1,068✔
891
            .with_parameter({"X", "The values to add."})
2,136✔
892
            .with_tags({"math"})
1,068✔
893
            .with_example({
1,068✔
894
                "To total all of the values in the column "
895
                "'ex_duration' from the table 'lnav_example_log'",
896
                "SELECT total(ex_duration) FROM lnav_example_log",
897
            }),
898

899
        help_text("generate_series",
1,068✔
900
                  "A table-valued-function that returns the whole numbers "
901
                  "between a lower and upper bound, inclusive")
902
            .sql_table_valued_function()
1,068✔
903
            .with_parameter({"start", "The starting point of the series"})
2,136✔
904
            .with_parameter({"stop", "The stopping point of the series"})
2,136✔
905
            .with_parameter(
1,068✔
906
                help_text("step", "The increment between each value")
2,136✔
907
                    .optional())
1,068✔
908
            .with_result({"value", "The number in the series"})
2,136✔
909
            .with_example({
1,068✔
910
                "To generate the numbers in the range [10, 14]",
911
                "SELECT value FROM generate_series(10, 14)",
912
            })
913
            .with_example({
1,068✔
914
                "To generate every other number in the range [10, 14]",
915
                "SELECT value FROM generate_series(10, 14, 2)",
916
            })
917
            .with_example({"To count down from five to 1",
1,068✔
918
                           "SELECT value FROM generate_series(1, 5, -1)"}),
919

920
        help_text("json",
1,068✔
921
                  "Verifies that its argument is valid JSON and returns a "
922
                  "minified version or throws an error.")
923
            .sql_function()
1,068✔
924
            .with_parameter({"X", "The string to interpret as JSON."})
2,136✔
925
            .with_tags({"json"}),
1,068✔
926

927
        help_text("json_array", "Constructs a JSON array from its arguments.")
1,068✔
928
            .sql_function()
1,068✔
929
            .with_parameter(
1,068✔
930
                help_text{"X", "The values of the JSON array"}.zero_or_more())
2,136✔
931
            .with_tags({"json"})
1,068✔
932
            .with_example({"To create an array of all types",
1,068✔
933
                           "SELECT json_array(NULL, 1, 2.1, 'three', "
934
                           "json_array(4), json_object('five', 'six'))"})
935
            .with_example({"To create an empty array", "SELECT json_array()"}),
1,068✔
936

937
        help_text("json_array_length", "Returns the length of a JSON array.")
1,068✔
938
            .sql_function()
1,068✔
939
            .with_parameter({"X", "The JSON object."})
2,136✔
940
            .with_parameter(
1,068✔
941
                help_text{"P", "The path to the array in 'X'."}.optional())
2,136✔
942
            .with_tags({"json"})
1,068✔
943
            .with_example({"To get the length of an array",
1,068✔
944
                           "SELECT json_array_length('[1, 2, 3]')"})
945
            .with_example(
1,068✔
946
                {"To get the length of a nested array",
947
                 "SELECT json_array_length('{\"arr\": [1, 2, 3]}', '$.arr')"}),
948

949
        help_text(
1,068✔
950
            "json_extract",
951
            "Returns the value(s) from the given JSON at the given path(s).")
952
            .sql_function()
1,068✔
953
            .with_parameter({"X", "The JSON value."})
2,136✔
954
            .with_parameter(
1,068✔
955
                help_text{"P", "The path to extract."}.one_or_more())
2,136✔
956
            .with_tags({"json"})
1,068✔
957
            .with_example({"To get a number",
1,068✔
958
                           R"(SELECT json_extract('{"num": 1}', '$.num'))"})
959
            .with_example(
1,068✔
960
                {"To get two numbers",
961
                 R"(SELECT json_extract('{"num": 1, "val": 2}', '$.num', '$.val'))"})
962
            .with_example(
1,068✔
963
                {"To get an object",
964
                 R"(SELECT json_extract('{"obj": {"sub": 1}}', '$.obj'))"})
965
#if 0 && SQLITE_VERSION_NUMBER >= 3038000
966
            .with_example({"To get a JSON value using the short-hand",
967
                           R"(SELECT '{"a":"b"}' -> '$.a')"})
968
            .with_example({"To get a SQL value using the short-hand",
969
                           R"(SELECT '{"a":"b"}' ->> '$.a')"})
970
#endif
971
            ,
972

973
        help_text("json_insert",
1,068✔
974
                  "Inserts values into a JSON object/array at the given "
975
                  "locations, if it does not already exist")
976
            .sql_function()
1,068✔
977
            .with_parameter({"X", "The JSON value to update"})
2,136✔
978
            .with_parameter({"P",
2,136✔
979
                             "The path to the insertion point.  A '#' array "
980
                             "index means append the value"})
981
            .with_parameter({"Y", "The value to insert"})
2,136✔
982
            .with_tags({"json"})
1,068✔
983
            .with_example({"To append to an array",
1,068✔
984
                           R"(SELECT json_insert('[1, 2]', '$[#]', 3))"})
985
            .with_example({"To update an object",
1,068✔
986
                           R"(SELECT json_insert('{"a": 1}', '$.b', 2))"})
987
            .with_example({"To ensure a value is set",
1,068✔
988
                           R"(SELECT json_insert('{"a": 1}', '$.a', 2))"})
989
            .with_example(
1,068✔
990
                {"To update multiple values",
991
                 R"(SELECT json_insert('{"a": 1}', '$.b', 2, '$.c', 3))"}),
992

993
        help_text("json_replace",
1,068✔
994
                  "Replaces existing values in a JSON object/array at the "
995
                  "given locations")
996
            .sql_function()
1,068✔
997
            .with_parameter({"X", "The JSON value to update"})
2,136✔
998
            .with_parameter({"P", "The path to replace"})
2,136✔
999
            .with_parameter({"Y", "The new value for the property"})
2,136✔
1000
            .with_tags({"json"})
1,068✔
1001
            .with_example({"To replace an existing value",
1,068✔
1002
                           R"(SELECT json_replace('{"a": 1}', '$.a', 2))"})
1003
            .with_example(
1,068✔
1004
                {"To replace a value without creating a new property",
1005
                 R"(SELECT json_replace('{"a": 1}', '$.a', 2, '$.b', 3))"}),
1006

1007
        help_text("json_set",
1,068✔
1008
                  "Inserts or replaces existing values in a JSON object/array "
1009
                  "at the given locations")
1010
            .sql_function()
1,068✔
1011
            .with_parameter({"X", "The JSON value to update"})
2,136✔
1012
            .with_parameter({"P",
2,136✔
1013
                             "The path to the insertion point.  A '#' array "
1014
                             "index means append the value"})
1015
            .with_parameter({"Y", "The value to set"})
2,136✔
1016
            .with_tags({"json"})
1,068✔
1017
            .with_example({"To replace an existing array element",
1,068✔
1018
                           R"(SELECT json_set('[1, 2]', '$[1]', 3))"})
1019
            .with_example(
1,068✔
1020
                {"To replace a value and create a new property",
1021
                 R"(SELECT json_set('{"a": 1}', '$.a', 2, '$.b', 3))"}),
1022

1023
        help_text("json_object",
1,068✔
1024
                  "Create a JSON object from the given arguments")
1025
            .sql_function()
1,068✔
1026
            .with_parameter({"N", "The property name"})
2,136✔
1027
            .with_parameter({"V", "The property value"})
2,136✔
1028
            .with_tags({"json"})
1,068✔
1029
            .with_example(
1,068✔
1030
                {"To create an object", "SELECT json_object('a', 1, 'b', 'c')"})
1031
            .with_example(
1,068✔
1032
                {"To create an empty object", "SELECT json_object()"}),
1033

1034
        help_text("json_remove", "Removes paths from a JSON value")
1,068✔
1035
            .sql_function()
1,068✔
1036
            .with_parameter({"X", "The JSON value to update"})
2,136✔
1037
            .with_parameter(help_text{"P", "The paths to remove"}.one_or_more())
2,136✔
1038
            .with_tags({"json"})
1,068✔
1039
            .with_example({"To remove elements of an array",
1,068✔
1040
                           "SELECT json_remove('[1,2,3]', '$[1]', '$[1]')"})
1041
            .with_example({"To remove object properties",
1,068✔
1042
                           R"(SELECT json_remove('{"a":1,"b":2}', '$.b'))"}),
1043

1044
        help_text("json_type", "Returns the type of a JSON value")
1,068✔
1045
            .sql_function()
1,068✔
1046
            .with_parameter({"X", "The JSON value to query"})
2,136✔
1047
            .with_parameter(help_text{"P", "The path to the value"}.optional())
2,136✔
1048
            .with_tags({"json"})
1,068✔
1049
            .with_example(
1,068✔
1050
                {"To get the type of a value",
1051
                 R"(SELECT json_type('[null,1,2.1,"three",{"four":5}]'))"})
1052
            .with_example(
1,068✔
1053
                {"To get the type of an array element",
1054
                 R"(SELECT json_type('[null,1,2.1,"three",{"four":5}]', '$[0]'))"})
1055
            .with_example(
1,068✔
1056
                {"To get the type of a string",
1057
                 R"(SELECT json_type('[null,1,2.1,"three",{"four":5}]', '$[3]'))"}),
1058

1059
        help_text("json_valid", "Tests if the given value is valid JSON")
1,068✔
1060
            .sql_function()
1,068✔
1061
            .with_parameter({"X", "The value to check"})
2,136✔
1062
            .with_tags({"json"})
1,068✔
1063
            .with_example({"To check an empty string", "SELECT json_valid('')"})
1,068✔
1064
            .with_example({"To check a string", R"(SELECT json_valid('"a"'))"}),
1,068✔
1065

1066
        help_text("json_quote",
1,068✔
1067
                  "Returns the JSON representation of the given value, if it "
1068
                  "is not already JSON")
1069
            .sql_function()
1,068✔
1070
            .with_parameter({"X", "The value to convert"})
2,136✔
1071
            .with_tags({"json"})
1,068✔
1072
            .with_example(
1,068✔
1073
                {"To convert a string", "SELECT json_quote('Hello, World!')"})
1074
            .with_example({"To pass through an existing JSON value",
1,068✔
1075
                           R"(SELECT json_quote(json('"Hello, World!"')))"}),
1076

1077
        help_text("json_each",
1,068✔
1078
                  "A table-valued-function that returns the children of the "
1079
                  "top-level JSON value")
1080
            .sql_table_valued_function()
1,068✔
1081
            .with_parameter({"X", "The JSON value to query"})
2,136✔
1082
            .with_parameter(
1,068✔
1083
                help_text{"P", "The path to the value to query"}.optional())
2,136✔
1084
            .with_result({"key",
2,136✔
1085
                          "The array index for elements of an array or "
1086
                          "property names of the object"})
1087
            .with_result({"value", "The value for the current element"})
2,136✔
1088
            .with_result({"type", "The type of the current element"})
2,136✔
1089
            .with_result(
2,136✔
1090
                {"atom",
1091
                 "The SQL value of the element, if it is a primitive type"})
1092
            .with_result({"fullkey", "The path to the current element"})
2,136✔
1093
            .with_tags({"json"})
1,068✔
1094
            .with_example(
1,068✔
1095
                {"To iterate over an array",
1096
                 R"(SELECT * FROM json_each('[null,1,"two",{"three":4.5}]'))"}),
1097

1098
        help_text("json_tree",
1,068✔
1099
                  "A table-valued-function that recursively descends through a "
1100
                  "JSON value")
1101
            .sql_table_valued_function()
1,068✔
1102
            .with_parameter({"X", "The JSON value to query"})
2,136✔
1103
            .with_parameter(
1,068✔
1104
                help_text{"P", "The path to the value to query"}.optional())
2,136✔
1105
            .with_result({"key",
2,136✔
1106
                          "The array index for elements of an array or "
1107
                          "property names of the object"})
1108
            .with_result({"value", "The value for the current element"})
2,136✔
1109
            .with_result({"type", "The type of the current element"})
2,136✔
1110
            .with_result(
2,136✔
1111
                {"atom",
1112
                 "The SQL value of the element, if it is a primitive type"})
1113
            .with_result({"fullkey", "The path to the current element"})
2,136✔
1114
            .with_result({"path", "The path to the container of this element"})
2,136✔
1115
            .with_tags({"json"})
1,068✔
1116
            .with_example(
1,068✔
1117
                {"To iterate over an array",
1118
                 R"(SELECT key,value,type,atom,fullkey,path FROM json_tree('[null,1,"two",{"three":4.5}]'))"}),
1119

1120
        help_text("text.contains", "Returns true if col contains sub")
1,068✔
1121
            .prql_function()
1,068✔
1122
            .with_parameter(
1,068✔
1123
                help_text{"sub", "The substring to look for in col"})
2,136✔
1124
            .with_parameter(help_text{"col", "The string to examine"})
2,136✔
1125
            .with_example({
1,068✔
1126
                "To check if 'Hello' contains 'lo'",
1127
                "from [{s='Hello'}] | select { s=text.contains 'lo' s }",
1128
                help_example::language::prql,
1129
            })
1130
            .with_example({
1,068✔
1131
                "To check if 'Goodbye' contains 'lo'",
1132
                "from [{s='Goodbye'}] | select { s=text.contains 'lo' s }",
1133
                help_example::language::prql,
1134
            }),
1135
        help_text("text.ends_with", "Returns true if col ends with suffix")
1,068✔
1136
            .prql_function()
1,068✔
1137
            .with_parameter(
1,068✔
1138
                help_text{"suffix", "The string to look for at the end of col"})
2,136✔
1139
            .with_parameter(help_text{"col", "The string to examine"})
2,136✔
1140
            .with_example({
1,068✔
1141
                "To check if 'Hello' ends with 'lo'",
1142
                "from [{s='Hello'}] | select { s=text.ends_with 'lo' s }",
1143
                help_example::language::prql,
1144
            })
1145
            .with_example({
1,068✔
1146
                "To check if 'Goodbye' ends with 'lo'",
1147
                "from [{s='Goodbye'}] | select { s=text.ends_with 'lo' s }",
1148
                help_example::language::prql,
1149
            }),
1150
        help_text("text.extract", "Extract a slice of a string")
1,068✔
1151
            .prql_function()
1,068✔
1152
            .with_parameter(help_text{
2,136✔
1153
                "idx",
1154
                "The starting index where the first character is index 1"})
1155
            .with_parameter(help_text{"len", "The length of the slice"})
2,136✔
1156
            .with_parameter(help_text{"str", "The string to extract from"})
2,136✔
1157
            .with_example({
1,068✔
1158
                "To extract a substring from s",
1159
                "from [{s='Hello, World!'}] | select { s=text.extract 1 5 s }",
1160
                help_example::language::prql,
1161
            }),
1162
        help_text("text.length", "Returns the number of characters in col")
1,068✔
1163
            .prql_function()
1,068✔
1164
            .with_parameter(help_text{"col", "The string to examine"})
2,136✔
1165
            .with_example({
1,068✔
1166
                "To count the number of characters in s",
1167
                "from [{s='Hello, World!'}] | select { s=text.length s }",
1168
                help_example::language::prql,
1169
            }),
1170
        help_text("text.lower", "Converts col to lowercase")
1,068✔
1171
            .prql_function()
1,068✔
1172
            .with_parameter(help_text{"col", "The string to convert"})
2,136✔
1173
            .with_example({
1,068✔
1174
                "To convert s to lowercase",
1175
                "from [{s='HELLO'}] | select { s=text.lower s }",
1176
                help_example::language::prql,
1177
            }),
1178
        help_text("text.ltrim", "Remove whitespace from the left side of col")
1,068✔
1179
            .prql_function()
1,068✔
1180
            .with_parameter(help_text{"col", "The string to trim"})
2,136✔
1181
            .with_example({
1,068✔
1182
                "To trim the left side of s",
1183
                "from [{s='  HELLO  '}] | select { s=text.ltrim s }",
1184
                help_example::language::prql,
1185
            }),
1186
        help_text("text.replace",
1,068✔
1187
                  "Replace all occurrences of before with after in col")
1188
            .prql_function()
1,068✔
1189
            .with_parameter(help_text{"before", "The string to find"})
2,136✔
1190
            .with_parameter(help_text{"after", "The replacement"})
2,136✔
1191
            .with_parameter(help_text{"col", "The string to trim"})
2,136✔
1192
            .with_example({
1,068✔
1193
                "To erase foo in s",
1194
                "from [{s='foobar'}] | select { s=text.replace 'foo' '' s }",
1195
                help_example::language::prql,
1196
            }),
1197
        help_text("text.rtrim", "Remove whitespace from the right side of col")
1,068✔
1198
            .prql_function()
1,068✔
1199
            .with_parameter(help_text{"col", "The string to trim"})
2,136✔
1200
            .with_example({
1,068✔
1201
                "To trim the right side of s",
1202
                "from [{s='  HELLO  '}] | select { s=text.rtrim s }",
1203
                help_example::language::prql,
1204
            }),
1205
        help_text("text.starts_with", "Returns true if col starts with suffix")
1,068✔
1206
            .prql_function()
1,068✔
1207
            .with_parameter(help_text{
2,136✔
1208
                "suffix", "The string to look for at the start of col"})
1209
            .with_parameter(help_text{"col", "The string to examine"})
2,136✔
1210
            .with_example({
1,068✔
1211
                "To check if 'Hello' starts with 'lo'",
1212
                "from [{s='Hello'}] | select { s=text.starts_with 'He' s }",
1213
                help_example::language::prql,
1214
            })
1215
            .with_example({
1,068✔
1216
                "To check if 'Goodbye' starts with 'lo'",
1217
                "from [{s='Goodbye'}] | select { s=text.starts_with 'He' s }",
1218
                help_example::language::prql,
1219
            }),
1220
        help_text("text.trim", "Remove whitespace from the both sides of col")
1,068✔
1221
            .prql_function()
1,068✔
1222
            .with_parameter(help_text{"col", "The string to trim"})
2,136✔
1223
            .with_example({
1,068✔
1224
                "To trim s",
1225
                "from [{s='  HELLO  '}] | select { s=text.trim s }",
1226
                help_example::language::prql,
1227
            }),
1228
        help_text("text.upper", "Converts col to uppercase")
1,068✔
1229
            .prql_function()
1,068✔
1230
            .with_parameter(help_text{"col", "The string to convert"})
2,136✔
1231
            .with_example({
2,136✔
1232
                "To convert s to uppercase",
1233
                "from [{s='hello'}] | select { s=text.upper s }",
1234
                help_example::language::prql,
1235
            }),
1236
    };
84,976✔
1237

1238
    if (!help_registration_done) {
1,672✔
1239
        for (auto& ht : builtin_funcs) {
84,372✔
1240
            switch (ht.ht_context) {
83,304✔
1241
                case help_context_t::HC_PRQL_FUNCTION:
11,748✔
1242
                    lnav::sql::prql_functions.emplace(ht.ht_name, &ht);
11,748✔
1243
                    break;
11,748✔
1244
                default:
71,556✔
1245
                    sqlite_function_help.emplace(ht.ht_name, &ht);
71,556✔
1246
                    break;
71,556✔
1247
            }
1248
            ht.index_tags();
83,304✔
1249
            if (!ht.ht_prql_path.empty()) {
83,304✔
UNCOV
1250
                register_help(phier, ht);
×
1251
            }
1252
        }
1253
    }
1254

1255
    static help_text builtin_win_funcs[] = {
1256
        help_text("row_number",
1,068✔
1257
                  "Returns the number of the row within the current partition, "
1258
                  "starting from 1.")
1259
            .sql_function()
1,068✔
1260
            .with_tags({"window"})
1,068✔
1261
            .with_example({"To number messages from a process",
1,068✔
1262
                           "SELECT row_number() OVER (PARTITION BY ex_procname "
1263
                           "ORDER BY log_line) AS msg_num, ex_procname, "
1264
                           "log_body FROM lnav_example_log"}),
1265

1266
        help_text("rank",
1,068✔
1267
                  "Returns the row_number() of the first peer in each group "
1268
                  "with gaps")
1269
            .sql_function()
1,068✔
1270
            .with_tags({"window"}),
1,068✔
1271

1272
        help_text("dense_rank",
1,068✔
1273
                  "Returns the row_number() of the first peer in each group "
1274
                  "without gaps")
1275
            .sql_function()
1,068✔
1276
            .with_tags({"window"}),
1,068✔
1277

1278
        help_text("percent_rank", "Returns (rank - 1) / (partition-rows - 1)")
1,068✔
1279
            .sql_function()
1,068✔
1280
            .with_tags({"window"}),
1,068✔
1281

1282
        help_text("cume_dist", "Returns the cumulative distribution")
1,068✔
1283
            .sql_function()
1,068✔
1284
            .with_tags({"window"}),
1,068✔
1285

1286
        help_text(
1,068✔
1287
            "ntile",
1288
            "Returns the number of the group that the current row is a part of")
1289
            .sql_function()
1,068✔
1290
            .with_parameter({"groups", "The number of groups"})
2,136✔
1291
            .with_tags({"window"}),
1,068✔
1292

1293
        help_text("lag",
1,068✔
1294
                  "Returns the result of evaluating the expression against the "
1295
                  "previous row in the partition.")
1296
            .sql_function()
1,068✔
1297
            .with_parameter(
2,136✔
1298
                {"expr", "The expression to execute over the previous row"})
1299
            .with_parameter(
1,068✔
1300
                help_text("offset",
2,136✔
1301
                          "The offset from the current row in the partition")
1302
                    .optional())
1,068✔
1303
            .with_parameter(help_text("default",
3,204✔
1304
                                      "The default value if the previous row "
1305
                                      "does not exist instead of NULL")
1306
                                .optional())
1,068✔
1307
            .with_tags({"window"}),
1,068✔
1308

1309
        help_text("lead",
1,068✔
1310
                  "Returns the result of evaluating the expression against the "
1311
                  "next row in the partition.")
1312
            .sql_function()
1,068✔
1313
            .with_parameter(
2,136✔
1314
                {"expr", "The expression to execute over the next row"})
1315
            .with_parameter(
1,068✔
1316
                help_text("offset",
2,136✔
1317
                          "The offset from the current row in the partition")
1318
                    .optional())
1,068✔
1319
            .with_parameter(help_text("default",
3,204✔
1320
                                      "The default value if the next row does "
1321
                                      "not exist instead of NULL")
1322
                                .optional())
1,068✔
1323
            .with_tags({"window"}),
1,068✔
1324

1325
        help_text("first_value",
1,068✔
1326
                  "Returns the result of evaluating the expression against the "
1327
                  "first row in the window frame.")
1328
            .sql_function()
1,068✔
1329
            .with_parameter(
2,136✔
1330
                {"expr", "The expression to execute over the first row"})
1331
            .with_tags({"window"}),
1,068✔
1332

1333
        help_text("last_value",
1,068✔
1334
                  "Returns the result of evaluating the expression against the "
1335
                  "last row in the window frame.")
1336
            .sql_function()
1,068✔
1337
            .with_parameter(
2,136✔
1338
                {"expr", "The expression to execute over the last row"})
1339
            .with_tags({"window"}),
1,068✔
1340

1341
        help_text("nth_value",
1,068✔
1342
                  "Returns the result of evaluating the expression against the "
1343
                  "nth row in the window frame.")
1344
            .sql_function()
1,068✔
1345
            .with_parameter(
2,136✔
1346
                {"expr", "The expression to execute over the nth row"})
1347
            .with_parameter({"N", "The row number"})
2,136✔
1348
            .with_tags({"window"}),
2,136✔
1349
    };
13,420✔
1350

1351
    if (!help_registration_done) {
1,672✔
1352
        for (auto& ht : builtin_win_funcs) {
12,816✔
1353
            sqlite_function_help.insert(std::make_pair(ht.ht_name, &ht));
11,748✔
1354
            ht.index_tags();
11,748✔
1355
        }
1356
    }
1357

1358
    static help_text idents[] = {
1359
        help_text("ATTACH", "Attach a database file to the current connection.")
1,068✔
1360
            .sql_keyword()
1,068✔
1361
            .with_parameter(
1,068✔
1362
                help_text("filename", "The path to the database file.")
2,136✔
1363
                    .with_flag_name("DATABASE"))
1,068✔
1364
            .with_parameter(help_text("schema-name",
3,204✔
1365
                                      "The prefix for tables in this database.")
1366
                                .with_flag_name("AS"))
1,068✔
1367
            .with_example({"To attach the database file '/tmp/customers.db' "
1,068✔
1368
                           "with the name customers",
1369
                           "ATTACH DATABASE '/tmp/customers.db' AS customers"}),
1370

1371
        help_text("DETACH", "Detach a database from the current connection.")
1,068✔
1372
            .sql_keyword()
1,068✔
1373
            .with_parameter(help_text("schema-name",
3,204✔
1374
                                      "The prefix for tables in this database.")
1375
                                .with_flag_name("DATABASE"))
1,068✔
1376
            .with_example({"To detach the database named 'customers'",
1,068✔
1377
                           "DETACH DATABASE customers"}),
1378

1379
        help_text("CREATE", "Assign a name to a SELECT statement")
1,068✔
1380
            .sql_keyword()
1,068✔
1381
            .with_parameter(help_text("TEMP").flag())
2,136✔
1382
            .with_parameter(help_text("").with_flag_name("VIEW"))
2,136✔
1383
            .with_parameter(
1,068✔
1384
                help_text("IF NOT EXISTS",
2,136✔
1385
                          "Do not create the view if it already exists")
1386
                    .flag())
1,068✔
1387
            .with_parameter(
1,068✔
1388
                help_text("schema-name.", "The database to create the view in")
2,136✔
1389
                    .optional())
1,068✔
1390
            .with_parameter(help_text("view-name", "The name of the view"))
2,136✔
1391
            .with_parameter(
1,068✔
1392
                help_text("select-stmt",
2,136✔
1393
                          "The SELECT statement the view represents")
1394
                    .with_flag_name("AS")),
1,068✔
1395

1396
        help_text("CREATE", "Create a table")
1,068✔
1397
            .sql_keyword()
1,068✔
1398
            .with_parameter(help_text("TEMP").flag())
2,136✔
1399
            .with_parameter(help_text("").with_flag_name("TABLE"))
2,136✔
1400
            .with_parameter(help_text("IF NOT EXISTS").flag())
2,136✔
1401
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1402
            .with_parameter(help_text("table-name"))
2,136✔
1403
            .with_parameter(help_text("select-stmt").with_flag_name("AS")),
2,136✔
1404

1405
        help_text("DELETE", "Delete rows from a table")
1,068✔
1406
            .sql_keyword()
1,068✔
1407
            .with_parameter(help_text("table-name", "The name of the table")
3,204✔
1408
                                .with_flag_name("FROM"))
1,068✔
1409
            .with_parameter(
1,068✔
1410
                help_text("cond", "The conditions used to delete the rows.")
2,136✔
1411
                    .with_flag_name("WHERE")
1,068✔
1412
                    .optional()),
1,068✔
1413

1414
        help_text("DROP", "Drop an index")
1,068✔
1415
            .sql_keyword()
1,068✔
1416
            .with_parameter(help_text("").with_flag_name("INDEX"))
2,136✔
1417
            .with_parameter(help_text("IF EXISTS").flag())
2,136✔
1418
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1419
            .with_parameter(help_text("index-name")),
2,136✔
1420

1421
        help_text("DROP", "Drop a table")
1,068✔
1422
            .sql_keyword()
1,068✔
1423
            .with_parameter(help_text("").with_flag_name("TABLE"))
2,136✔
1424
            .with_parameter(help_text("IF EXISTS").flag())
2,136✔
1425
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1426
            .with_parameter(help_text("table-name")),
2,136✔
1427

1428
        help_text("DROP", "Drop a view")
1,068✔
1429
            .sql_keyword()
1,068✔
1430
            .with_parameter(help_text("").with_flag_name("VIEW"))
2,136✔
1431
            .with_parameter(help_text("IF EXISTS").flag())
2,136✔
1432
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1433
            .with_parameter(help_text("view-name")),
2,136✔
1434

1435
        help_text("DROP", "Drop a trigger")
1,068✔
1436
            .sql_keyword()
1,068✔
1437
            .with_parameter(help_text("").with_flag_name("TRIGGER"))
2,136✔
1438
            .with_parameter(help_text("IF EXISTS").flag())
2,136✔
1439
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1440
            .with_parameter(help_text("trigger-name")),
2,136✔
1441

1442
        help_text("INSERT", "Insert rows into a table")
1,068✔
1443
            .sql_keyword()
1,068✔
1444
            .with_parameter(help_text("").with_flag_name("INTO"))
2,136✔
1445
            .with_parameter(help_text("schema-name.").optional())
2,136✔
1446
            .with_parameter(help_text("table-name"))
2,136✔
1447
            .with_parameter(
1,068✔
1448
                help_text("column-name").with_grouping("(", ")").zero_or_more())
2,136✔
1449
            .with_parameter(help_text("expr")
3,204✔
1450
                                .with_flag_name("VALUES")
1,068✔
1451
                                .with_grouping("(", ")")
1,068✔
1452
                                .one_or_more())
1,068✔
1453
            .with_example(
1,068✔
1454
                {"To insert the pair containing 'MSG' and 'HELLO, WORLD!' into "
1455
                 "the 'environ' table",
1456
                 "INSERT INTO environ VALUES ('MSG', 'HELLO, WORLD!')"}),
1457

1458
        help_text("SELECT",
1,068✔
1459
                  "Query the database and return zero or more rows of data.")
1460
            .sql_keyword()
1,068✔
1461
            .with_parameter(
1,068✔
1462
                help_text("filter", "Additional processing of rows")
2,136✔
1463
                    .optional()
1,068✔
1464
                    .with_enum_values({"DISTINCT"_frag, "ALL"_frag}))
1,068✔
1465
            .with_parameter(
1,068✔
1466
                help_text(
2,136✔
1467
                    "result-column",
1468
                    "The expression used to generate a result for this column.")
1469
                    .one_or_more())
1,068✔
1470
            .with_parameter(help_text("table", "The table(s) to query for data")
3,204✔
1471
                                .with_flag_name("FROM")
1,068✔
1472
                                .zero_or_more())
1,068✔
1473
            .with_parameter(
1,068✔
1474
                help_text("cond",
2,136✔
1475
                          "The conditions used to select the rows to return.")
1476
                    .with_flag_name("WHERE")
1,068✔
1477
                    .optional())
1,068✔
1478
            .with_parameter(
1,068✔
1479
                help_text("grouping-expr",
2,136✔
1480
                          "The expression to use when grouping rows.")
1481
                    .with_flag_name("GROUP BY")
1,068✔
1482
                    .zero_or_more())
1,068✔
1483
            .with_parameter(
1,068✔
1484
                help_text("ordering-term",
2,136✔
1485
                          "The values to use when ordering the result set.")
1486
                    .with_flag_name("ORDER BY")
1,068✔
1487
                    .zero_or_more())
1,068✔
1488
            .with_parameter(
1,068✔
1489
                help_text("limit-expr", "The maximum number of rows to return.")
2,136✔
1490
                    .with_flag_name("LIMIT")
1,068✔
1491
                    .zero_or_more())
1,068✔
1492
            .with_example({"To select all of the columns from the table "
1,068✔
1493
                           "'lnav_example_log'",
1494
                           "SELECT * FROM lnav_example_log"}),
1495

1496
        help_text("WITH",
1,068✔
1497
                  "Create a temporary view that exists only for the duration "
1498
                  "of a SQL statement.")
1499
            .sql_keyword()
1,068✔
1500
            .with_parameter(
1,068✔
1501
                help_text("").with_flag_name("RECURSIVE").optional())
2,136✔
1502
            .with_parameter(
2,136✔
1503
                {"cte-table-name", "The name for the temporary table."})
1504
            .with_parameter(help_text("select-stmt",
3,204✔
1505
                                      "The SELECT statement used to populate "
1506
                                      "the temporary table.")
1507
                                .with_flag_name("AS")),
1,068✔
1508

1509
        help_text(
1,068✔
1510
            "UPDATE",
1511
            "Modify a subset of values in zero or more rows of the given table")
1512
            .sql_keyword()
1,068✔
1513
            .with_parameter(help_text("table", "The table to update"))
2,136✔
1514
            .with_parameter(help_text("").with_flag_name("SET"))
2,136✔
1515
            .with_parameter(
1,068✔
1516
                help_text("column-name", "The columns in the table to update.")
2,136✔
1517
                    .with_parameter(
1,068✔
1518
                        help_text("expr",
2,136✔
1519
                                  "The values to place into the column.")
1520
                            .with_flag_name("="))
1,068✔
1521
                    .one_or_more())
1,068✔
1522
            .with_parameter(help_text("cond",
3,204✔
1523
                                      "The condition used to determine whether "
1524
                                      "a row should be updated.")
1525
                                .with_flag_name("WHERE")
1,068✔
1526
                                .optional())
1,068✔
1527
            .with_example({
1,068✔
1528
                "To mark the syslog message at line 40",
1529
                "UPDATE syslog_log SET log_mark = 1 WHERE log_line = 40",
1530
            }),
1531

1532
        help_text("CASE",
1,068✔
1533
                  "Evaluate a series of expressions in order until one "
1534
                  "evaluates to true and then return it's result.  "
1535
                  "Similar to an IF-THEN-ELSE construct in other languages.")
1536
            .sql_keyword()
1,068✔
1537
            .with_parameter(help_text("base-expr",
3,204✔
1538
                                      "The base expression that is used for "
1539
                                      "comparison in the branches")
1540
                                .optional())
1,068✔
1541
            .with_parameter(
1,068✔
1542
                help_text(
2,136✔
1543
                    "cmp-expr",
1544
                    "The expression to test if this branch should be taken")
1545
                    .with_flag_name("WHEN")
1,068✔
1546
                    .one_or_more()
1,068✔
1547
                    .with_parameter(
1,068✔
1548
                        help_text("then-expr", "The result for this branch.")
2,136✔
1549
                            .with_flag_name("THEN")))
1,068✔
1550
            .with_parameter(
1,068✔
1551
                help_text("else-expr",
2,136✔
1552
                          "The result of this CASE if no branches matched.")
1553
                    .with_flag_name("ELSE")
1,068✔
1554
                    .optional())
1,068✔
1555
            .with_parameter(help_text("").with_flag_name("END"))
2,136✔
1556
            .with_example({
1,068✔
1557
                "To evaluate the number one and return the string 'one'",
1558
                "SELECT CASE 1 WHEN 0 THEN 'zero' WHEN 1 THEN 'one' END",
1559
            }),
1560

1561
        help_text("CAST",
1,068✔
1562
                  "Convert the value of the given expression to a different "
1563
                  "storage class specified by type-name.")
1564
            .sql_function()
1,068✔
1565
            .with_parameter({"expr", "The value to convert."})
2,136✔
1566
            .with_parameter(
1,068✔
1567
                help_text("type-name", "The name of the type to convert to.")
2,136✔
1568
                    .with_flag_name("AS"))
1,068✔
1569
            .with_example({
1,068✔
1570
                "To cast the value 1.23 as an integer",
1571
                "SELECT CAST(1.23 AS INTEGER)",
1572
            }),
1573

1574
        help_text("expr", "Match an expression against a glob pattern.")
1,068✔
1575
            .sql_infix()
1,068✔
1576
            .with_parameter(help_text("NOT").flag())
2,136✔
1577
            .with_parameter(
1,068✔
1578
                help_text("pattern", "The glob pattern to match against.")
2,136✔
1579
                    .with_flag_name("GLOB"))
1,068✔
1580
            .with_example({
1,068✔
1581
                "To check if a value matches the pattern '*.log'",
1582
                "SELECT 'foobar.log' GLOB '*.log'",
1583
            }),
1584

1585
        help_text("expr", "Match an expression against a text pattern.")
1,068✔
1586
            .sql_infix()
1,068✔
1587
            .with_parameter(help_text("NOT").flag())
2,136✔
1588
            .with_parameter(
1,068✔
1589
                help_text("pattern", "The pattern to match against.")
2,136✔
1590
                    .with_flag_name("LIKE"))
1,068✔
1591
            .with_parameter(
1,068✔
1592
                help_text("escape",
2,136✔
1593
                          "Character used to escape a % or _ in the pattern")
1594
                    .with_flag_name("ESCAPE")
1,068✔
1595
                    .optional())
1,068✔
1596
            .with_example({
1,068✔
1597
                "To check if a value matches the pattern 'Hello, %!'",
1598
                "SELECT 'Hello, World!' LIKE 'Hello, %!'",
1599
            }),
1600

1601
        help_text("expr", "Match an expression against a regular expression.")
1,068✔
1602
            .sql_infix()
1,068✔
1603
            .with_parameter(help_text("NOT").flag())
2,136✔
1604
            .with_parameter(
1,068✔
1605
                help_text("pattern", "The regular expression to match against.")
2,136✔
1606
                    .with_flag_name("REGEXP"))
1,068✔
1607
            .with_example({
1,068✔
1608
                "To check if a value matches the pattern 'file-\\d+'",
1609
                "SELECT 'file-23' REGEXP 'file-\\d+'",
1610
            }),
1611

1612
        help_text("expr", "Check an expression against NULL")
1,068✔
1613
            .sql_infix()
1,068✔
1614
            .with_parameter(help_text("nullness")
3,204✔
1615
                                .with_enum_values({
1,068✔
1616
                                    "ISNULL"_frag,
1617
                                    "NOTNULL"_frag,
1618
                                    "NOT NULL"_frag,
1619
                                    "IS NOT NULL"_frag,
1620
                                })
1621
                                .optional())
1,068✔
1622
            .with_example({
1,068✔
1623
                "To check if a value is not NULL",
1624
                "SELECT 'abc' NOT NULL",
1625
            }),
1626

1627
        help_text("expr", "Assign a collating sequence to the expression.")
1,068✔
1628
            .sql_infix()
1,068✔
1629
            .with_parameter(
1,068✔
1630
                help_text("collation-name", "The name of the collator.")
2,136✔
1631
                    .with_flag_name("COLLATE"))
1,068✔
1632
            .with_example({
1,068✔
1633
                "To change the collation method for string comparisons",
1634
                "SELECT ('a2' < 'a10'), ('a2' < 'a10' COLLATE "
1635
                "naturalnocase)",
1636
            }),
1637

1638
        help_text("expr", "Test if an expression is between two values.")
1,068✔
1639
            .sql_infix()
1,068✔
1640
            .with_parameter(help_text("NOT").flag())
2,136✔
1641
            .with_parameter(
1,068✔
1642
                help_text("low", "The low point").with_flag_name("BETWEEN"))
2,136✔
1643
            .with_parameter(
1,068✔
1644
                help_text("hi", "The high point").with_flag_name("AND"))
2,136✔
1645
            .with_example({
1,068✔
1646
                "To check if 3 is between 5 and 10",
1647
                "SELECT 3 BETWEEN 5 AND 10",
1648
            })
1649
            .with_example({
1,068✔
1650
                "To check if 10 is between 5 and 10",
1651
                "SELECT 10 BETWEEN 5 AND 10",
1652
            }),
1653

1654
        help_text("expr", "Test the distinctness of an expression")
1,068✔
1655
            .sql_infix()
1,068✔
1656
            .with_parameter(
1,068✔
1657
                help_text("expr")
2,136✔
1658
                    .with_flag_name("IS")
1,068✔
1659
                    .with_parameter(help_text("NOT").flag())
2,136✔
1660
                    .with_parameter(help_text("DISTINCT FROM").flag()))
2,136✔
1661
            .with_example({
1,068✔
1662
                "To check if 10 is between 5 and 10",
1663
                "SELECT 10 BETWEEN 5 AND 10",
1664
            }),
1665

1666
        help_text("ordering-term")
1,068✔
1667
            .sql_infix()
1,068✔
1668
            .with_summary("The values to use in ordering result rows")
1,068✔
1669
            .with_parameter(help_text("collation-name")
3,204✔
1670
                                .with_flag_name("COLLATE")
1,068✔
1671
                                .optional())
1,068✔
1672
            .with_parameter(
1,068✔
1673
                help_text("direction", "The direction, ASCending or DESCending")
2,136✔
1674
                    .optional()
1,068✔
1675
                    .with_enum_values({"ASC"_frag, "DESC"_frag}))
1,068✔
1676
            .with_parameter(
1,068✔
1677
                help_text("null-handling")
2,136✔
1678
                    .optional()
1,068✔
1679
                    .with_enum_values({"NULLS FIRST"_frag, "NULLS LAST"_frag})),
1,068✔
1680

1681
        help_text("select-stmt")
1,068✔
1682
            .with_summary(
1,068✔
1683
                "Execute a query and return 0 if no rows match or 1 otherwise")
1684
            .sql_infix()
1,068✔
1685
            .with_grouping("(", ")")
1,068✔
1686
            .with_flag_name("EXISTS"),
1,068✔
1687

1688
        help_text("select-stmt")
1,068✔
1689
            .with_summary(
1,068✔
1690
                "Execute a query and return 1 if no rows match or 0 otherwise")
1691
            .sql_infix()
1,068✔
1692
            .with_grouping("(", ")")
1,068✔
1693
            .with_flag_name("NOT EXISTS"),
1,068✔
1694

1695
        help_text("FILTER")
1,068✔
1696
            .with_summary("Condition for rows to include in the aggregate")
1,068✔
1697
            .sql_infix()
1,068✔
1698
            .with_grouping("(", ")")
1,068✔
1699
            .with_parameter(help_text("expr").with_flag_name("WHERE")),
2,136✔
1700

1701
        help_text("OVER", "Executes the preceding function over a window")
1,068✔
1702
            .sql_keyword()
1,068✔
1703
            .with_parameter(
2,136✔
1704
                {"window-name", "The name of the window definition"}),
1705

1706
        help_text("OVER", "Executes the preceding function over a window")
1,068✔
1707
            .sql_function()
1,068✔
1708
            .with_parameter(help_text{
3,204✔
1709
                "base-window-name",
1710
                "The name of the window definition",
1711
            }
1712
                                .optional())
1,068✔
1713
            .with_parameter(
1,068✔
1714
                help_text{"expr", "The values to use for partitioning"}
2,136✔
1715
                    .with_flag_name("PARTITION BY")
1,068✔
1716
                    .zero_or_more())
1,068✔
1717
            .with_parameter(help_text{
3,204✔
1718
                "expr", "The values used to order the rows in the window"}
1719
                                .with_flag_name("ORDER BY")
1,068✔
1720
                                .zero_or_more())
1,068✔
1721
            .with_parameter(help_text{
3,204✔
1722
                "frame-spec",
1723
                "Determines which output rows are read "
1724
                "by an aggregate window function",
1725
            }
1726
                                .optional()),
1,068✔
1727
    };
31,576✔
1728

1729
    if (!help_registration_done) {
1,672✔
1730
        for (auto& ht : idents) {
30,972✔
1731
            insert_sql_help(ht, ht);
29,904✔
1732
        }
1733
    }
1734

1735
    help_registration_done = true;
1,672✔
1736

1737
#ifdef HAVE_RUST_DEPS
1738
    if (sqlite_extension_prql.empty()) {
1,672✔
1739
        require(phier.ph_declarations.empty());
1,068✔
1740
        for (const auto& mod_pair : phier.ph_modules) {
12,816✔
1741
            std::string content;
11,748✔
1742

1743
            mod_pair.second.to_string(content);
11,748✔
1744
            sqlite_extension_prql.emplace_back(lnav_rs_ext::SourceTreeElement{
11,748✔
1745
                fmt::format(FMT_STRING("{}.prql"), mod_pair.first),
58,740✔
1746
                content,
1747
            });
1748
        }
11,748✔
1749
    }
1750
#endif
1751

1752
    return 0;
1,672✔
1753
}
1,672✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc