]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improvements to the accuracy of json_error(). Add the extension SQL
authordrh <>
Fri, 28 Apr 2023 17:38:35 +0000 (17:38 +0000)
committerdrh <>
Fri, 28 Apr 2023 17:38:35 +0000 (17:38 +0000)
functions random_json(SEED) and random_json5(SEED).

FossilOrigin-Name: 8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7

ext/misc/randomjson.c [new file with mode: 0644]
manifest
manifest.uuid
src/json.c

diff --git a/ext/misc/randomjson.c b/ext/misc/randomjson.c
new file mode 100644 (file)
index 0000000..3a6f545
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+** 2023-04-28
+**
+** 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 SQLite extension implements a the random_json(SEED) and
+** random_json5(SEED) functions.  Given a numeric SEED value, these
+** routines generate pseudo-random JSON or JSON5, respectively.  The
+** same value is always generated for the same seed.
+**
+** These SQL functions are intended for testing.  They do not have any
+** practical real-world use, that we know of.
+**
+** COMPILE:
+**
+**     gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c
+**
+** USING FROM THE CLI:
+**
+**     .load ./randomjson
+**     SELECT random_json(1);
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Pseudo-random number generator */
+typedef struct Prng {
+  unsigned int x, y;
+} Prng;
+
+/* Reseed the PRNG */
+static void prngSeed(Prng *p, unsigned int iSeed){
+  p->x = iSeed | 1;
+  p->y = iSeed;
+}
+
+/* Extract a random number */
+static unsigned int prngInt(Prng *p){
+  p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001);
+  p->y = p->y*1103515245 + 12345;
+  return p->x ^ p->y;
+}
+
+static const char *azJsonAtoms[] = {
+  /* JSON                    /* JSON-5 */
+  "0",                       "0",
+  "1",                       "1",
+  "-1",                      "-1",
+  "2",                       "+2",
+  "3",                       "3",
+  "2.5",                     "2.5",
+  "0.75",                    ".75",
+  "-4.0e2",                  "-4.e2",
+  "5.0e-3",                  "+5e-3",
+  "0",                       "0x0",
+  "512",                     "0x200",
+  "256",                     "+0x100",
+  "-2748",                   "-0xabc",
+  "true",                    "true",
+  "false",                   "false",
+  "null",                    "null",
+  "9.0e999",                 "Infinity",
+  "-9.0e999",                "-Infinity",
+  "9.0e999",                 "+Infinity",
+  "null",                    "NaN",
+  "-0.0005123",              "-0.0005123",
+  "4.35e-3",                 "+4.35e-3",
+  "\"gem\\\"hay\"",          "\"gem\\\"hay\"",
+  "\"icy'joy\"",             "'icy\\'joy\'",
+  "\"keylog\"",              "\"key\\\nlog\"",
+  "\"mix\\\\\\tnet\"",       "\"mix\\\\\\tnet\"",
+  "{}",                      "{}",
+  "[]",                      "[]",
+  "[]",                      "[/*empty*/]",
+  "{}",                      "{//empty\n}",
+  "\"ask\"",                 "\"ask\"",
+  "\"bag\"",                 "\"bag\"",
+  "\"can\"",                 "\"can\"",
+  "\"day\"",                 "\"day\"",
+  "\"end\"",                 "'end'",
+  "\"fly\"",                 "\"fly\"",
+  "\"\"",                    "\"\"",
+};
+static const char *azJsonTemplate[] = {
+  /* JSON                                      JSON-5 */
+  "{\"a\":%,\"b\":%,\"c\":%}",                 "{a:%,b:%,c:%}",
+  "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"e\":%}", "{a:%,b:%,c:%,d:%,e:%}",
+  "{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"\":%}",  "{a:%,b:%,c:%,d:%,\"\":%}",
+  "{\"d\":%}",                                 "{d:%}",
+  "{\"eeee\":%, \"ffff\":%}",                  "{eeee:% /*and*/, ffff:%}",
+  "{\"$g\":%,\"_h_\":%}",                      "{$g:%,_h_:%,}",
+  "{\"x\":%,\n  \"y\":%}",                     "{\"x\":%,\n  \"y\":%}",
+  "{\"a b c d\":%,\"e\":%,\"f\":%,\"x\":%,\"y\":%}",
+                                           "{\"a b c d\":%,e:%,f:%,x:%,y:%}",
+  "{\"Z\":%}",                                 "{Z:%,}",
+  "[%]",                                       "[%,]",
+  "[%,%]",                                     "[%,%]",
+  "[%,%,%]",                                   "[%,%,%,]",
+  "[%,%,%,%]",                                 "[%,%,%,%]",
+  "[%,%,%,%,%]",                               "[%,%,%,%,%]",
+};
+
+#define count(X)  (sizeof(X)/sizeof(X[0]))
+
+#define STRSZ 10000
+
+static void jsonExpand(
+  const char *zSrc,
+  char *zDest,
+  Prng *p,
+  int eType,            /* 0 for JSON, 1 for JSON5 */
+  unsigned int r        /* Growth probability 0..1000.  0 means no growth */
+){
+  unsigned int i, j, k;
+  const char *z;
+  size_t n;
+
+  j = 0;
+  if( zSrc==0 ){
+    k = prngInt(p)%(count(azJsonTemplate)/2);
+    k = k*2 + eType;
+    zSrc = azJsonTemplate[k];
+  }
+  if( strlen(zSrc)>=STRSZ/10 ) r = 0;
+  for(i=0; zSrc[i]; i++){
+    if( zSrc[i]!='%' ){
+      if( j<STRSZ ) zDest[j++] = zSrc[i];
+      continue;
+    }
+    if( r==0 || (r<1000 && (prngInt(p)%1000)<=r) ){
+      /* Fill in without values without any new % */
+      k = prngInt(p)%(count(azJsonAtoms)/2);
+      k = k*2 + eType;
+      z = azJsonAtoms[k];
+    }else{
+      /* Add new % terms */
+      k = prngInt(p)%(count(azJsonTemplate)/2);
+      k = k*2 + eType;
+      z = azJsonTemplate[k];
+    }
+    n = strlen(z);
+    if( j+n<STRSZ ){
+      memcpy(&zDest[j], z, n);
+      j += n;
+    }
+  }
+  zDest[STRSZ-1] = 0;
+  if( j<STRSZ ) zDest[j] = 0;
+}
+
+static void randJsonFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  unsigned int iSeed;
+  int eType = *(int*)sqlite3_user_data(context);
+  Prng prng;
+  char z1[STRSZ+1], z2[STRSZ+1];
+
+  iSeed = (unsigned int)sqlite3_value_int(argv[0]);
+  prngSeed(&prng, iSeed);
+  jsonExpand(0, z2, &prng, eType, 1000);
+  jsonExpand(z2, z1, &prng, eType, 1000);
+  jsonExpand(z1, z2, &prng, eType, 100);
+  jsonExpand(z2, z1, &prng, eType, 0);
+  sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_randomjson_init(
+  sqlite3 *db, 
+  char **pzErrMsg, 
+  const sqlite3_api_routines *pApi
+){
+  static int cOne = 1;
+  static int cZero = 0;
+  int rc = SQLITE_OK;
+  SQLITE_EXTENSION_INIT2(pApi);
+  (void)pzErrMsg;  /* Unused parameter */
+  rc = sqlite3_create_function(db, "random_json", 1,
+                   SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+                   &cZero, randJsonFunc, 0, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "random_json5", 1,
+                   SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
+                   &cOne, randJsonFunc, 0, 0);
+  }
+  return rc;
+}
index 77de439258482da1920e99aaf36439a6d453ed2b..c10ebff82d58c3180f976ccbf1299dbeeb193285 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sjson_error(X)\sfunction\sthat\sreturns\sthe\s1-based\scharacter\soffset\sto\nthe\sfirst\ssyntax\serror\sin\sJSON5\sstring\sX,\sor\s0\sif\sthere\sare\sno\serrors.
-D 2023-04-28T14:48:11.699
+C Improvements\sto\sthe\saccuracy\sof\sjson_error().\s\sAdd\sthe\sextension\sSQL\nfunctions\srandom_json(SEED)\sand\srandom_json5(SEED).
+D 2023-04-28T17:38:35.385
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -301,6 +301,7 @@ F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d
 F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
 F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009
+F ext/misc/randomjson.c 7dd13664155319d47b9facc0d8dbf45e13062966a47168e54e3f26d48240d7ea
 F ext/misc/regexp.c f50ab59bfa8934b7ed98de069d2c74c187f2ef523fb09e85f8840f6459a90942
 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
 F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
@@ -593,7 +594,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
 F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7
 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4
-F src/json.c b532d42d310e570d7f620a692a26f21dc7306063b866088c4e43c647a17118b3
+F src/json.c 03eba427c0e8700db2895b785642f4dba8440020658d8eef97ec4fadf3836cdf
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
 F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136
 F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d
@@ -2066,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 629db09fceb7bf37561b52ccee06ebf4df261291e9a8ffcca82b243f6db5ff07
-R cda10fe16274848e02e4d5ea3713e4b9
+P 901ad995d5a722ca2672516205ff488e9acd703a828ca5fc43f11fca5f2af120
+R c710d125f016d21291562e41606e0d2f
 U drh
-Z 80f17384e6d82e3c8657040553f2124e
+Z 98bfef108d9db21095967c25b17439f3
 # Remove this line to create a well-formed Fossil manifest.
index 0b6e554ed2ad4dd86f9bbe5229ce8e22d5c44623..2d0bc4ddafea5181384534a048697d399a2e1a4c 100644 (file)
@@ -1 +1 @@
-901ad995d5a722ca2672516205ff488e9acd703a828ca5fc43f11fca5f2af120
\ No newline at end of file
+8d09dc1c45a8026b94f70273d064e47939f30cadedc17548b5a26ba054a8d3a7
\ No newline at end of file
index d1a057b48eb6162913df50f8cfffc4bc4a8beef1..f76d7dc5a768fa246a5133e2630f76a9803973ea 100644 (file)
@@ -1093,7 +1093,7 @@ json_parse_restart:
           pParse->has5 = 1;
           x = k;
         }else{
-          pParse->iErr = j;
+          if( x!=-1 ) pParse->iErr = j;
           return -1;
         }
       }
@@ -1114,7 +1114,7 @@ json_parse_restart:
         }
         x = jsonParseValue(pParse, j);
         if( x!=(-5) ){
-          pParse->iErr = j;
+          if( x!=(-1) ) pParse->iErr = j;
           return -1;
         }
         j = pParse->iErr+1;
@@ -1123,7 +1123,7 @@ json_parse_restart:
       x = jsonParseValue(pParse, j);
       pParse->iDepth--;
       if( x<=0 ){
-        pParse->iErr = j;
+        if( x!=(-1) ) pParse->iErr = j;
         return -1;
       }
       j = x;
@@ -1174,7 +1174,7 @@ json_parse_restart:
           if( pParse->nNode!=(u32)iThis+1 ) pParse->has5 = 1;
           break;
         }
-        pParse->iErr = j;
+        if( x!=(-1) ) pParse->iErr = j;
         return -1;
       }
       j = x;