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

tstack / lnav / 24959179949-2999

26 Apr 2026 02:37PM UTC coverage: 69.227% (+0.09%) from 69.141%
24959179949-2999

push

github

tstack
[tests] fix paths

53969 of 77959 relevant lines covered (69.23%)

568944.78 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
14,208✔
85
    {
86
        for (const auto& mod_pair : this->ph_modules) {
15,392✔
87
            accum.append("module ");
1,184✔
88
            accum.append(mod_pair.first);
1,184✔
89
            accum.append(" {\n");
1,184✔
90
            mod_pair.second.to_string(accum);
1,184✔
91
            accum.append("}\n");
1,184✔
92
        }
93
        for (const auto& decl_pair : this->ph_declarations) {
56,832✔
94
            accum.append(decl_pair.second);
42,624✔
95
            accum.append("\n");
42,624✔
96
        }
97
    }
14,208✔
98
};
99

100
static void
101
register_help(prql_hier& phier, const help_text& ht)
41,440✔
102
{
103
    auto prql_fqid = fmt::to_string(fmt::join(ht.ht_prql_path, "."));
41,440✔
104
    lnav::sql::prql_functions.emplace(prql_fqid, &ht);
41,440✔
105

106
    auto* curr_hier = &phier;
41,440✔
107
    for (size_t name_index = 0; name_index < ht.ht_prql_path.size();
129,056✔
108
         name_index++)
109
    {
110
        const auto& prql_name = ht.ht_prql_path[name_index];
87,616✔
111
        if (name_index == ht.ht_prql_path.size() - 1) {
87,616✔
112
            auto param_names
113
                = ht.ht_parameters | lnav::itertools::map([](const auto& elem) {
41,440✔
114
                      if (elem.ht_nargs == help_nargs_t::HN_OPTIONAL
53,280✔
115
                          || elem.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE)
48,544✔
116
                      {
117
                          return fmt::format(FMT_STRING("{}:null"),
9,472✔
118
                                             elem.ht_name);
9,472✔
119
                      }
120
                      return fmt::format(FMT_STRING("p_{}"), elem.ht_name);
194,176✔
121
                  });
41,440✔
122
            auto func_args
123
                = ht.ht_parameters | lnav::itertools::map([](const auto& elem) {
82,880✔
124
                      if (elem.ht_nargs == help_nargs_t::HN_OPTIONAL
53,280✔
125
                          || elem.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE)
48,544✔
126
                      {
127
                          return fmt::format(FMT_STRING("{{{}:0}}"),
9,472✔
128
                                             elem.ht_name);
9,472✔
129
                      }
130
                      return fmt::format(FMT_STRING("{{p_{}:0}}"),
97,088✔
131
                                         elem.ht_name);
97,088✔
132
                  });
41,440✔
133
            curr_hier->ph_declarations[prql_name]
82,880✔
134
                = fmt::format(FMT_STRING("let {} = func {} -> s\"{}({})\""),
207,200✔
135
                              prql_name,
136
                              fmt::join(param_names, " "),
41,440✔
137
                              ht.ht_name,
41,440✔
138
                              fmt::join(func_args, ", "));
82,880✔
139
        } else {
41,440✔
140
            curr_hier = &curr_hier->ph_modules[prql_name];
138,528✔
141
        }
142
    }
143
}
41,440✔
144

145
static void
146
insert_sql_help(help_text& root, const help_text& curr)
98,272✔
147
{
148
    if (curr.ht_flag_name) {
98,272✔
149
        sqlite_function_help.insert(
50,912✔
150
            std::make_pair(toupper(curr.ht_flag_name), &root));
101,824✔
151
    } else if (isupper(curr.ht_name[0])) {
47,360✔
152
        sqlite_function_help.insert(
37,888✔
153
            std::make_pair(toupper(curr.ht_name), &root));
75,776✔
154
    }
155
    for (const auto& param : curr.ht_parameters) {
196,544✔
156
        if (param.ht_flag_name) {
98,272✔
157
            insert_sql_help(root, param);
48,544✔
158
        }
159
        if (param.ht_format == help_parameter_format_t::HPF_NONE
98,272✔
160
            && param.ht_nargs == help_nargs_t::HN_OPTIONAL)
16,576✔
161
        {
162
            insert_sql_help(root, param);
16,576✔
163
        }
164
        for (const auto& eval : param.ht_enum_values) {
110,112✔
165
            sqlite_function_help.emplace(eval.to_string(), &root);
11,840✔
166
        }
167
    }
168
}
98,272✔
169

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

177
    phier.ph_modules["sqlite"].ph_declarations["strftime"]
7,588✔
178
        = "let strftime = func p_format p_timestring -> "
179
          "s\"strftime({p_format:0}, {p_timestring:0})\"";
1,897✔
180

181
    require(db != nullptr);
1,897✔
182
    require(reg_funcs != nullptr);
1,897✔
183

184
    {
185
        auto_mem<char> errmsg(sqlite3_free);
1,897✔
186

187
        sqlite3_series_init(db, errmsg.out(), nullptr);
1,897✔
188
    }
1,897✔
189

190
    for (lpc = 0; reg_funcs[lpc]; lpc++) {
17,073✔
191
        struct FuncDef* basic_funcs = nullptr;
15,176✔
192
        struct FuncDefAgg* agg_funcs = nullptr;
15,176✔
193
        int i;
194

195
        reg_funcs[lpc](&basic_funcs, &agg_funcs);
15,176✔
196

197
        for (i = 0; basic_funcs && basic_funcs[i].zName; i++) {
172,627✔
198
            struct FuncDef& fd = basic_funcs[i];
157,451✔
199

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

211
            if (!help_registration_done
157,451✔
212
                && fd.fd_help.ht_context != help_context_t::HC_NONE)
98,272✔
213
            {
214
                auto& ht = fd.fd_help;
85,248✔
215

216
                sqlite_function_help.insert(std::make_pair(ht.ht_name, &ht));
85,248✔
217
                ht.index_tags();
85,248✔
218
                if (!ht.ht_prql_path.empty()) {
85,248✔
219
                    register_help(phier, ht);
36,704✔
220
                }
221
            }
222
        }
223

224
        for (i = 0; agg_funcs && agg_funcs[i].zName; i++) {
39,837✔
225
            struct FuncDefAgg& fda = agg_funcs[i];
24,661✔
226

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

237
            if (!help_registration_done
24,661✔
238
                && fda.fda_help.ht_context != help_context_t::HC_NONE)
15,392✔
239
            {
240
                auto& ht = fda.fda_help;
5,920✔
241

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1237
    if (!help_registration_done) {
1,897✔
1238
        for (auto& ht : builtin_funcs) {
93,536✔
1239
            switch (ht.ht_context) {
92,352✔
1240
                case help_context_t::HC_PRQL_FUNCTION:
13,024✔
1241
                    lnav::sql::prql_functions.emplace(ht.ht_name, &ht);
13,024✔
1242
                    break;
13,024✔
1243
                default:
79,328✔
1244
                    sqlite_function_help.emplace(ht.ht_name, &ht);
79,328✔
1245
                    break;
79,328✔
1246
            }
1247
            ht.index_tags();
92,352✔
1248
            if (!ht.ht_prql_path.empty()) {
92,352✔
1249
                register_help(phier, ht);
×
1250
            }
1251
        }
1252
    }
1253

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

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

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

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

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

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

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

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

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

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

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

1350
    if (!help_registration_done) {
1,897✔
1351
        for (auto& ht : builtin_win_funcs) {
14,208✔
1352
            sqlite_function_help.insert(std::make_pair(ht.ht_name, &ht));
13,024✔
1353
            ht.index_tags();
13,024✔
1354
        }
1355
    }
1356

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1728
    if (!help_registration_done) {
1,897✔
1729
        for (auto& ht : idents) {
34,336✔
1730
            insert_sql_help(ht, ht);
33,152✔
1731
        }
1732
    }
1733

1734
    help_registration_done = true;
1,897✔
1735

1736
#ifdef HAVE_RUST_DEPS
1737
    if (sqlite_extension_prql.empty()) {
1,897✔
1738
        require(phier.ph_declarations.empty());
1,184✔
1739
        for (const auto& mod_pair : phier.ph_modules) {
14,208✔
1740
            std::string content;
13,024✔
1741

1742
            mod_pair.second.to_string(content);
13,024✔
1743
            sqlite_extension_prql.emplace_back(lnav_rs_ext::SourceTreeElement{
13,024✔
1744
                fmt::format(FMT_STRING("{}.prql"), mod_pair.first),
65,120✔
1745
                content,
1746
            });
1747
        }
13,024✔
1748
    }
1749
#endif
1750

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

© 2026 Coveralls, Inc