[sql] add json_group_object aggregate function

pull/290/head
Timothy Stack 9 years ago
parent 4eb797ce9a
commit 62075fb340

@ -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…
Cancel
Save