aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2018-07-05 20:05:29 +0000
committerdrh <drh@noemail.net>2018-07-05 20:05:29 +0000
commit8be47a7e86bc6f40999cf2cdd98a9af1d1c99aef (patch)
tree48b50168a9cfff716b842848ad44ab1e4890185e
parent7a606e1ab2daa216834a557da342fa692cb0bc76 (diff)
downloadsqlite-8be47a7e86bc6f40999cf2cdd98a9af1d1c99aef.tar.gz
sqlite-8be47a7e86bc6f40999cf2cdd98a9af1d1c99aef.zip
Get the json_group_array() and json_group_object() SQL functions working
as window functions. FossilOrigin-Name: 916cdc83f5a45e0b6f61c52ff5fde70d54bcd0dfaa4a32f9ac709fe0ddbb480b
-rw-r--r--ext/misc/json1.c74
-rw-r--r--manifest18
-rw-r--r--manifest.uuid2
-rw-r--r--src/vdbe.c2
-rw-r--r--test/json103.test20
5 files changed, 97 insertions, 19 deletions
diff --git a/ext/misc/json1.c b/ext/misc/json1.c
index 735e3c94a..398dd465c 100644
--- a/ext/misc/json1.c
+++ b/ext/misc/json1.c
@@ -1802,7 +1802,7 @@ static void jsonArrayStep(
jsonAppendValue(pStr, argv[0]);
}
}
-static void jsonArrayFinal(sqlite3_context *ctx){
+static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
@@ -1811,16 +1811,60 @@ static void jsonArrayFinal(sqlite3_context *ctx){
if( pStr->bErr ){
if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
- }else{
+ }else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
pStr->bStatic = 1;
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed, SQLITE_TRANSIENT);
+ pStr->nUsed--;
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
+static void jsonArrayValue(sqlite3_context *ctx){
+ jsonArrayCompute(ctx, 0);
+}
+static void jsonArrayFinal(sqlite3_context *ctx){
+ jsonArrayCompute(ctx, 1);
+}
+
+#ifndef SQLITE_OMIT_WINDOWFUNC
+/*
+** This method works for both json_group_array() and json_group_object().
+** It works by removing the first element of the group by searching forward
+** to the first comma (",") that is not within a string and deleting all
+** text through that comma.
+*/
+static void jsonGroupInverse(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ int inStr = 0;
+ char *z;
+ JsonString *pStr;
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+ if( !pStr ) return;
+ z = pStr->zBuf;
+ for(i=1; z[i]!=',' || inStr; i++){
+ assert( i<pStr->nUsed );
+ if( z[i]=='"' ){
+ inStr = !inStr;
+ }else if( z[i]=='\\' ){
+ i++;
+ }
+ }
+ pStr->nUsed -= i;
+ memmove(&z[1], &z[i+1], pStr->nUsed-1);
+}
+#else
+# define jsonGroupInverse 0
+#endif
+
/*
** json_group_obj(NAME,VALUE)
@@ -1852,7 +1896,7 @@ static void jsonObjectStep(
jsonAppendValue(pStr, argv[1]);
}
}
-static void jsonObjectFinal(sqlite3_context *ctx){
+static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
@@ -1860,16 +1904,26 @@ static void jsonObjectFinal(sqlite3_context *ctx){
if( pStr->bErr ){
if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
- }else{
+ }else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
pStr->bStatic = 1;
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed, SQLITE_TRANSIENT);
+ pStr->nUsed--;
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
}
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
+static void jsonObjectValue(sqlite3_context *ctx){
+ jsonObjectCompute(ctx, 0);
+}
+static void jsonObjectFinal(sqlite3_context *ctx){
+ jsonObjectCompute(ctx, 1);
+}
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -2377,9 +2431,12 @@ int sqlite3Json1Init(sqlite3 *db){
int nArg;
void (*xStep)(sqlite3_context*,int,sqlite3_value**);
void (*xFinal)(sqlite3_context*);
+ void (*xValue)(sqlite3_context*);
} aAgg[] = {
- { "json_group_array", 1, jsonArrayStep, jsonArrayFinal },
- { "json_group_object", 2, jsonObjectStep, jsonObjectFinal },
+ { "json_group_array", 1,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue },
+ { "json_group_object", 2,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue },
};
#ifndef SQLITE_OMIT_VIRTUALTABLE
static const struct {
@@ -2397,9 +2454,10 @@ int sqlite3Json1Init(sqlite3 *db){
aFunc[i].xFunc, 0, 0);
}
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_function(db, aAgg[i].zName, aAgg[i].nArg,
+ rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
- 0, aAgg[i].xStep, aAgg[i].xFinal);
+ aAgg[i].xStep, aAgg[i].xFinal,
+ aAgg[i].xValue, jsonGroupInverse, 0);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
diff --git a/manifest b/manifest
index c19ed5b7c..cd0c49c7e 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Return\san\serror\sif\sa\s"RANGE"\swindow-frame\suses\s"<expr>\sPRECEDING"\sor\s"<expr>\nFOLLOWING".
-D 2018-07-05T18:34:53.220
+C Get\sthe\sjson_group_array()\sand\sjson_group_object()\sSQL\sfunctions\sworking\nas\swindow\sfunctions.
+D 2018-07-05T20:05:29.904
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
@@ -281,7 +281,7 @@ F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0
F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
-F ext/misc/json1.c 02f5f0d9c131df79f4ee168d2b426f0f0d273b7771fc0bb5293c4e7692d9a2ee
+F ext/misc/json1.c 8dca18a64560481f566fe03e26b60dfc95b665c709be86934d261ac21d6f1c2f
F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567
F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
@@ -566,7 +566,7 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855
-F src/vdbe.c 37b942ccdd224dd51bd80312b8d236f27c7c728e76c3f425c50e9d8ecff3b7b4
+F src/vdbe.c ff748307932fac5a5a983a20a4d8805aff7b0f310ca58cff4051df96d34a4dc8
F src/vdbe.h 847735b3e083285aa225d2bf1fe97107a0a2b8942c80d643cdb764990705820d
F src/vdbeInt.h 6b5cbd338dc9aaad343fb11b455e47463b0474d9f79710f50e647158941146e0
F src/vdbeapi.c e35f813312b0f6e8e2835b357f121e0d1da6d3013bac3e367fe4efd518e7a2ec
@@ -1028,7 +1028,7 @@ F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd7
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
F test/json101.test b40a9f5395d8e669b0bc3eb550ad2ae9e5ada01fbce23c446c2a30a305a6d575
F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb28487bc1
-F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
+F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f67e3b
F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/kvtest.c 94da54bb66aae7a54e47cf7e4ea4acecc0f217560f79ad3abfcc0361d6d557ba
@@ -1745,7 +1745,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 73391283101288251fe5d030ba12bcdabe8a781d41086afe3564c58c05d0fd5c
-R e2d8bd888d8359c89112b96b5805f3f9
-U dan
-Z 61151c44890df0915ca49d8561fa462d
+P 786c87ba4150509e141baab32c51e64bbd3ce323735e0f47d65ed54d14215bc4
+R d772fd50a83277dafc7b2d62ea89dd92
+U drh
+Z fcfb43d365d0b34e61224fdf77a12270
diff --git a/manifest.uuid b/manifest.uuid
index f3f2a9156..c14381624 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-786c87ba4150509e141baab32c51e64bbd3ce323735e0f47d65ed54d14215bc4 \ No newline at end of file
+916cdc83f5a45e0b6f61c52ff5fde70d54bcd0dfaa4a32f9ac709fe0ddbb480b \ No newline at end of file
diff --git a/src/vdbe.c b/src/vdbe.c
index 6eb5f9502..2b449b946 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -6411,7 +6411,7 @@ case OP_AggStep: {
** P4 is a pointer to the FuncDef for this function. The P2
** argument is not used by this opcode. It is only there to disambiguate
** functions that can take varying numbers of arguments. The
-** P4 argument is only needed for the degenerate case where
+** P4 argument is only needed for the case where
** the step function was not previously called.
*/
case OP_AggFinal: {
diff --git a/test/json103.test b/test/json103.test
index d7d12e337..35580ce4e 100644
--- a/test/json103.test
+++ b/test/json103.test
@@ -75,4 +75,24 @@ do_execsql_test json103-300 {
FROM t1;
} {{[1,"abc"]} {[{"x":1},{"x":"abc"}]}}
+# json_group_array() and json_group_object() work as window functions.
+#
+ifcapable windowfunc {
+ do_execsql_test json103-400 {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES
+ (1),
+ ('a,b'),
+ (3),
+ ('x"y'),
+ (5),
+ (6),
+ (7);
+ SELECT json_group_array(x) OVER (ROWS 2 PRECEDING) FROM t4;
+ } {{[1]} {[1,"a,b"]} {[1,"a,b",3]} {["a,b",3,"x\"y"]} {[3,"x\"y",5]} {["x\"y",5,6]} {[5,6,7]}}
+ do_execsql_test json103-410 {
+ SELECT json_group_object(rowid, x) OVER (ROWS 2 PRECEDING) FROM t4;
+ } {{{"1":1}} {{"1":1,"2":"a,b"}} {{"1":1,"2":"a,b","3":3}} {{"2":"a,b","3":3,"4":"x\"y"}} {{"3":3,"4":"x\"y","5":5}} {{"4":"x\"y","5":5,"6":6}} {{"5":5,"6":6,"7":7}}}
+}
+
finish_test