mirror of
https://github.com/tstack/lnav.git
synced 2024-09-19 16:57:32 +03:00
[sql] add json_group_object aggregate function
This commit is contained in:
parent
4eb797ce9a
commit
62075fb340
2
NEWS
2
NEWS
@ -4,6 +4,8 @@ lnav v0.8.1:
|
||||
* Log formats can now create SQL views and execute other statements
|
||||
by adding '.sql' files to their format directories. The SQL scripts
|
||||
will be executed on startup.
|
||||
* Added a 'json_group_object' aggregate SQL function that collects values
|
||||
from a GROUP BY query into an JSON object.
|
||||
|
||||
Interface Changes:
|
||||
* The 'o/O' hotkeys have been reassigned to navigate through log
|
||||
|
@ -39,8 +39,8 @@ AC_PROG_CXX
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -D_ISOC99_SOURCE -D__STDC_LIMIT_MACROS"
|
||||
|
||||
# CFLAGS=`echo $CFLAGS | sed 's/-O2//g'`
|
||||
# CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'`
|
||||
CFLAGS=`echo $CFLAGS | sed 's/-O2//g'`
|
||||
CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'`
|
||||
|
||||
AC_ARG_VAR(SFTP_TEST_URL)
|
||||
|
||||
|
@ -49,7 +49,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'lnav'
|
||||
copyright = u'2014, Tim Stack'
|
||||
copyright = u'2015, Tim Stack'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
@ -139,6 +139,18 @@ Network information functions:
|
||||
* gethostbyaddr - Convert an IPv4/IPv6 address into a host name. If the
|
||||
reverse lookup fails, the input value will be returned.
|
||||
|
||||
JSON
|
||||
----
|
||||
|
||||
JSON functions:
|
||||
|
||||
* jget(json, json_ptr) - Get the value from the JSON-encoded string in
|
||||
first argument that is referred to by the
|
||||
`JSON-Pointer <https://tools.ietf.org/html/rfc6901>`_ in the second.
|
||||
* json_group_array(key0, value0, ... keyN, valueN) - An aggregate function
|
||||
that creates a JSON-encoded object from the key value pairs given as
|
||||
arguments.
|
||||
|
||||
Time
|
||||
----
|
||||
|
||||
|
@ -212,17 +212,111 @@ static void sql_jget(sqlite3_context *context,
|
||||
sqlite3_result_text(context, result.c_str(), result.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
struct json_agg_context {
|
||||
yajl_gen_t *jac_yajl_gen;
|
||||
};
|
||||
|
||||
static void sql_json_group_object_step(sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv)
|
||||
{
|
||||
if ((argc % 2) == 1) {
|
||||
sqlite3_result_error(
|
||||
context,
|
||||
"Uneven number of arguments to json_group_object(), "
|
||||
"expecting key and value pairs",
|
||||
-1);
|
||||
return;
|
||||
}
|
||||
|
||||
json_agg_context *jac = (json_agg_context *) sqlite3_aggregate_context(
|
||||
context, sizeof(json_agg_context));
|
||||
|
||||
|
||||
if (jac->jac_yajl_gen == NULL) {
|
||||
jac->jac_yajl_gen = yajl_gen_alloc(NULL);
|
||||
yajl_gen_config(jac->jac_yajl_gen, yajl_gen_beautify, false);
|
||||
|
||||
yajl_gen_map_open(jac->jac_yajl_gen);
|
||||
}
|
||||
|
||||
for (int lpc = 0; (lpc + 1) < argc; lpc += 2) {
|
||||
if (sqlite3_value_type(argv[lpc]) == SQLITE_NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const unsigned char *key = sqlite3_value_text(argv[lpc]);
|
||||
|
||||
yajl_gen_string(jac->jac_yajl_gen, key, strlen((const char *) key));
|
||||
|
||||
switch (sqlite3_value_type(argv[lpc + 1])) {
|
||||
case SQLITE_NULL:
|
||||
yajl_gen_null(jac->jac_yajl_gen);
|
||||
break;
|
||||
case SQLITE3_TEXT: {
|
||||
const unsigned char *value = sqlite3_value_text(argv[lpc + 1]);
|
||||
|
||||
yajl_gen_string(jac->jac_yajl_gen,
|
||||
value,
|
||||
strlen((const char *) value));
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
const unsigned char *value = sqlite3_value_text(argv[lpc + 1]);
|
||||
|
||||
yajl_gen_number(jac->jac_yajl_gen,
|
||||
(const char *) value,
|
||||
strlen((const char *) value));
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
double value = sqlite3_value_double(argv[lpc + 1]);
|
||||
|
||||
yajl_gen_double(jac->jac_yajl_gen, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void sql_json_group_object_final(sqlite3_context *context)
|
||||
{
|
||||
json_agg_context *jac = (json_agg_context *) sqlite3_aggregate_context(
|
||||
context, 0);
|
||||
|
||||
if (jac == NULL) {
|
||||
sqlite3_result_text(context, "{}", -1, SQLITE_STATIC);
|
||||
}
|
||||
else {
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
|
||||
yajl_gen_map_close(jac->jac_yajl_gen);
|
||||
yajl_gen_get_buf(jac->jac_yajl_gen, &buf, &len);
|
||||
sqlite3_result_text(context, (const char *) buf, len, SQLITE_TRANSIENT);
|
||||
yajl_gen_free(jac->jac_yajl_gen);
|
||||
}
|
||||
}
|
||||
|
||||
int json_extension_functions(const struct FuncDef **basic_funcs,
|
||||
const struct FuncDefAgg **agg_funcs)
|
||||
{
|
||||
static const struct FuncDef fs_funcs[] = {
|
||||
static const struct FuncDef json_funcs[] = {
|
||||
{ "jget", -1, 0, SQLITE_UTF8, 0, sql_jget },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
*basic_funcs = fs_funcs;
|
||||
*agg_funcs = NULL;
|
||||
static const struct FuncDefAgg json_agg_funcs[] = {
|
||||
{ "json_group_object", -1, 0, 0,
|
||||
sql_json_group_object_step, sql_json_group_object_final, },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
*basic_funcs = json_funcs;
|
||||
*agg_funcs = json_agg_funcs;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -53,3 +53,47 @@ check_output "jget for array does not work" <<EOF
|
||||
Row 0:
|
||||
Column jget('[null, true, 20, 30, 40]', '/0/foo'): (null)
|
||||
EOF
|
||||
|
||||
run_test ./drive_sql "select json_group_object(key) from (select 1 as key)"
|
||||
|
||||
check_error_output "" <<EOF
|
||||
error: sqlite3_exec failed -- Uneven number of arguments to json_group_object(), expecting key and value pairs
|
||||
EOF
|
||||
|
||||
GROUP_SELECT_1=$(cat <<EOF
|
||||
SELECT id, json_group_object(key, value) as stack FROM (
|
||||
SELECT 1 as id, 'key1' as key, 10 as value
|
||||
UNION ALL SELECT 1 as id, 'key2' as key, 20 as value
|
||||
UNION ALL SELECT 1 as id, 'key3' as key, 30 as value)
|
||||
EOF
|
||||
)
|
||||
|
||||
run_test ./drive_sql "$GROUP_SELECT_1"
|
||||
|
||||
check_error_output "" <<EOF
|
||||
EOF
|
||||
|
||||
check_output "json_group_object does not work" <<EOF
|
||||
Row 0:
|
||||
Column id: 1
|
||||
Column stack: {"key1":10,"key2":20,"key3":30}
|
||||
EOF
|
||||
|
||||
GROUP_SELECT_2=$(cat <<EOF
|
||||
SELECT id, json_group_object(key, value) as stack FROM (
|
||||
SELECT 1 as id, 1 as key, 10 as value
|
||||
UNION ALL SELECT 1 as id, 2 as key, null as value
|
||||
UNION ALL SELECT 1 as id, 3 as key, 30.5 as value)
|
||||
EOF
|
||||
)
|
||||
|
||||
run_test ./drive_sql "$GROUP_SELECT_2"
|
||||
|
||||
check_error_output "" <<EOF
|
||||
EOF
|
||||
|
||||
check_output "json_group_object does not work" <<EOF
|
||||
Row 0:
|
||||
Column id: 1
|
||||
Column stack: {"1":10,"2":null,"3":30.5}
|
||||
EOF
|
||||
|
Loading…
Reference in New Issue
Block a user