]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the json_group_array() and json_group_object() aggregate functions to
authordrh <drh@noemail.net>
Wed, 30 Dec 2015 01:07:02 +0000 (01:07 +0000)
committerdrh <drh@noemail.net>
Wed, 30 Dec 2015 01:07:02 +0000 (01:07 +0000)
the JSON1 extension.

FossilOrigin-Name: 7f386a9332237100a345035ca213327e21d95855

ext/misc/json1.c
manifest
manifest.uuid
test/json103.test [new file with mode: 0644]

index 61d013ea4b639fe078053c93cb9b7481f7277d69..0bd0baff122d81c28b1e41516f47d618fef8de70 100644 (file)
@@ -1181,7 +1181,7 @@ static void jsonTest1Func(
 #endif /* SQLITE_DEBUG */
 
 /****************************************************************************
-** SQL function implementations
+** Scalar SQL function implementations
 ****************************************************************************/
 
 /*
@@ -1514,6 +1514,102 @@ static void jsonValidFunc(
   sqlite3_result_int(ctx, rc);
 }
 
+
+/****************************************************************************
+** Aggregate SQL function implementations
+****************************************************************************/
+/*
+** json_group_array(VALUE)
+**
+** Return a JSON array composed of all values in the aggregate.
+*/
+static void jsonArrayStep(
+  sqlite3_context *ctx,
+  int argc,
+  sqlite3_value **argv
+){
+  JsonString *pStr;
+  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+  if( pStr ){
+    if( pStr->zBuf==0 ){
+      jsonInit(pStr, ctx);
+      jsonAppendChar(pStr, '[');
+    }else{
+      jsonAppendChar(pStr, ',');
+      pStr->pCtx = ctx;
+    }
+    jsonAppendValue(pStr, argv[0]);
+  }
+}
+static void jsonArrayFinal(sqlite3_context *ctx){
+  JsonString *pStr;
+  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+  if( pStr ){
+    pStr->pCtx = ctx;
+    jsonAppendChar(pStr, ']');
+    if( pStr->bErr ){
+      sqlite3_result_error_nomem(ctx);
+      if( !pStr->bStatic ) sqlite3_free(pStr->zBuf);
+    }else{
+      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+      pStr->bStatic = 1;
+    }
+  }else{
+    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
+  }
+  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+/*
+** json_group_obj(NAME,VALUE)
+**
+** Return a JSON object composed of all names and values in the aggregate.
+*/
+static void jsonObjectStep(
+  sqlite3_context *ctx,
+  int argc,
+  sqlite3_value **argv
+){
+  JsonString *pStr;
+  const char *z;
+  u32 n;
+  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+  if( pStr ){
+    if( pStr->zBuf==0 ){
+      jsonInit(pStr, ctx);
+      jsonAppendChar(pStr, '{');
+    }else{
+      jsonAppendChar(pStr, ',');
+      pStr->pCtx = ctx;
+    }
+    z = (const char*)sqlite3_value_text(argv[0]);
+    n = (u32)sqlite3_value_bytes(argv[0]);
+    jsonAppendString(pStr, z, n);
+    jsonAppendChar(pStr, ':');
+    jsonAppendValue(pStr, argv[1]);
+  }
+}
+static void jsonObjectFinal(sqlite3_context *ctx){
+  JsonString *pStr;
+  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+  if( pStr ){
+    jsonAppendChar(pStr, '}');
+    if( pStr->bErr ){
+      sqlite3_result_error_nomem(ctx);
+      if( !pStr->bStatic ) sqlite3_free(pStr->zBuf);
+    }else{
+      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+      pStr->bStatic = 1;
+    }
+  }else{
+    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
+  }
+  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
 #ifndef SQLITE_OMIT_VIRTUALTABLE
 /****************************************************************************
 ** The json_each virtual table
@@ -2012,6 +2108,15 @@ int sqlite3Json1Init(sqlite3 *db){
     { "json_test1",           1, 0,   jsonTest1Func         },
 #endif
   };
+  static const struct {
+     const char *zName;
+     int nArg;
+     void (*xStep)(sqlite3_context*,int,sqlite3_value**);
+     void (*xFinal)(sqlite3_context*);
+  } aAgg[] = {
+    { "json_group_array",     1,   jsonArrayStep,   jsonArrayFinal  },
+    { "json_group_object",    2,   jsonObjectStep,  jsonObjectFinal },
+  };
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   static const struct {
      const char *zName;
@@ -2027,6 +2132,11 @@ int sqlite3Json1Init(sqlite3 *db){
                                  (void*)&aFunc[i].flag,
                                  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,
+                                 SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
+                                 0, aAgg[i].xStep, aAgg[i].xFinal);
+  }
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
     rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
index 0bcc78c6496892d6843811adf9ed1abb01dcdfff..20c9426af8b093ab2c4ed8daee03ff79bfe53159 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Avoid\sa\sharmless\scompiler\swarning\son\ssystems\swhere\sthe\sbyteorder\scannot\nbe\sdetermined\sat\scompile-time.
-D 2015-12-24T14:53:27.528
+C Add\sthe\sjson_group_array()\sand\sjson_group_object()\saggregate\sfunctions\sto\nthe\sJSON1\sextension.
+D 2015-12-30T01:07:02.009
 F Makefile.in 28bcd6149e050dff35d4dcfd97e890cd387a499d
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 5fff077fcc46de7714ed6eebb6159a4c00eab751
@@ -191,7 +191,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
 F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
-F ext/misc/json1.c 4f45afd9dbcd6feca8c528251efbb7fc09299a09
+F ext/misc/json1.c b7ed42db00f7429c0f0b5068209c95c41b531596
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
@@ -820,6 +820,7 @@ F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
 F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
 F test/json101.test f0178422b3a2418f423fd0d3caf3571c8d1b9863
 F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a
+F test/json103.test 923b288a0610ec86c0951778f7db19cbcca36ad1
 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
 F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
@@ -1405,7 +1406,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 5d44d4a6cf5c6b983cbd846d9bc34251df8f4bc5
-R c0120dd70eca82393527f5b4b745caa6
+P 7c7b7f26306b6aa6ff35b871ad756f43f5db9838
+R 9a71d4f0e52af46677f6902fe927abd2
 U drh
-Z 6af8ad4397eb3ff04028cb96b6439a6e
+Z d3d1e4137b6ba95a64ac245c7769bfd5
index 6d6b0b730251f35efc8d90080ea6dfd332dd97fb..9a8ba9a337346a29a647292cbea6bda0cbbbf680 100644 (file)
@@ -1 +1 @@
-7c7b7f26306b6aa6ff35b871ad756f43f5db9838
\ No newline at end of file
+7f386a9332237100a345035ca213327e21d95855
\ No newline at end of file
diff --git a/test/json103.test b/test/json103.test
new file mode 100644 (file)
index 0000000..0f0241e
--- /dev/null
@@ -0,0 +1,65 @@
+# 2015-12-30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements tests for JSON aggregate SQL functions
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !json1 {
+  finish_test
+  return
+}
+
+do_execsql_test json103-100 {
+  CREATE TABLE t1(a,b,c);
+  WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<100)
+  INSERT INTO t1(a,b,c) SELECT x, x%3, printf('n%d',x)  FROM c;
+  UPDATE t1 SET a='orange' WHERE rowid=39;
+  UPDATE t1 SET a=32.5 WHERE rowid=31;
+  UPDATE t1 SET a=x'303132' WHERE rowid=29;
+  UPDATE t1 SET a=NULL WHERE rowid=37;
+  SELECT json_group_array(a) FROM t1 WHERE a<0 AND typeof(a)!='blob';
+} {{[]}}
+do_catchsql_test json103-101 {
+  SELECT json_group_array(a) FROM t1;
+} {1 {JSON cannot hold BLOB values}}
+do_execsql_test json103-110 {
+  SELECT json_group_array(a) FROM t1
+   WHERE rowid BETWEEN 31 AND 39;
+} {{[32.5,32,33,34,35,36,null,38,"orange"]}}
+do_execsql_test json103-111 {
+  SELECT json_array_length(json_group_array(a)) FROM t1
+   WHERE rowid BETWEEN 31 AND 39;
+} {9}
+do_execsql_test json103-120 {
+  SELECT b, json_group_array(a) FROM t1 WHERE rowid<10 GROUP BY b ORDER BY b;
+} {0 {[3,6,9]} 1 {[1,4,7]} 2 {[2,5,8]}}
+
+do_execsql_test json103-200 {
+  SELECT json_group_object(c,a) FROM t1 WHERE a<0 AND typeof(a)!='blob';
+} {{{}}}
+do_catchsql_test json103-201 {
+  SELECT json_group_object(c,a) FROM t1;
+} {1 {JSON cannot hold BLOB values}}
+
+do_execsql_test json103-210 {
+  SELECT json_group_object(c,a) FROM t1
+   WHERE rowid BETWEEN 31 AND 39 AND rowid%2==1;
+} {{{"n31":32.5,"n33":33,"n35":35,"n37":null,"n39":"orange"}}}
+do_execsql_test json103-220 {
+  SELECT b, json_group_object(c,a) FROM t1
+   WHERE rowid<7 GROUP BY b ORDER BY b;
+} {0 {{"n3":3,"n6":6}} 1 {{"n1":1,"n4":4}} 2 {{"n2":2,"n5":5}}}
+
+
+
+finish_test