lnav/src/sqlite-extension-func.cc

991 lines
46 KiB
C++

/**
* Copyright (c) 2013, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file sqlite-extension-func.c
*/
#include "config.h"
#include <assert.h>
#include "base/string_util.hh"
#include "base/lnav_log.hh"
#include "sql_util.hh"
#include "sqlite-extension-func.hh"
using namespace std;
sqlite_registration_func_t sqlite_registration_funcs[] = {
common_extension_functions,
state_extension_functions,
string_extension_functions,
network_extension_functions,
fs_extension_functions,
json_extension_functions,
time_extension_functions,
nullptr
};
int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
{
int lpc;
assert(db != nullptr);
assert(reg_funcs != nullptr);
for (lpc = 0; reg_funcs[lpc]; lpc++) {
struct FuncDef *basic_funcs = nullptr;
struct FuncDefAgg *agg_funcs = nullptr;
int i;
reg_funcs[lpc](&basic_funcs, &agg_funcs);
for (i = 0; basic_funcs && basic_funcs[i].zName; i++) {
struct FuncDef &fd = basic_funcs[i];
//sqlite3CreateFunc
/* LMH no error checking */
sqlite3_create_function(db,
basic_funcs[i].zName,
basic_funcs[i].nArg,
basic_funcs[i].eTextRep,
(void *) &fd,
basic_funcs[i].xFunc,
nullptr,
nullptr);
if (fd.fd_help.ht_context != help_context_t::HC_NONE) {
help_text &ht = fd.fd_help;
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
}
for (i = 0; agg_funcs && agg_funcs[i].zName; i++) {
struct FuncDefAgg &fda = agg_funcs[i];
//sqlite3CreateFunc
sqlite3_create_function(db,
agg_funcs[i].zName,
agg_funcs[i].nArg,
SQLITE_UTF8,
(void *) &agg_funcs[i],
0,
agg_funcs[i].xStep,
agg_funcs[i].xFinalize);
if (fda.fda_help.ht_context != help_context_t::HC_NONE) {
help_text &ht = fda.fda_help;
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
}
}
static help_text builtin_funcs[] = {
help_text("abs",
"Return the absolute value of the argument")
.sql_function()
.with_parameter({"x", "The number to convert"})
.with_tags({"math"})
.with_example({
"To get the absolute value of -1",
"SELECT abs(-1)"
}),
help_text("changes",
"The number of database rows that were changed, inserted, or deleted by the most recent statement.")
.sql_function(),
help_text("char",
"Returns a string composed of characters having the given unicode code point values")
.sql_function()
.with_parameter(help_text("X", "The unicode code point values")
.zero_or_more())
.with_tags({"string"})
.with_example({
"To get a string with the code points 0x48 and 0x49",
"SELECT char(0x48, 0x49)"
}),
help_text("coalesce",
"Returns a copy of its first non-NULL argument, or NULL if all arguments are NULL")
.sql_function()
.with_parameter({"X", "A value to check for NULL-ness"})
.with_parameter(help_text("Y", "A value to check for NULL-ness")
.one_or_more())
.with_example({
"To get the first non-null value from three parameters",
"SELECT coalesce(null, 0, null)"
}),
help_text("glob",
"Match a string against Unix glob pattern")
.sql_function()
.with_parameter({"pattern", "The glob pattern"})
.with_parameter({"str", "The string to match"})
.with_example({
"To test if the string 'abc' matches the glob 'a*'",
"SELECT glob('a*', 'abc')"
}),
help_text("hex",
"Returns a string which is the upper-case hexadecimal rendering of the content of its argument.")
.sql_function()
.with_parameter({"X", "The blob to convert to hexadecimal"})
.with_example({
"To get the hexadecimal rendering of the string 'abc'",
"SELECT hex('abc')"
}),
help_text("ifnull",
"Returns a copy of its first non-NULL argument, or NULL if both arguments are NULL")
.sql_function()
.with_parameter({"X", "A value to check for NULL-ness"})
.with_parameter({"Y", "A value to check for NULL-ness"})
.with_example({
"To get the first non-null value between null and zero",
"SELECT ifnull(null, 0)"
}),
help_text("instr",
"Finds the first occurrence of the needle within the haystack and returns the number of prior characters plus 1, or 0 if the needle was not found")
.sql_function()
.with_parameter({"haystack", "The string to search within"})
.with_parameter({"needle", "The string to look for in the haystack"})
.with_tags({"string"})
.with_example({
"To test get the position of 'b' in the string 'abc'",
"SELECT instr('abc', 'b')"
}),
help_text("last_insert_rowid",
"Returns the ROWID of the last row insert from the database connection which invoked the function")
.sql_function(),
help_text("length",
"Returns the number of characters (not bytes) in the given string prior to the first NUL character")
.sql_function()
.with_parameter({"str", "The string to determine the length of"})
.with_tags({"string"})
.with_example({
"To get the length of the string 'abc'",
"SELECT length('abc')"
}),
help_text("like",
"Match a string against a pattern")
.sql_function()
.with_parameter({"pattern",
"The pattern to match. "
"A percent symbol (%) will match zero or more characters "
"and an underscore (_) will match a single character."})
.with_parameter({"str", "The string to match"})
.with_parameter(help_text("escape",
"The escape character that can be used to prefix a literal percent or underscore in the pattern.")
.optional())
.with_example({
"To test if the string 'aabcc' contains the letter 'b'",
"SELECT like('%b%', 'aabcc')"
})
.with_example({
"To test if the string 'aab%' ends with 'b%'",
"SELECT like('%b:%', 'aab%', ':')"
}),
help_text("likelihood",
"Provides a hint to the query planner that the first argument is a boolean that is true with the given probability")
.sql_function()
.with_parameter({"value", "The boolean value to return"})
.with_parameter({"probability", "A floating point constant between 0.0 and 1.0"}),
help_text("likely",
"Short-hand for likelihood(X,0.9375)")
.sql_function()
.with_parameter({"value", "The boolean value to return"}),
help_text("load_extension",
"Loads SQLite extensions out of the given shared library file using the given entry point.")
.sql_function()
.with_parameter({"path", "The path to the shared library containing the extension."})
.with_parameter(help_text("entry-point", "")
.optional()),
help_text("lower",
"Returns a copy of the given string with all ASCII characters converted to lower case.")
.sql_function()
.with_parameter({"str", "The string to convert."})
.with_tags({"string"})
.with_example({
"To lowercase the string 'AbC'",
"SELECT lower('AbC')"
}),
help_text("ltrim",
"Returns a string formed by removing any and all characters that appear in the second argument from the left side of the first.")
.sql_function()
.with_parameter({"str", "The string to trim characters from the left side"})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({
"To trim the leading whitespace from the string ' abc'",
"SELECT ltrim(' abc')"})
.with_example({
"To trim the characters 'a' or 'b' from the left side of the string 'aaaabbbc'",
"SELECT ltrim('aaaabbbc', 'ab')"
}),
help_text("max",
"Returns the argument with the maximum value, or return NULL if any argument is NULL.")
.sql_function()
.with_parameter(help_text("X", "The numbers to find the maximum of. "
"If only one argument is given, this function operates as an aggregate.")
.one_or_more())
.with_tags({"math"})
.with_example({
"To get the largest value from the parameters",
"SELECT max(2, 1, 3)"})
.with_example({
"To get the largest value from an aggregate",
"SELECT max(status) FROM http_status_codes"
}),
help_text("min",
"Returns the argument with the minimum value, or return NULL if any argument is NULL.")
.sql_function()
.with_parameter(help_text("X", "The numbers to find the minimum of. "
"If only one argument is given, this function operates as an aggregate.")
.one_or_more())
.with_tags({"math"})
.with_example({
"To get the smallest value from the parameters",
"SELECT min(2, 1, 3)"})
.with_example({
"To get the smallest value from an aggregate",
"SELECT min(status) FROM http_status_codes"
}),
help_text("nullif",
"Returns its first argument if the arguments are different and NULL if the arguments are the same.")
.sql_function()
.with_parameter({"X", "The first argument to compare."})
.with_parameter({"Y", "The argument to compare against the first."})
.with_example({
"To test if 1 is different from 1",
"SELECT nullif(1, 1)"})
.with_example({
"To test if 1 is different from 2",
"SELECT nullif(1, 2)"
}),
help_text("printf",
"Returns a string with this functions arguments substituted into the given format. "
"Substitution points are specified using percent (%) options, much like the standard C printf() function.")
.sql_function()
.with_parameter({"format", "The format of the string to return."})
.with_parameter(help_text("X", "The argument to substitute at a given position in the format."))
.with_tags({"string"})
.with_example({
"To substitute 'World' into the string 'Hello, %s!'",
"SELECT printf('Hello, %s!', 'World')"})
.with_example({
"To right-align 'small' in the string 'align:' with a column width of 10",
"SELECT printf('align: % 10s', 'small')"})
.with_example({
"To format 11 with a width of five characters and leading zeroes",
"SELECT printf('value: %05d', 11)"
}),
help_text("quote",
"Returns the text of an SQL literal which is the value of its argument suitable for inclusion into an SQL statement.")
.sql_function()
.with_parameter({"X", "The string to quote."})
.with_example({
"To quote the string 'abc'",
"SELECT quote('abc')"})
.with_example({
"To quote the string 'abc'123'",
"SELECT quote('abc''123')"
}),
help_text("random",
"Returns a pseudo-random integer between -9223372036854775808 and +9223372036854775807.")
.sql_function(),
help_text("randomblob",
"Return an N-byte blob containing pseudo-random bytes.")
.sql_function()
.with_parameter({"N", "The size of the blob in bytes."}),
help_text("replace",
"Returns a string formed by substituting the replacement string for every occurrence of the old string in the given string.")
.sql_function()
.with_parameter({"str", "The string to perform substitutions on."})
.with_parameter({"old", "The string to be replaced."})
.with_parameter({"replacement", "The string to replace any occurrences of the old string with."})
.with_tags({"string"})
.with_example({
"To replace the string 'x' with 'z' in 'abc'",
"SELECT replace('abc', 'x', 'z')"})
.with_example({
"To replace the string 'a' with 'z' in 'abc'",
"SELECT replace('abc', 'a', 'z')"
}),
help_text("round",
"Returns a floating-point value rounded to the given number of digits to the right of the decimal point.")
.sql_function()
.with_parameter({"num", "The value to round."})
.with_parameter(help_text("digits", "The number of digits to the right of the decimal to round to.")
.optional())
.with_tags({"math"})
.with_example({
"To round the number 123.456 to an integer",
"SELECT round(123.456)"})
.with_example({
"To round the number 123.456 to a precision of 1",
"SELECT round(123.456, 1)"})
.with_example({
"To round the number 123.456 to a precision of 5",
"SELECT round(123.456, 5)"
}),
help_text("rtrim",
"Returns a string formed by removing any and all characters that appear in the second argument from the right side of the first.")
.sql_function()
.with_parameter({"str", "The string to trim characters from the right side"})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({
"To trim the whitespace from the end of the string 'abc '",
"SELECT rtrim('abc ')"
})
.with_example({
"To trim the characters 'b' and 'c' from the string 'abbbbcccc'",
"SELECT rtrim('abbbbcccc', 'bc')"
}),
help_text("sqlite_compileoption_get",
"Returns the N-th compile-time option used to build SQLite or NULL if N is out of range.")
.sql_function()
.with_parameter({"N", "The option number to get"}),
help_text("sqlite_compileoption_used",
"Returns true (1) or false (0) depending on whether or not that compile-time option was used during the build.")
.sql_function()
.with_parameter({"option", "The name of the compile-time option."})
.with_example({
"To check if the SQLite library was compiled with ENABLE_FTS3",
"SELECT sqlite_compileoption_used('ENABLE_FTS3')"
}),
help_text("sqlite_source_id",
"Returns a string that identifies the specific version of the source code that was used to build the SQLite library.")
.sql_function(),
help_text("sqlite_version",
"Returns the version string for the SQLite library that is running.")
.sql_function(),
help_text("substr",
"Returns a substring of input string X that begins with the Y-th character and which is Z characters long.")
.sql_function()
.with_parameter({"str", "The string to extract a substring from."})
.with_parameter({"start", "The index within 'str' that is the start of the substring. "
"Indexes begin at 1. "
"A negative value means that the substring is found by counting from the right rather than the left. "})
.with_parameter(help_text("size", "The size of the substring. "
"If not given, then all characters through the end of the string are returned. "
"If the value is negative, then the characters before the start are returned.")
.optional())
.with_tags({"string"})
.with_example({
"To get the substring starting at the second character until the end of the string 'abc'",
"SELECT substr('abc', 2)"
})
.with_example({
"To get the substring of size one starting at the second character of the string 'abc'",
"SELECT substr('abc', 2, 1)"
})
.with_example({
"To get the substring starting at the last character until the end of the string 'abc'",
"SELECT substr('abc', -1)"
})
.with_example({
"To get the substring starting at the last character and going backwards one step of the string 'abc'",
"SELECT substr('abc', -1, -1)"
}),
help_text("total_changes",
"Returns the number of row changes caused by INSERT, UPDATE or DELETE statements since the current database connection was opened.")
.sql_function(),
help_text("trim",
"Returns a string formed by removing any and all characters that appear in the second argument from the left and right sides of the first.")
.sql_function()
.with_parameter({"str", "The string to trim characters from the left and right sides."})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({
"To trim whitespace from the start and end of the string ' abc '",
"SELECT trim(' abc ')"
})
.with_example({
"To trim the characters '-' and '+' from the string '-+abc+-'",
"SELECT trim('-+abc+-', '-+')"
}),
help_text("typeof",
"Returns a string that indicates the datatype of the expression X: \"null\", \"integer\", \"real\", \"text\", or \"blob\".")
.sql_function()
.with_parameter({"X", "The expression to check."})
.with_example({
"To get the type of the number 1",
"SELECT typeof(1)"
})
.with_example({
"To get the type of the string 'abc'",
"SELECT typeof('abc')"
}),
help_text("unicode",
"Returns the numeric unicode code point corresponding to the first character of the string X.")
.sql_function()
.with_parameter({"X", "The string to examine."})
.with_tags({"string"})
.with_example({
"To get the unicode code point for the first character of 'abc'",
"SELECT unicode('abc')"
}),
help_text("unlikely",
"Short-hand for likelihood(X, 0.0625)")
.sql_function()
.with_parameter({"value", "The boolean value to return"}),
help_text("upper",
"Returns a copy of the given string with all ASCII characters converted to upper case.")
.sql_function()
.with_parameter({"str", "The string to convert."})
.with_tags({"string"})
.with_example({
"To uppercase the string 'aBc'",
"SELECT upper('aBc')"
}),
help_text("zeroblob",
"Returns a BLOB consisting of N bytes of 0x00.")
.sql_function()
.with_parameter({"N", "The size of the BLOB."}),
help_text("date",
"Returns the date in this format: YYYY-MM-DD.")
.sql_function()
.with_parameter({"timestring", "The string to convert to a date."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({
"To get the date portion of the timestamp '2017-01-02T03:04:05'",
"SELECT date('2017-01-02T03:04:05')"
})
.with_example({
"To get the date portion of the timestamp '2017-01-02T03:04:05' plus one day",
"SELECT date('2017-01-02T03:04:05', '+1 day')"
})
.with_example({
"To get the date portion of the epoch timestamp 1491341842",
"SELECT date(1491341842, 'unixepoch')"
}),
help_text("time",
"Returns the time in this format: HH:MM:SS.")
.sql_function()
.with_parameter({"timestring", "The string to convert to a time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({
"To get the time portion of the timestamp '2017-01-02T03:04:05'",
"SELECT time('2017-01-02T03:04:05')"
})
.with_example({
"To get the time portion of the timestamp '2017-01-02T03:04:05' plus one minute",
"SELECT time('2017-01-02T03:04:05', '+1 minute')"
})
.with_example({
"To get the time portion of the epoch timestamp 1491341842",
"SELECT time(1491341842, 'unixepoch')"
}),
help_text("datetime",
"Returns the date and time in this format: YYYY-MM-DD HH:MM:SS.")
.sql_function()
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({
"To get the date and time portion of the timestamp '2017-01-02T03:04:05'",
"SELECT datetime('2017-01-02T03:04:05')"
})
.with_example({
"To get the date and time portion of the timestamp '2017-01-02T03:04:05' plus one minute",
"SELECT datetime('2017-01-02T03:04:05', '+1 minute')"
})
.with_example({
"To get the date and time portion of the epoch timestamp 1491341842",
"SELECT datetime(1491341842, 'unixepoch')"
}),
help_text("julianday",
"Returns the number of days since noon in Greenwich on November 24, 4714 B.C.")
.sql_function()
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({
"To get the julian day from the timestamp '2017-01-02T03:04:05'",
"SELECT julianday('2017-01-02T03:04:05')"
})
.with_example({
"To get the julian day from the timestamp '2017-01-02T03:04:05' plus one minute",
"SELECT julianday('2017-01-02T03:04:05', '+1 minute')"
})
.with_example({
"To get the julian day from the timestamp 1491341842",
"SELECT julianday(1491341842, 'unixepoch')"
}),
help_text("strftime",
"Returns the date formatted according to the format string specified as the first argument.")
.sql_function()
.with_parameter({"format", "A format string with substitutions similar to those found in the strftime() standard C library."})
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({
"To get the year from the timestamp '2017-01-02T03:04:05'",
"SELECT strftime('%Y', '2017-01-02T03:04:05')"
})
.with_example({
"To create a string with the time from the timestamp '2017-01-02T03:04:05' plus one minute",
"SELECT strftime('The time is: %H:%M:%S', '2017-01-02T03:04:05', '+1 minute')"
})
.with_example({
"To create a string with the Julian day from the epoch timestamp 1491341842",
"SELECT strftime('Julian day: %J', 1491341842, 'unixepoch')"
}),
help_text("avg",
"Returns the average value of all non-NULL numbers within a group.")
.sql_function()
.with_parameter({"X", "The value to compute the average of."})
.with_tags({"math"})
.with_example({
"To get the average of the column 'ex_duration' from the table 'lnav_example_log'",
"SELECT avg(ex_duration) FROM lnav_example_log"
})
.with_example({
"To get the average of the column 'ex_duration' from the table 'lnav_example_log' when grouped by 'ex_procname'",
"SELECT ex_procname, avg(ex_duration) FROM lnav_example_log GROUP BY ex_procname"
}),
help_text("count",
"If the argument is '*', the total number of rows in the group is returned. "
"Otherwise, the number of times the argument is non-NULL.")
.sql_function()
.with_parameter({"X", "The value to count."})
.with_example({
"To get the count of the non-NULL rows of 'lnav_example_log'",
"SELECT count(*) FROM lnav_example_log"
})
.with_example({
"To get the count of the non-NULL values of 'log_part' from 'lnav_example_log'",
"SELECT count(log_part) FROM lnav_example_log"
}),
help_text("group_concat",
"Returns a string which is the concatenation of all non-NULL values of X separated by a comma or the given separator.")
.sql_function()
.with_parameter({"X", "The value to concatenate."})
.with_parameter(help_text("sep", "The separator to place between the values.")
.optional())
.with_tags({"string"})
.with_example({
"To concatenate the values of the column 'ex_procname' from the table 'lnav_example_log'",
"SELECT group_concat(ex_procname) FROM lnav_example_log"
})
.with_example({
"To join the values of the column 'ex_procname' using the string ', '",
"SELECT group_concat(ex_procname, ', ') FROM lnav_example_log"
})
.with_example({
"To concatenate the distinct values of the column 'ex_procname' from the table 'lnav_example_log'",
"SELECT group_concat(DISTINCT ex_procname) FROM lnav_example_log"
}),
help_text("sum",
"Returns the sum of the values in the group as an integer.")
.sql_function()
.with_parameter({"X", "The values to add."})
.with_tags({"math"})
.with_example({
"To sum all of the values in the column 'ex_duration' from the table 'lnav_example_log'",
"SELECT sum(ex_duration) FROM lnav_example_log"
}),
help_text("total",
"Returns the sum of the values in the group as a floating-point.")
.sql_function()
.with_parameter({"X", "The values to add."})
.with_tags({"math"})
.with_example({
"To total all of the values in the column 'ex_duration' from the table 'lnav_example_log'",
"SELECT total(ex_duration) FROM lnav_example_log"
}),
};
for (auto &ht : builtin_funcs) {
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
static help_text builtin_win_funcs[] = {
help_text("row_number", "Returns the number of the row within the current partition, starting from 1.")
.sql_function()
.with_tags({"window"})
.with_example({
"To number messages from a process",
"SELECT row_number() OVER (PARTITION BY ex_procname ORDER BY log_line) AS msg_num, ex_procname, log_body FROM lnav_example_log"
}),
help_text("rank", "Returns the row_number() of the first peer in each group with gaps")
.sql_function()
.with_tags({"window"}),
help_text("dense_rank", "Returns the row_number() of the first peer in each group without gaps")
.sql_function()
.with_tags({"window"}),
help_text("percent_rank", "Returns (rank - 1) / (partition-rows - 1)")
.sql_function()
.with_tags({"window"}),
help_text("cume_dist", "Returns the cumulative distribution")
.sql_function()
.with_tags({"window"}),
help_text("ntile", "Returns the number of the group that the current row is a part of")
.sql_function()
.with_parameter({"groups", "The number of groups"})
.with_tags({"window"}),
help_text("lag", "Returns the result of evaluating the expression against the previous row in the partition.")
.sql_function()
.with_parameter({"expr", "The expression to execute over the previous row"})
.with_parameter(help_text("offset", "The offset from the current row in the partition")
.optional())
.with_parameter(help_text("default", "The default value if the previous row does not exist instead of NULL")
.optional())
.with_tags({"window"}),
help_text("lead", "Returns the result of evaluating the expression against the next row in the partition.")
.sql_function()
.with_parameter({"expr", "The expression to execute over the next row"})
.with_parameter(help_text("offset", "The offset from the current row in the partition")
.optional())
.with_parameter(help_text("default", "The default value if the next row does not exist instead of NULL")
.optional())
.with_tags({"window"}),
help_text("first_value", "Returns the result of evaluating the expression against the first row in the window frame.")
.sql_function()
.with_parameter({"expr", "The expression to execute over the first row"})
.with_tags({"window"}),
help_text("last_value", "Returns the result of evaluating the expression against the last row in the window frame.")
.sql_function()
.with_parameter({"expr", "The expression to execute over the last row"})
.with_tags({"window"}),
help_text("nth_value", "Returns the result of evaluating the expression against the nth row in the window frame.")
.sql_function()
.with_parameter({"expr", "The expression to execute over the nth row"})
.with_parameter({"N", "The row number"})
.with_tags({"window"}),
};
for (auto &ht : builtin_win_funcs) {
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
static help_text idents[] = {
help_text("ATTACH",
"Attach a database file to the current connection.")
.sql_keyword()
.with_parameter(help_text("filename", "The path to the database file.")
.with_flag_name("DATABASE"))
.with_parameter(help_text("schema-name", "The prefix for tables in this database.")
.with_flag_name("AS"))
.with_example({
"To attach the database file '/tmp/customers.db' with the name customers",
"ATTACH DATABASE '/tmp/customers.db' AS customers"
}),
help_text("DETACH",
"Detach a database from the current connection.")
.sql_keyword()
.with_parameter(help_text("schema-name", "The prefix for tables in this database.")
.with_flag_name("DATABASE"))
.with_example({
"To detach the database named 'customers'",
"DETACH DATABASE customers"
}),
help_text("CREATE", "Assign a name to a SELECT statement")
.sql_keyword()
.with_parameter(help_text("TEMP")
.optional())
.with_parameter(help_text("")
.with_flag_name("VIEW"))
.with_parameter(help_text("IF NOT EXISTS", "Do not create the view if it already exists")
.optional())
.with_parameter(help_text("schema-name.", "The database to create the view in")
.optional())
.with_parameter(help_text("view-name", "The name of the view"))
.with_parameter(help_text("select-stmt", "The SELECT statement the view represents")
.with_flag_name("AS")),
help_text("CREATE", "Create a table")
.sql_keyword()
.with_parameter(help_text("TEMP").optional())
.with_parameter(help_text("")
.with_flag_name("TABLE"))
.with_parameter(help_text("IF NOT EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("table-name"))
.with_parameter(help_text("select-stmt")
.with_flag_name("AS")),
help_text("DELETE", "Delete rows from a table")
.sql_keyword()
.with_parameter(help_text("table-name", "The name of the table")
.with_flag_name("FROM"))
.with_parameter(help_text("cond", "The conditions used to delete the rows.")
.with_flag_name("WHERE")
.optional()),
help_text("DROP", "Drop an index")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("INDEX"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("index-name")),
help_text("DROP", "Drop a table")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("TABLE"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("table-name")),
help_text("DROP", "Drop a view")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("VIEW"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("view-name")),
help_text("DROP", "Drop a trigger")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("TRIGGER"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("trigger-name")),
help_text("INSERT", "Insert rows into a table")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("INTO"))
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("table-name"))
.with_parameter(help_text("column-name")
.with_grouping("(", ")")
.zero_or_more())
.with_parameter(help_text("expr")
.with_flag_name("VALUES")
.with_grouping("(", ")")
.one_or_more())
.with_example({
"To insert the pair containing 'MSG' and 'HELLO, WORLD!' into the 'environ' table",
"INSERT INTO environ VALUES ('MSG', 'HELLO, WORLD!')"
}),
help_text("SELECT",
"Query the database and return zero or more rows of data.")
.sql_keyword()
.with_parameter(help_text("result-column", "")
.one_or_more())
.with_parameter(help_text("table", "The table(s) to query for data")
.with_flag_name("FROM")
.zero_or_more())
.with_parameter(help_text("cond", "The conditions used to select the rows to return.")
.with_flag_name("WHERE")
.optional())
.with_parameter(help_text("grouping-expr", "The expression to use when grouping rows.")
.with_flag_name("GROUP BY")
.zero_or_more())
.with_parameter(help_text("ordering-term", "The values to use when ordering the result set.")
.with_flag_name("ORDER BY")
.zero_or_more())
.with_parameter(help_text("limit-expr", "The maximum number of rows to return")
.with_flag_name("LIMIT")
.zero_or_more())
.with_example({
"To select all of the columns from the table 'syslog_log'",
"SELECT * FROM syslog_log"
}),
help_text("WITH",
"Create a temporary view that exists only for the duration of a SQL statement.")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("RECURSIVE")
.optional())
.with_parameter({"cte-table-name", "The name for the temporary table."})
.with_parameter(help_text("select-stmt", "The SELECT statement used to populate the temporary table.")
.with_flag_name("AS")),
help_text("UPDATE",
"Modify a subset of values in zero or more rows of the given table")
.sql_keyword()
.with_parameter(help_text("table", "The table to update"))
.with_parameter(help_text("")
.with_flag_name("SET"))
.with_parameter(help_text("column-name", "The columns in the table to update.")
.with_parameter(help_text("expr", "The values to place into the column.")
.with_flag_name("="))
.one_or_more())
.with_parameter(help_text("cond", "The condition used to determine whether a row should be updated.")
.with_flag_name("WHERE")
.optional())
.with_example({
"To mark the syslog message at line 40",
"UPDATE syslog_log SET log_mark = 1 WHERE log_line = 40"
}),
help_text("CASE",
"Evaluate a series of expressions in order until one evaluates to true and then return it's result. "
"Similar to an IF-THEN-ELSE construct in other languages.")
.sql_keyword()
.with_parameter(help_text("base-expr", "The base expression that is used for comparison in the branches")
.optional())
.with_parameter(help_text("cmp-expr", "The expression to test if this branch should be taken")
.with_flag_name("WHEN")
.one_or_more()
.with_parameter(help_text("then-expr", "The result for this branch.")
.with_flag_name("THEN")))
.with_parameter(help_text("else-expr", "The result of this CASE if no branches matched.")
.with_flag_name("ELSE")
.optional())
.with_parameter(help_text("")
.with_flag_name("END"))
.with_example({
"To evaluate the number one and return the string 'one'",
"SELECT CASE 1 WHEN 0 THEN 'zero' WHEN 1 THEN 'one' END"
}),
help_text("CAST",
"Convert the value of the given expression to a different storage class specified by type-name.")
.sql_function()
.with_parameter({"expr", "The value to convert."})
.with_parameter(help_text("type-name", "The name of the type to convert to.")
.with_flag_name("AS"))
.with_example({
"To cast the value 1.23 as an integer",
"SELECT CAST(1.23 AS INTEGER)"
}),
help_text("OVER", "Executes the preceding function over a window")
.sql_keyword()
.with_parameter({"window-name", "The name of the window definition"}),
help_text("OVER", "Executes the preceding function over a window")
.sql_function()
.with_parameter(help_text{"base-window-name", "The name of the window definition"}
.optional())
.with_parameter(help_text{"expr", "The values to use for partitioning"}
.with_flag_name("PARTITION BY")
.zero_or_more())
.with_parameter(help_text{"expr", "The values used to order the rows in the window"}
.with_flag_name("ORDER BY")
.zero_or_more())
.with_parameter(help_text{"frame-spec", "Determines which output rows are read by an aggregate window function"}
.optional()),
};
for (auto &ht : idents) {
sqlite_function_help.insert(make_pair(toupper(ht.ht_name), &ht));
for (const auto &param : ht.ht_parameters) {
if (!param.ht_flag_name) {
continue;
}
sqlite_function_help.insert(make_pair(toupper(param.ht_flag_name), &ht));
}
}
return 0;
}