From 5289c51050a9cc4860dca4afcbb276710b7ff8d6 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 29 Jun 2020 18:22:21 +0000 Subject: [PATCH 001/347] Change things so that if SQLITE_ENABLE_SETLK_TIMEOUT is defined as 2 instead of 1, all blocking locks are taken for a single millisecond and the default busy-handler invoked as normal. FossilOrigin-Name: ac381e6eb3c9284e65f7aad66d21bee1bca9ef4123684ccaf069b8a6d157a56d --- manifest | 14 +++++++++----- manifest.uuid | 2 +- src/os_unix.c | 8 +++++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 689c6dab71..11f78d1b6f 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ B 7a876209a678a34c198b54ceef9e3c041f128a14dc73357f6a57cadadaa6cf7b -C Add\stest\sscript\sto\sverify\sbusy-timeouts\sare\sworking\sfor\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds. -D 2020-06-29T17:52:53.862 +C Change\sthings\sso\sthat\sif\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined\sas\s2\sinstead\sof\s1,\sall\sblocking\slocks\sare\staken\sfor\sa\ssingle\smillisecond\sand\sthe\sdefault\sbusy-handler\sinvoked\sas\snormal. +D 2020-06-29T18:22:21.402 F Makefile.in 19374a5db06c3199ec1bab71ab74a103d8abf21053c05e9389255dc58083f806 F Makefile.msc 48f5a3fc32672c09ad73795749f6253e406a31526935fbbffd8f021108d54574 F autoconf/Makefile.am a8d1d24affe52ebf8d7ddcf91aa973fa0316618ab95bb68c87cabf8faf527dc8 @@ -12,6 +12,7 @@ F ext/misc/ieee754.c bb6bd8e9eeeda5a7ac82839fcab5c0b8156b0532165387cc5458a97f600 F main.mk b1cd0bc6aedad7ebb667b7f74f835f932f60ee33be2a5c3051fd93eb465f5c75 F src/build.c ba1bbe563a3dc02d5fed20537603181e5289c13ea30ae5e775f552e7557adbfa F src/expr.c a3ab84399b3415f66d2d0c25f5bcd98ef465c0c07ea1f19bf2a418b1c8fcad74 +F src/os_unix.c 93ed45be97dbd1dd081e402fdeef3f0c2a4e513b246a552bacdf7b6809e0e52a F src/select.c bc25f48e49eca122c16c247e7d4697241156e7c32c735b219be667657aa017ef F src/shell.c.in d663152487d4bfddea0f6d21ebc2ed51575d22657a02c6828afd344bbd4651af F src/test1.c fe56c4bcaa2685ca9aa25d817a0ee9345e189aff4a5a71a3d8ba946c7776feb8 @@ -27,7 +28,10 @@ F tool/mksqlite3c.tcl f4ef476510eca4124c874a72029f1e01bc54a896b1724e8f9eef0d8bfa F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564 F tool/speed-check.sh 615cbdf50f1409ef3bbf9f682e396df80f49d97ed93ed3e61c8e91fae6afde58 -P 1b426603f05033bcee0331c6f664cd5ed2ebf8f5d4cde8c6673c7a699ff53bb1 -R 6c7789a7098efb29eff36caf8aa4c6fc +P ada43e7c490bf72a50ee84e1db994e149744b2a943260449076b83d1874813b2 +R 93e888407e9721413a70c252713da7cc +T *branch * wal-setlk-changes +T *sym-wal-setlk-changes * +T -sym-trunk * U dan -Z 112b18c988addaa054b3697edfd4eb11 +Z 013fbd5cd0583cc6a6abd723420eba48 diff --git a/manifest.uuid b/manifest.uuid index 36e1ca4ac3..bb90647dc0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ada43e7c490bf72a50ee84e1db994e149744b2a943260449076b83d1874813b2 \ No newline at end of file +ac381e6eb3c9284e65f7aad66d21bee1bca9ef4123684ccaf069b8a6d157a56d \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index 3571f3f2c2..01852afd3b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3997,7 +3997,13 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT case SQLITE_FCNTL_LOCK_TIMEOUT: { int iOld = pFile->iBusyTimeout; +#if SQLITE_ENABLE_SETLK_TIMEOUT==1 pFile->iBusyTimeout = *(int*)pArg; +#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 + pFile->iBusyTimeout = !!(*(int*)pArg); +#else +# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" +#endif *(int*)pArg = iOld; return SQLITE_OK; } @@ -4325,7 +4331,7 @@ static int unixShmSystemLock( f.l_len = n; res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); if( res==-1 ){ -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1 rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); #else rc = SQLITE_BUSY; From 4ffaa7c5dedbc86b5736b139bc0465524ab3db51 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 29 Jun 2020 19:58:26 +0000 Subject: [PATCH 002/347] Passive checkpoints do not use the busy-handler. So, in order to minimize visible changes for legacy applications, do not enable blocking locks for passive checkpoints. FossilOrigin-Name: 9c2b4bdd03716bf492ba85198717f3084ebf187bdb068893bd1ff8662362df89 --- manifest | 14 ++++++-------- manifest.uuid | 2 +- src/wal.c | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 11f78d1b6f..707e72749a 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ B 7a876209a678a34c198b54ceef9e3c041f128a14dc73357f6a57cadadaa6cf7b -C Change\sthings\sso\sthat\sif\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined\sas\s2\sinstead\sof\s1,\sall\sblocking\slocks\sare\staken\sfor\sa\ssingle\smillisecond\sand\sthe\sdefault\sbusy-handler\sinvoked\sas\snormal. -D 2020-06-29T18:22:21.402 +C Passive\scheckpoints\sdo\snot\suse\sthe\sbusy-handler.\sSo,\sin\sorder\sto\sminimize\svisible\schanges\sfor\slegacy\sapplications,\sdo\snot\senable\sblocking\slocks\sfor\spassive\scheckpoints. +D 2020-06-29T19:58:26.005 F Makefile.in 19374a5db06c3199ec1bab71ab74a103d8abf21053c05e9389255dc58083f806 F Makefile.msc 48f5a3fc32672c09ad73795749f6253e406a31526935fbbffd8f021108d54574 F autoconf/Makefile.am a8d1d24affe52ebf8d7ddcf91aa973fa0316618ab95bb68c87cabf8faf527dc8 @@ -17,6 +17,7 @@ F src/select.c bc25f48e49eca122c16c247e7d4697241156e7c32c735b219be667657aa017ef F src/shell.c.in d663152487d4bfddea0f6d21ebc2ed51575d22657a02c6828afd344bbd4651af F src/test1.c fe56c4bcaa2685ca9aa25d817a0ee9345e189aff4a5a71a3d8ba946c7776feb8 F src/vdbeapi.c c1a9004ac554d8d48794d2ce5f80397f8e419fd28643a543cc1e004c7713c3ef +F src/wal.c 53e6b90973d9ef8b4bedd48430d22bf5c7c320e73b9e42e84518cd92200613d1 F test/busy2.test 5a449cd1bd7616c6ce709484d3e2a419a151b75e87ec5d2c7cb26e05a15dbd7b F test/decimal.test 12739a01bdba4c4d79f95b323e6b67b9fad1ab6ffb56116bd2b9c81a5b19e1d9 F test/fts3corrupt4.test 99a3017da1f43c8dbecd1b053029ade08dfa51b94ca043abffe5d32f21cc5736 @@ -28,10 +29,7 @@ F tool/mksqlite3c.tcl f4ef476510eca4124c874a72029f1e01bc54a896b1724e8f9eef0d8bfa F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564 F tool/speed-check.sh 615cbdf50f1409ef3bbf9f682e396df80f49d97ed93ed3e61c8e91fae6afde58 -P ada43e7c490bf72a50ee84e1db994e149744b2a943260449076b83d1874813b2 -R 93e888407e9721413a70c252713da7cc -T *branch * wal-setlk-changes -T *sym-wal-setlk-changes * -T -sym-trunk * +P ac381e6eb3c9284e65f7aad66d21bee1bca9ef4123684ccaf069b8a6d157a56d +R 753e39341ac04afeb02e6da18d0f5274 U dan -Z 013fbd5cd0583cc6a6abd723420eba48 +Z d19f6be8ed2e8d92d9c0542836650d0b diff --git a/manifest.uuid b/manifest.uuid index bb90647dc0..4ca8338e4e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac381e6eb3c9284e65f7aad66d21bee1bca9ef4123684ccaf069b8a6d157a56d \ No newline at end of file +9c2b4bdd03716bf492ba85198717f3084ebf187bdb068893bd1ff8662362df89 \ No newline at end of file diff --git a/src/wal.c b/src/wal.c index c6d4476e7b..910a2a6e79 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3701,10 +3701,9 @@ int sqlite3WalCheckpoint( if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - /* Enable blocking locks, if possible. If blocking locks are successfully - ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ + /* Enable blocking locks, if possible. */ sqlite3WalDb(pWal, db); - (void)walEnableBlocking(pWal); + if( xBusy2 ) (void)walEnableBlocking(pWal); /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. @@ -3746,7 +3745,7 @@ int sqlite3WalCheckpoint( if( rc==SQLITE_OK ){ walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); - (void)walEnableBlocking(pWal); + if( xBusy2 ) (void)walEnableBlocking(pWal); if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); } From 6831cad03bc8c477eb10842c0a9bdaa6f372211c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 21 Sep 2023 17:51:39 +0000 Subject: [PATCH 003/347] Initial development code for an experimental binary BLOB encoding for JSON. FossilOrigin-Name: 8131b3c272f47db2618886046a9713285ce120cb87d721484ee7444273290681 --- manifest | 17 +- manifest.uuid | 2 +- src/json.c | 670 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 681 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index fc2752435a..78b4dedcd2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sextra\stests\sfor\sjava\sFts5ExtensionApi\sAPI. -D 2023-09-18T20:42:06.838 +C Initial\sdevelopment\scode\sfor\san\sexperimental\sbinary\sBLOB\sencoding\sfor\sJSON. +D 2023-09-21T17:51:39.740 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 51141f1c09ccb177057e5813e6302a5e32e5ba88cc4a756318a35081010fc6df +F src/json.c aa191fb04ff8b3857c2e05a51b347a23992807a2c12cda5247286ba4768075e7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c923893f3604b278277de1bb919ef713bf7a4296b7ff71451cfe19bc2ff03190 -R dce10d8ae94249d41d42f7dff2440548 -U dan -Z 7d6c76f5fce3d6a1618c1597ed085ea5 +P f9d62b853ce8bfbfdc9f137e984e7a1b51d70e88c38b136b4fad1e8ae6ee8913 +R 707eaf997af5bd7aea243a294bfb17a0 +T *branch * jsonb +T *sym-jsonb * +T -sym-trunk * +U drh +Z b6e8348ea42422278215287176b59cff # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 81b26d4090..37c7ebe312 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9d62b853ce8bfbfdc9f137e984e7a1b51d70e88c38b136b4fad1e8ae6ee8913 \ No newline at end of file +8131b3c272f47db2618886046a9713285ce120cb87d721484ee7444273290681 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 253fce9f49..c7c6becbd4 100644 --- a/src/json.c +++ b/src/json.c @@ -122,6 +122,19 @@ struct JsonCleanup { #define JSON_ARRAY 7 #define JSON_OBJECT 8 +/* JSON BLOB node types +*/ +#define JSONB_NULL 0 +#define JSONB_TRUE 1 +#define JSONB_FALSE 2 +#define JSONB_NUMBER 3 /* VarInt size + text */ +#define JSONB_NUMBER5 4 /* VarInt size + text (JSON5 formatted) */ +#define JSONB_TEXT 5 /* VarInt size + text (Raw and unescaped) */ +#define JSONB_TEXTJ 6 /* VarInt size + text (JSON formatted) */ +#define JSONB_TEXT5 7 /* VarInt size + text (JSON5 formatted) */ +#define JSONB_ARRAY 8 /* u32 size + content */ +#define JSONB_OBJECT 9 /* u32 size + content */ + /* The "subtype" set for JSON values */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ @@ -210,6 +223,10 @@ struct JsonParse { u32 iErr; /* Error location in zJson[] */ u32 iSubst; /* Last JSON_SUBST entry in aNode[] */ u32 iHold; /* Age of this entry in the cache for LRU replacement */ + /* Binary format */ + u32 nBlob; /* Bytes of aBlob[] actually used */ + u32 nBlobAlloc; /* Bytes allocated to aBlob[] */ + u8 *aBlob; /* BLOB representation of zJson */ }; /* @@ -1804,6 +1821,582 @@ static int jsonParse( return 0; } +/* +** Expand pParse->aBlob so that it holds at least N bytes. +** +** Return the number of errors. +*/ +static int jsonBlobExpand(JsonParse *pParse, u32 N){ + u8 *aNew; + u32 t; + if( N<=pParse->nBlobAlloc ) return 0; + if( pParse->nBlobAlloc==0 ){ + t = 100; + }else{ + t = pParse->nBlobAlloc*2; + } + if( taBlob, t ); + if( aNew==0 ){ pParse->oom = 1; return 1; } + pParse->aBlob = aNew; + pParse->nBlobAlloc = t; + return 0; +} + +/* Expand pParse->aBlob and append N bytes. +** +** Return the number of errors. +*/ +static SQLITE_NOINLINE int jsonBlobExpandAndAppend( + JsonParse *pParse, + const u8 *aData, + u32 N +){ + if( jsonBlobExpand(pParse, pParse->nBlob+N) ) return 1; + memcpy(&pParse->aBlob[pParse->nBlob], aData, N); + pParse->nBlob += N; + return 0; +} + +/* Append a single character. Return 1 if an error occurs. +*/ +static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ + if( pParse->nBlob >= pParse->nBlobAlloc ){ + return jsonBlobExpandAndAppend(pParse, &c, 1); + } + pParse->aBlob[pParse->nBlob++] = c; + return 0; +} + +/* Append bytes. Return 1 if an error occurs. +*/ +static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){ + if( pParse->nBlob+N > pParse->nBlobAlloc ){ + return jsonBlobExpandAndAppend(pParse, aData, N); + } + memcpy(&pParse->aBlob[pParse->nBlob], aData, N); + pParse->nBlob += N; + return 0; +} + +/* Append a u32. Return 1 if an error occurs. +*/ +static int jsonBlobAppendU32(JsonParse *pParse, u32 x){ + u8 a[4]; + a[3] = x & 0xff; + x >>= 8; + a[2] = x & 0xff; + x >>= 8; + a[1] = x & 0xff; + x >>= 8; + a[0] = x & 0xff; + return jsonBlobAppendNBytes(pParse, a, 4); +} + +/* Write a u32 and offset i. +*/ +static int jsonBlobWriteU32(JsonParse *pParse, u32 x, u32 i){ + u8 *a = &pParse->aBlob[i]; + a[3] = x & 0xff; + x >>= 8; + a[2] = x & 0xff; + x >>= 8; + a[1] = x & 0xff; + x >>= 8; + a[0] = x & 0xff; + return 0; +} + +/* Append a VarInt. Return 1 if an error occurs. +*/ +static int jsonBlobAppendVarint(JsonParse *pParse, u32 x){ + u8 a[5]; + if( x<=0x7f ) return jsonBlobAppendOneByte(pParse, x & 0x7f); + a[0] = 0x80 | (x & 0x7f); + x >>= 7; + a[1] = x & 0x7f; + x >>= 7; + if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 2); + a[1] |= 0x80; + a[2] = x & 0x7f; + x >>= 7; + if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 3); + a[2] |= 0x80; + a[3] = x & 0x7f; + x >>= 7; + if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 4); + a[3] |= 0x80; + a[4] = x & 0x7f; + return jsonBlobAppendNBytes(pParse, a, 5); +} + +/* +** Parse a single JSON text value which begins at pParse->zJson[i] into +** its equivalent BLOB representation in pParse->aBlob[]. The parse is +** appended to pParse->aBlob[] beginning at pParse->nBlob. The size of +** pParse->aBlob[] is increased as necessary. +** +** Return the index of the first character past the end of the value parsed, +** or one of the following special result codes: +** +** 0 End of input +** -1 Syntax error +** -2 '}' seen +** -3 ']' seen +** -4 ',' seen +** -5 ':' seen +*/ +static int jsonParseValueB(JsonParse *pParse, u32 i){ + char c; + u32 j; + int iThis; + int x; + u8 t; + const char *z = pParse->zJson; +json_parse_restart: + switch( (u8)z[i] ){ + case '{': { + /* Parse object */ + jsonBlobAppendOneByte(pParse, JSONB_OBJECT); + iThis = pParse->nBlob; + jsonBlobAppendU32(pParse, 0); + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = i; + return -1; + } + for(j=i+1;;j++){ + u32 iBlob = pParse->nBlob; + x = jsonParseValueB(pParse, j); + if( x<=0 ){ + if( x==(-2) ){ + j = pParse->iErr; + if( pParse->nBlob!=(u32)iThis+1 ) pParse->hasNonstd = 1; + break; + } + j += json5Whitespace(&z[j]); + if( sqlite3JsonId1(z[j]) + || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) + ){ + int k = j+1; + while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) + || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) + ){ + k++; + } + assert( iBlob==pParse->nBlob ); + jsonBlobAppendOneByte(pParse, JSONB_TEXT5); + jsonBlobAppendVarint(pParse, k-j); + jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j); + pParse->hasNonstd = 1; + x = k; + }else{ + if( x!=-1 ) pParse->iErr = j; + return -1; + } + } + if( pParse->oom ) return -1; + t = pParse->aBlob[iBlob]; + if( tJSONB_TEXT5 ){ + pParse->iErr = j; + return -1; + } + j = x; + if( z[j]==':' ){ + j++; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==':' ){ + j++; + goto parse_object_value; + } + } + x = jsonParseValueB(pParse, j); + if( x!=(-5) ){ + if( x!=(-1) ) pParse->iErr = j; + return -1; + } + j = pParse->iErr+1; + } + parse_object_value: + x = jsonParseValueB(pParse, j); + if( x<=0 ){ + if( x!=(-1) ) pParse->iErr = j; + return -1; + } + j = x; + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]=='}' ){ + break; + } + } + x = jsonParseValueB(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-2) ){ + j = pParse->iErr; + break; + } + } + pParse->iErr = j; + return -1; + } + jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis); + pParse->iDepth--; + return j+1; + } + case '[': { + /* Parse array */ + jsonBlobAppendOneByte(pParse, JSONB_ARRAY); + iThis = pParse->nBlob; + jsonBlobAppendU32(pParse, 0); + if( pParse->oom ) return -1; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + pParse->iErr = i; + return -1; + } + for(j=i+1;;j++){ + x = jsonParseValueB(pParse, j); + if( x<=0 ){ + if( x==(-3) ){ + j = pParse->iErr; + if( pParse->nBlob!=iThis+4 ) pParse->hasNonstd = 1; + break; + } + if( x!=(-1) ) pParse->iErr = j; + return -1; + } + j = x; + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + }else{ + if( fast_isspace(z[j]) ){ + do{ j++; }while( fast_isspace(z[j]) ); + if( z[j]==',' ){ + continue; + }else if( z[j]==']' ){ + break; + } + } + x = jsonParseValueB(pParse, j); + if( x==(-4) ){ + j = pParse->iErr; + continue; + } + if( x==(-3) ){ + j = pParse->iErr; + break; + } + } + pParse->iErr = j; + return -1; + } + jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis); + pParse->iDepth--; + return j+1; + } + case '\'': { + u8 opcode; + char cDelim; + pParse->hasNonstd = 1; + opcode = JNODE_JSON5; + goto parse_string; + case '"': + /* Parse string */ + opcode = JSONB_TEXT; + parse_string: + cDelim = z[i]; + for(j=i+1; 1; j++){ + if( jsonIsOk[(unsigned char)z[j]] ) continue; + c = z[j]; + if( c==cDelim ){ + break; + }else if( c=='\\' ){ + c = z[++j]; + if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' + || c=='n' || c=='r' || c=='t' + || (c=='u' && jsonIs4Hex(&z[j+1])) ){ + if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ; + }else if( c=='\'' || c=='0' || c=='v' || c=='\n' + || (0xe2==(u8)c && 0x80==(u8)z[j+1] + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) + || (c=='x' && jsonIs2Hex(&z[j+1])) ){ + opcode = JSONB_TEXT5; + pParse->hasNonstd = 1; + }else if( c=='\r' ){ + if( z[j+1]=='\n' ) j++; + opcode = JSONB_TEXT5; + pParse->hasNonstd = 1; + }else{ + pParse->iErr = j; + return -1; + } + }else if( c<=0x1f ){ + /* Control characters are not allowed in strings */ + pParse->iErr = j; + return -1; + } + } + jsonBlobAppendOneByte(pParse, opcode); + jsonBlobAppendVarint(pParse, j+1-i); + jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j+1-i); + return j+1; + } + case 't': { + if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonBlobAppendOneByte(pParse, JSONB_TRUE); + return i+4; + } + pParse->iErr = i; + return -1; + } + case 'f': { + if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ + jsonBlobAppendOneByte(pParse, JSONB_FALSE); + return i+5; + } + pParse->iErr = i; + return -1; + } + case '+': { + u8 seenDP, seenE, jnFlags; + pParse->hasNonstd = 1; + jnFlags = 0; + goto parse_number; + case '.': + if( sqlite3Isdigit(z[i+1]) ){ + pParse->hasNonstd = 1; + jnFlags = JNODE_JSON5; + seenE = 0; + seenDP = JSON_REAL; + goto parse_number_2; + } + pParse->iErr = i; + return -1; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Parse number */ + jnFlags = 0; + parse_number: + seenDP = JSON_INT; + seenE = 0; + assert( '-' < '0' ); + assert( '+' < '0' ); + assert( '.' < '0' ); + c = z[i]; + + if( c<='0' ){ + if( c=='0' ){ + if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ + assert( seenDP==JSON_INT ); + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + for(j=i+3; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; + }else if( sqlite3Isdigit(z[i+1]) ){ + pParse->iErr = i+1; + return -1; + } + }else{ + if( !sqlite3Isdigit(z[i+1]) ){ + /* JSON5 allows for "+Infinity" and "-Infinity" using exactly + ** that case. SQLite also allows these in any case and it allows + ** "+inf" and "-inf". */ + if( (z[i+1]=='I' || z[i+1]=='i') + && sqlite3StrNICmp(&z[i+1], "inf",3)==0 + ){ + pParse->hasNonstd = 1; + jsonBlobAppendOneByte(pParse, JSONB_NUMBER5); + if( z[i]=='-' ){ + jsonBlobAppendVarint(pParse, 8); + jsonBlobAppendNBytes(pParse, (const u8*)"-9.0e999", 8); + }else{ + jsonBlobAppendVarint(pParse, 7); + jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7); + } + return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); + } + if( z[i+1]=='.' ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + goto parse_number_2; + } + pParse->iErr = i; + return -1; + } + if( z[i+1]=='0' ){ + if( sqlite3Isdigit(z[i+2]) ){ + pParse->iErr = i+1; + return -1; + }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + for(j=i+4; sqlite3Isxdigit(z[j]); j++){} + goto parse_number_finish; + } + } + } + } + parse_number_2: + for(j=i+1;; j++){ + c = z[j]; + if( sqlite3Isdigit(c) ) continue; + if( c=='.' ){ + if( seenDP==JSON_REAL ){ + pParse->iErr = j; + return -1; + } + seenDP = JSON_REAL; + continue; + } + if( c=='e' || c=='E' ){ + if( z[j-1]<'0' ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + }else{ + pParse->iErr = j; + return -1; + } + } + if( seenE ){ + pParse->iErr = j; + return -1; + } + seenDP = JSON_REAL; + seenE = 1; + c = z[j+1]; + if( c=='+' || c=='-' ){ + j++; + c = z[j+1]; + } + if( c<'0' || c>'9' ){ + pParse->iErr = j; + return -1; + } + continue; + } + break; + } + if( z[j-1]<'0' ){ + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ + pParse->hasNonstd = 1; + jnFlags |= JNODE_JSON5; + }else{ + pParse->iErr = j; + return -1; + } + } + parse_number_finish: + if( jnFlags & JNODE_JSON5 ){ + jsonBlobAppendOneByte(pParse, JSONB_NUMBER5); + }else{ + jsonBlobAppendOneByte(pParse, JSONB_NUMBER); + } + jsonBlobAppendVarint(pParse, j-i); + jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i); + return j; + } + case '}': { + pParse->iErr = i; + return -2; /* End of {...} */ + } + case ']': { + pParse->iErr = i; + return -3; /* End of [...] */ + } + case ',': { + pParse->iErr = i; + return -4; /* List separator */ + } + case ':': { + pParse->iErr = i; + return -5; /* Object label/value separator */ + } + case 0: { + return 0; /* End of file */ + } + case 0x09: + case 0x0a: + case 0x0d: + case 0x20: { + do{ + i++; + }while( fast_isspace(z[i]) ); + goto json_parse_restart; + } + case 0x0b: + case 0x0c: + case '/': + case 0xc2: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xef: { + j = json5Whitespace(&z[i]); + if( j>0 ){ + i += j; + pParse->hasNonstd = 1; + goto json_parse_restart; + } + pParse->iErr = i; + return -1; + } + case 'n': { + if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ + jsonBlobAppendOneByte(pParse, JSONB_NULL); + return i+4; + } + /* fall-through into the default case that checks for NaN */ + } + default: { + u32 k; + int nn; + c = z[i]; + for(k=0; khasNonstd = 1; + return i + nn; + } + pParse->iErr = i; + return -1; /* Syntax error */ + } + } /* End switch(z[i]) */ +} + /* Mark node i of pParse as being a child of iParent. Call recursively ** to fill in all the descendants of node i. @@ -1832,6 +2425,50 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ } } +/* +** Parse a complete JSON string. Return 0 on success or non-zero if there +** are any errors. If an error occurs, free all memory held by pParse, +** but not pParse itself. +** +** pParse must be initialized to an empty parse object prior to calling +** this routine. +*/ +static int jsonParseB( + JsonParse *pParse, /* Initialize and fill this JsonParse object */ + sqlite3_context *pCtx /* Report errors here */ +){ + int i; + const char *zJson = pParse->zJson; + jsonBlobAppendOneByte(pParse, 0x4a); + jsonBlobAppendOneByte(pParse, 0x01); + i = jsonParseValueB(pParse, 0); + if( pParse->oom ) i = -1; + if( i>0 ){ + assert( pParse->iDepth==0 ); + while( fast_isspace(zJson[i]) ) i++; + if( zJson[i] ){ + i += json5Whitespace(&zJson[i]); + if( zJson[i] ){ + jsonParseReset(pParse); + return 1; + } + pParse->hasNonstd = 1; + } + } + if( i<=0 ){ + if( pCtx!=0 ){ + if( pParse->oom ){ + sqlite3_result_error_nomem(pCtx); + }else{ + sqlite3_result_error(pCtx, "malformed JSON", -1); + } + } + jsonParseReset(pParse); + return 1; + } + return 0; +} + /* ** Compute the parentage of all nodes in a completed parse. */ @@ -2408,6 +3045,38 @@ static void jsonTest1Func( ** Scalar SQL function implementations ****************************************************************************/ +/* SQL Function: jsonb_test(TEXT_JSON) +** +** Parse TEXT JSON into the BLOB format and return the resulting BLOB. +** Development testing only. +*/ +static void jsonbTest1( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonParse *pParse; + int nJson; + const char *zJson; + JsonParse x; + UNUSED_PARAMETER(argc); + + zJson = (const char*)sqlite3_value_text(argv[0]); + if( zJson==0 ) return; + nJson = sqlite3_value_bytes(argv[0]); + pParse = &x; + memset(&x, 0, sizeof(x)); + x.zJson = (char*)zJson; + x.nJson = nJson; + if( jsonParseB(pParse, ctx)==0 && pParse->aBlob!=0 ){ + sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; + } + jsonParseReset(pParse); +} + /* ** Implementation of the json_QUOTE(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting @@ -3815,6 +4484,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 1, 0, jsonTypeFunc), JFUNCTION(json_type, 2, 0, jsonTypeFunc), JFUNCTION(json_valid, 1, 0, jsonValidFunc), + JFUNCTION(jsonb_test1, 1, 0, jsonbTest1), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func), From 769a8dea818701ca91221dd691b907aac3033afd Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 21 Sep 2023 18:16:35 +0000 Subject: [PATCH 004/347] Reorganize the code to put the new JSONB routines together, for easier editing. FossilOrigin-Name: dc23e783d4147d363856abe109586fc79a5b535b492beee0cf7a0234c0210667 --- manifest | 15 +- manifest.uuid | 2 +- src/json.c | 992 +++++++++++++++++++++++++------------------------- 3 files changed, 505 insertions(+), 504 deletions(-) diff --git a/manifest b/manifest index 78b4dedcd2..00ee965eea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Initial\sdevelopment\scode\sfor\san\sexperimental\sbinary\sBLOB\sencoding\sfor\sJSON. -D 2023-09-21T17:51:39.740 +C Reorganize\sthe\scode\sto\sput\sthe\snew\sJSONB\sroutines\stogether,\sfor\seasier\sediting. +D 2023-09-21T18:16:35.240 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c aa191fb04ff8b3857c2e05a51b347a23992807a2c12cda5247286ba4768075e7 +F src/json.c 909c1ed65cdff9745534ae31783b394c0412307af67392202c38eb4a5a051682 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,11 +2121,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 f9d62b853ce8bfbfdc9f137e984e7a1b51d70e88c38b136b4fad1e8ae6ee8913 -R 707eaf997af5bd7aea243a294bfb17a0 -T *branch * jsonb -T *sym-jsonb * -T -sym-trunk * +P 8131b3c272f47db2618886046a9713285ce120cb87d721484ee7444273290681 +R f3c29be4023c5b4b5aa9f93c4ce8cc1a U drh -Z b6e8348ea42422278215287176b59cff +Z 6a7efc86b292f902336443f2d5840fea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 37c7ebe312..00f1a8286b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8131b3c272f47db2618886046a9713285ce120cb87d721484ee7444273290681 \ No newline at end of file +dc23e783d4147d363856abe109586fc79a5b535b492beee0cf7a0234c0210667 \ No newline at end of file diff --git a/src/json.c b/src/json.c index c7c6becbd4..9ea085753b 100644 --- a/src/json.c +++ b/src/json.c @@ -1779,6 +1779,33 @@ json_parse_restart: } /* End switch(z[i]) */ } +/* Mark node i of pParse as being a child of iParent. Call recursively +** to fill in all the descendants of node i. +*/ +static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ + JsonNode *pNode = &pParse->aNode[i]; + u32 j; + pParse->aUp[i] = iParent; + switch( pNode->eType ){ + case JSON_ARRAY: { + for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ + jsonParseFillInParentage(pParse, i+j, i); + } + break; + } + case JSON_OBJECT: { + for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ + pParse->aUp[i+j] = i; + jsonParseFillInParentage(pParse, i+j+1, i); + } + break; + } + default: { + break; + } + } +} + /* ** Parse a complete JSON string. Return 0 on success or non-zero if there ** are any errors. If an error occurs, free all memory held by pParse, @@ -1821,6 +1848,477 @@ static int jsonParse( return 0; } +/* +** Compute the parentage of all nodes in a completed parse. +*/ +static int jsonParseFindParents(JsonParse *pParse){ + u32 *aUp; + assert( pParse->aUp==0 ); + aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); + if( aUp==0 ){ + pParse->oom = 1; + return SQLITE_NOMEM; + } + jsonParseFillInParentage(pParse, 0, 0); + return SQLITE_OK; +} + +/* +** Magic number used for the JSON parse cache in sqlite3_get_auxdata() +*/ +#define JSON_CACHE_ID (-429938) /* First cache entry */ +#define JSON_CACHE_SZ 4 /* Max number of cache entries */ + +/* +** Obtain a complete parse of the JSON found in the pJson argument +** +** Use the sqlite3_get_auxdata() cache to find a preexisting parse +** if it is available. If the cache is not available or if it +** is no longer valid, parse the JSON again and return the new parse. +** Also register the new parse so that it will be available for +** future sqlite3_get_auxdata() calls. +** +** If an error occurs and pErrCtx!=0 then report the error on pErrCtx +** and return NULL. +** +** The returned pointer (if it is not NULL) is owned by the cache in +** most cases, not the caller. The caller does NOT need to invoke +** jsonParseFree(), in most cases. +** +** Except, if an error occurs and pErrCtx==0 then return the JsonParse +** object with JsonParse.nErr non-zero and the caller will own the JsonParse +** object. In that case, it will be the responsibility of the caller to +** invoke jsonParseFree(). To summarize: +** +** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the +** cache. Call does not need to +** free it. +** +** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller +** and so the caller must free it. +*/ +static JsonParse *jsonParseCached( + sqlite3_context *pCtx, /* Context to use for cache search */ + sqlite3_value *pJson, /* Function param containing JSON text */ + sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ + int bUnedited /* No prior edits allowed */ +){ + char *zJson = (char*)sqlite3_value_text(pJson); + int nJson = sqlite3_value_bytes(pJson); + JsonParse *p; + JsonParse *pMatch = 0; + int iKey; + int iMinKey = 0; + u32 iMinHold = 0xffffffff; + u32 iMaxHold = 0; + int bJsonRCStr; + + if( zJson==0 ) return 0; + for(iKey=0; iKeynJson==nJson + && (p->hasMod==0 || bUnedited==0) + && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0) + ){ + p->nErr = 0; + p->useMod = 0; + pMatch = p; + }else + if( pMatch==0 + && p->zAlt!=0 + && bUnedited==0 + && p->nAlt==nJson + && memcmp(p->zAlt, zJson, nJson)==0 + ){ + p->nErr = 0; + p->useMod = 1; + pMatch = p; + }else if( p->iHoldiHold; + iMinKey = iKey; + } + if( p->iHold>iMaxHold ){ + iMaxHold = p->iHold; + } + } + if( pMatch ){ + /* The input JSON text was found in the cache. Use the preexisting + ** parse of this JSON */ + pMatch->nErr = 0; + pMatch->iHold = iMaxHold+1; + assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ + return pMatch; + } + + /* The input JSON was not found anywhere in the cache. We will need + ** to parse it ourselves and generate a new JsonParse object. + */ + bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref); + p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) ); + if( p==0 ){ + sqlite3_result_error_nomem(pCtx); + return 0; + } + memset(p, 0, sizeof(*p)); + if( bJsonRCStr ){ + p->zJson = sqlite3RCStrRef(zJson); + p->bJsonIsRCStr = 1; + }else{ + p->zJson = (char*)&p[1]; + memcpy(p->zJson, zJson, nJson+1); + } + p->nJPRef = 1; + if( jsonParse(p, pErrCtx) ){ + if( pErrCtx==0 ){ + p->nErr = 1; + assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ + return p; + } + jsonParseFree(p); + return 0; + } + p->nJson = nJson; + p->iHold = iMaxHold+1; + /* Transfer ownership of the new JsonParse to the cache */ + sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, + (void(*)(void*))jsonParseFree); + return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); +} + +/* +** Compare the OBJECT label at pNode against zKey,nKey. Return true on +** a match. +*/ +static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_RAW ){ + if( pNode->n!=nKey ) return 0; + return strncmp(pNode->u.zJContent, zKey, nKey)==0; + }else{ + if( pNode->n!=nKey+2 ) return 0; + return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; + } +} +static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ + if( p1->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p2, p1->u.zJContent, p1->n); + }else if( p2->jnFlags & JNODE_RAW ){ + return jsonLabelCompare(p1, p2->u.zJContent, p2->n); + }else{ + return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; + } +} + +/* forward declaration */ +static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); + +/* +** Search along zPath to find the node specified. Return a pointer +** to that node, or NULL if zPath is malformed or if there is no such +** node. +** +** If pApnd!=0, then try to append new nodes to complete zPath if it is +** possible to do so and if no existing node corresponds to zPath. If +** new nodes are appended *pApnd is set to 1. +*/ +static JsonNode *jsonLookupStep( + JsonParse *pParse, /* The JSON to search */ + u32 iRoot, /* Begin the search at this node */ + const char *zPath, /* The path to search */ + int *pApnd, /* Append nodes to complete path if not NULL */ + const char **pzErr /* Make *pzErr point to any syntax error in zPath */ +){ + u32 i, j, nKey; + const char *zKey; + JsonNode *pRoot; + if( pParse->oom ) return 0; + pRoot = &pParse->aNode[iRoot]; + if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){ + while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ + u32 idx = (u32)(pRoot - pParse->aNode); + i = pParse->iSubst; + while( 1 /*exit-by-break*/ ){ + assert( inNode ); + assert( pParse->aNode[i].eType==JSON_SUBST ); + assert( pParse->aNode[i].eU==4 ); + assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ + pRoot = &pParse->aNode[i+1]; + iRoot = i+1; + break; + } + i = pParse->aNode[i].u.iPrev; + } + } + if( pRoot->jnFlags & JNODE_REMOVE ){ + return 0; + } + } + if( zPath[0]==0 ) return pRoot; + if( zPath[0]=='.' ){ + if( pRoot->eType!=JSON_OBJECT ) return 0; + zPath++; + if( zPath[0]=='"' ){ + zKey = zPath + 1; + for(i=1; zPath[i] && zPath[i]!='"'; i++){} + nKey = i-1; + if( zPath[i] ){ + i++; + }else{ + *pzErr = zPath; + return 0; + } + testcase( nKey==0 ); + }else{ + zKey = zPath; + for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} + nKey = i; + if( nKey==0 ){ + *pzErr = zPath; + return 0; + } + } + j = 1; + for(;;){ + while( j<=pRoot->n ){ + if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ + return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); + } + j++; + j += jsonNodeSize(&pRoot[j]); + } + if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pRoot->eU==2 ); + iRoot = pRoot->u.iAppend; + pRoot = &pParse->aNode[iRoot]; + j = 1; + } + if( pApnd ){ + u32 iStart, iLabel; + JsonNode *pNode; + assert( pParse->useMod ); + iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); + iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); + zPath += i; + pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); + if( pParse->oom ) return 0; + if( pNode ){ + pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); + pRoot->u.iAppend = iStart; + pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); + pParse->aNode[iLabel].jnFlags |= JNODE_RAW; + } + return pNode; + } + }else if( zPath[0]=='[' ){ + i = 0; + j = 1; + while( sqlite3Isdigit(zPath[j]) ){ + i = i*10 + zPath[j] - '0'; + j++; + } + if( j<2 || zPath[j]!=']' ){ + if( zPath[1]=='#' ){ + JsonNode *pBase = pRoot; + int iBase = iRoot; + if( pRoot->eType!=JSON_ARRAY ) return 0; + for(;;){ + while( j<=pBase->n ){ + if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; + j += jsonNodeSize(&pBase[j]); + } + if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pBase->eU==2 ); + iBase = pBase->u.iAppend; + pBase = &pParse->aNode[iBase]; + j = 1; + } + j = 2; + if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ + unsigned int x = 0; + j = 3; + do{ + x = x*10 + zPath[j] - '0'; + j++; + }while( sqlite3Isdigit(zPath[j]) ); + if( x>i ) return 0; + i -= x; + } + if( zPath[j]!=']' ){ + *pzErr = zPath; + return 0; + } + }else{ + *pzErr = zPath; + return 0; + } + } + if( pRoot->eType!=JSON_ARRAY ) return 0; + zPath += j + 1; + j = 1; + for(;;){ + while( j<=pRoot->n + && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) + ){ + if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; + j += jsonNodeSize(&pRoot[j]); + } + if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pRoot->eU==2 ); + iRoot = pRoot->u.iAppend; + pRoot = &pParse->aNode[iRoot]; + j = 1; + } + if( j<=pRoot->n ){ + return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); + } + if( i==0 && pApnd ){ + u32 iStart; + JsonNode *pNode; + assert( pParse->useMod ); + iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); + pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); + if( pParse->oom ) return 0; + if( pNode ){ + pRoot = &pParse->aNode[iRoot]; + assert( pRoot->eU==0 ); + pRoot->u.iAppend = iStart; + pRoot->jnFlags |= JNODE_APPEND; + VVA( pRoot->eU = 2 ); + } + return pNode; + } + }else{ + *pzErr = zPath; + } + return 0; +} + +/* +** Append content to pParse that will complete zPath. Return a pointer +** to the inserted node, or return NULL if the append fails. +*/ +static JsonNode *jsonLookupAppend( + JsonParse *pParse, /* Append content to the JSON parse */ + const char *zPath, /* Description of content to append */ + int *pApnd, /* Set this flag to 1 */ + const char **pzErr /* Make this point to any syntax error */ +){ + *pApnd = 1; + if( zPath[0]==0 ){ + jsonParseAddNode(pParse, JSON_NULL, 0, 0); + return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; + } + if( zPath[0]=='.' ){ + jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); + }else if( strncmp(zPath,"[0]",3)==0 ){ + jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); + }else{ + return 0; + } + if( pParse->oom ) return 0; + return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); +} + +/* +** Return the text of a syntax error message on a JSON path. Space is +** obtained from sqlite3_malloc(). +*/ +static char *jsonPathSyntaxError(const char *zErr){ + return sqlite3_mprintf("JSON path error near '%q'", zErr); +} + +/* +** Do a node lookup using zPath. Return a pointer to the node on success. +** Return NULL if not found or if there is an error. +** +** On an error, write an error message into pCtx and increment the +** pParse->nErr counter. +** +** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if +** nodes are appended. +*/ +static JsonNode *jsonLookup( + JsonParse *pParse, /* The JSON to search */ + const char *zPath, /* The path to search */ + int *pApnd, /* Append nodes to complete path if not NULL */ + sqlite3_context *pCtx /* Report errors here, if not NULL */ +){ + const char *zErr = 0; + JsonNode *pNode = 0; + char *zMsg; + + if( zPath==0 ) return 0; + if( zPath[0]!='$' ){ + zErr = zPath; + goto lookup_err; + } + zPath++; + pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); + if( zErr==0 ) return pNode; + +lookup_err: + pParse->nErr++; + assert( zErr!=0 && pCtx!=0 ); + zMsg = jsonPathSyntaxError(zErr); + if( zMsg ){ + sqlite3_result_error(pCtx, zMsg, -1); + sqlite3_free(zMsg); + }else{ + sqlite3_result_error_nomem(pCtx); + } + return 0; +} + + +/* +** Report the wrong number of arguments for json_insert(), json_replace() +** or json_set(). +*/ +static void jsonWrongNumArgs( + sqlite3_context *pCtx, + const char *zFuncName +){ + char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", + zFuncName); + sqlite3_result_error(pCtx, zMsg, -1); + sqlite3_free(zMsg); +} + +/* +** Mark all NULL entries in the Object passed in as JNODE_REMOVE. +*/ +static void jsonRemoveAllNulls(JsonNode *pNode){ + int i, n; + assert( pNode->eType==JSON_OBJECT ); + n = pNode->n; + for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ + switch( pNode[i].eType ){ + case JSON_NULL: + pNode[i].jnFlags |= JNODE_REMOVE; + break; + case JSON_OBJECT: + jsonRemoveAllNulls(&pNode[i]); + break; + } + } +} + +/**************************************************************************** +** Utility routines for dealing with the binary BLOB representation of JSON +****************************************************************************/ + + /* ** Expand pParse->aBlob so that it holds at least N bytes. ** @@ -2398,33 +2896,6 @@ json_parse_restart: } -/* Mark node i of pParse as being a child of iParent. Call recursively -** to fill in all the descendants of node i. -*/ -static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ - JsonNode *pNode = &pParse->aNode[i]; - u32 j; - pParse->aUp[i] = iParent; - switch( pNode->eType ){ - case JSON_ARRAY: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ - jsonParseFillInParentage(pParse, i+j, i); - } - break; - } - case JSON_OBJECT: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ - pParse->aUp[i+j] = i; - jsonParseFillInParentage(pParse, i+j+1, i); - } - break; - } - default: { - break; - } - } -} - /* ** Parse a complete JSON string. Return 0 on success or non-zero if there ** are any errors. If an error occurs, free all memory held by pParse, @@ -2469,473 +2940,6 @@ static int jsonParseB( return 0; } -/* -** Compute the parentage of all nodes in a completed parse. -*/ -static int jsonParseFindParents(JsonParse *pParse){ - u32 *aUp; - assert( pParse->aUp==0 ); - aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); - if( aUp==0 ){ - pParse->oom = 1; - return SQLITE_NOMEM; - } - jsonParseFillInParentage(pParse, 0, 0); - return SQLITE_OK; -} - -/* -** Magic number used for the JSON parse cache in sqlite3_get_auxdata() -*/ -#define JSON_CACHE_ID (-429938) /* First cache entry */ -#define JSON_CACHE_SZ 4 /* Max number of cache entries */ - -/* -** Obtain a complete parse of the JSON found in the pJson argument -** -** Use the sqlite3_get_auxdata() cache to find a preexisting parse -** if it is available. If the cache is not available or if it -** is no longer valid, parse the JSON again and return the new parse. -** Also register the new parse so that it will be available for -** future sqlite3_get_auxdata() calls. -** -** If an error occurs and pErrCtx!=0 then report the error on pErrCtx -** and return NULL. -** -** The returned pointer (if it is not NULL) is owned by the cache in -** most cases, not the caller. The caller does NOT need to invoke -** jsonParseFree(), in most cases. -** -** Except, if an error occurs and pErrCtx==0 then return the JsonParse -** object with JsonParse.nErr non-zero and the caller will own the JsonParse -** object. In that case, it will be the responsibility of the caller to -** invoke jsonParseFree(). To summarize: -** -** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the -** cache. Call does not need to -** free it. -** -** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller -** and so the caller must free it. -*/ -static JsonParse *jsonParseCached( - sqlite3_context *pCtx, /* Context to use for cache search */ - sqlite3_value *pJson, /* Function param containing JSON text */ - sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ - int bUnedited /* No prior edits allowed */ -){ - char *zJson = (char*)sqlite3_value_text(pJson); - int nJson = sqlite3_value_bytes(pJson); - JsonParse *p; - JsonParse *pMatch = 0; - int iKey; - int iMinKey = 0; - u32 iMinHold = 0xffffffff; - u32 iMaxHold = 0; - int bJsonRCStr; - - if( zJson==0 ) return 0; - for(iKey=0; iKeynJson==nJson - && (p->hasMod==0 || bUnedited==0) - && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0) - ){ - p->nErr = 0; - p->useMod = 0; - pMatch = p; - }else - if( pMatch==0 - && p->zAlt!=0 - && bUnedited==0 - && p->nAlt==nJson - && memcmp(p->zAlt, zJson, nJson)==0 - ){ - p->nErr = 0; - p->useMod = 1; - pMatch = p; - }else if( p->iHoldiHold; - iMinKey = iKey; - } - if( p->iHold>iMaxHold ){ - iMaxHold = p->iHold; - } - } - if( pMatch ){ - /* The input JSON text was found in the cache. Use the preexisting - ** parse of this JSON */ - pMatch->nErr = 0; - pMatch->iHold = iMaxHold+1; - assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ - return pMatch; - } - - /* The input JSON was not found anywhere in the cache. We will need - ** to parse it ourselves and generate a new JsonParse object. - */ - bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref); - p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) ); - if( p==0 ){ - sqlite3_result_error_nomem(pCtx); - return 0; - } - memset(p, 0, sizeof(*p)); - if( bJsonRCStr ){ - p->zJson = sqlite3RCStrRef(zJson); - p->bJsonIsRCStr = 1; - }else{ - p->zJson = (char*)&p[1]; - memcpy(p->zJson, zJson, nJson+1); - } - p->nJPRef = 1; - if( jsonParse(p, pErrCtx) ){ - if( pErrCtx==0 ){ - p->nErr = 1; - assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ - return p; - } - jsonParseFree(p); - return 0; - } - p->nJson = nJson; - p->iHold = iMaxHold+1; - /* Transfer ownership of the new JsonParse to the cache */ - sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, - (void(*)(void*))jsonParseFree); - return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); -} - -/* -** Compare the OBJECT label at pNode against zKey,nKey. Return true on -** a match. -*/ -static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->n!=nKey ) return 0; - return strncmp(pNode->u.zJContent, zKey, nKey)==0; - }else{ - if( pNode->n!=nKey+2 ) return 0; - return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; - } -} -static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ - if( p1->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p2, p1->u.zJContent, p1->n); - }else if( p2->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p1, p2->u.zJContent, p2->n); - }else{ - return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; - } -} - -/* forward declaration */ -static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); - -/* -** Search along zPath to find the node specified. Return a pointer -** to that node, or NULL if zPath is malformed or if there is no such -** node. -** -** If pApnd!=0, then try to append new nodes to complete zPath if it is -** possible to do so and if no existing node corresponds to zPath. If -** new nodes are appended *pApnd is set to 1. -*/ -static JsonNode *jsonLookupStep( - JsonParse *pParse, /* The JSON to search */ - u32 iRoot, /* Begin the search at this node */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - const char **pzErr /* Make *pzErr point to any syntax error in zPath */ -){ - u32 i, j, nKey; - const char *zKey; - JsonNode *pRoot; - if( pParse->oom ) return 0; - pRoot = &pParse->aNode[iRoot]; - if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){ - while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ - u32 idx = (u32)(pRoot - pParse->aNode); - i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pRoot = &pParse->aNode[i+1]; - iRoot = i+1; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - if( pRoot->jnFlags & JNODE_REMOVE ){ - return 0; - } - } - if( zPath[0]==0 ) return pRoot; - if( zPath[0]=='.' ){ - if( pRoot->eType!=JSON_OBJECT ) return 0; - zPath++; - if( zPath[0]=='"' ){ - zKey = zPath + 1; - for(i=1; zPath[i] && zPath[i]!='"'; i++){} - nKey = i-1; - if( zPath[i] ){ - i++; - }else{ - *pzErr = zPath; - return 0; - } - testcase( nKey==0 ); - }else{ - zKey = zPath; - for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} - nKey = i; - if( nKey==0 ){ - *pzErr = zPath; - return 0; - } - } - j = 1; - for(;;){ - while( j<=pRoot->n ){ - if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ - return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); - } - j++; - j += jsonNodeSize(&pRoot[j]); - } - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( pApnd ){ - u32 iStart, iLabel; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); - iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - zPath += i; - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); - pParse->aNode[iLabel].jnFlags |= JNODE_RAW; - } - return pNode; - } - }else if( zPath[0]=='[' ){ - i = 0; - j = 1; - while( sqlite3Isdigit(zPath[j]) ){ - i = i*10 + zPath[j] - '0'; - j++; - } - if( j<2 || zPath[j]!=']' ){ - if( zPath[1]=='#' ){ - JsonNode *pBase = pRoot; - int iBase = iRoot; - if( pRoot->eType!=JSON_ARRAY ) return 0; - for(;;){ - while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; - j += jsonNodeSize(&pBase[j]); - } - if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pBase->eU==2 ); - iBase = pBase->u.iAppend; - pBase = &pParse->aNode[iBase]; - j = 1; - } - j = 2; - if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ - unsigned int x = 0; - j = 3; - do{ - x = x*10 + zPath[j] - '0'; - j++; - }while( sqlite3Isdigit(zPath[j]) ); - if( x>i ) return 0; - i -= x; - } - if( zPath[j]!=']' ){ - *pzErr = zPath; - return 0; - } - }else{ - *pzErr = zPath; - return 0; - } - } - if( pRoot->eType!=JSON_ARRAY ) return 0; - zPath += j + 1; - j = 1; - for(;;){ - while( j<=pRoot->n - && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) - ){ - if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; - j += jsonNodeSize(&pRoot[j]); - } - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( j<=pRoot->n ){ - return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); - } - if( i==0 && pApnd ){ - u32 iStart; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); - } - return pNode; - } - }else{ - *pzErr = zPath; - } - return 0; -} - -/* -** Append content to pParse that will complete zPath. Return a pointer -** to the inserted node, or return NULL if the append fails. -*/ -static JsonNode *jsonLookupAppend( - JsonParse *pParse, /* Append content to the JSON parse */ - const char *zPath, /* Description of content to append */ - int *pApnd, /* Set this flag to 1 */ - const char **pzErr /* Make this point to any syntax error */ -){ - *pApnd = 1; - if( zPath[0]==0 ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; - } - if( zPath[0]=='.' ){ - jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - }else if( strncmp(zPath,"[0]",3)==0 ){ - jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); - }else{ - return 0; - } - if( pParse->oom ) return 0; - return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); -} - -/* -** Return the text of a syntax error message on a JSON path. Space is -** obtained from sqlite3_malloc(). -*/ -static char *jsonPathSyntaxError(const char *zErr){ - return sqlite3_mprintf("JSON path error near '%q'", zErr); -} - -/* -** Do a node lookup using zPath. Return a pointer to the node on success. -** Return NULL if not found or if there is an error. -** -** On an error, write an error message into pCtx and increment the -** pParse->nErr counter. -** -** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if -** nodes are appended. -*/ -static JsonNode *jsonLookup( - JsonParse *pParse, /* The JSON to search */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - sqlite3_context *pCtx /* Report errors here, if not NULL */ -){ - const char *zErr = 0; - JsonNode *pNode = 0; - char *zMsg; - - if( zPath==0 ) return 0; - if( zPath[0]!='$' ){ - zErr = zPath; - goto lookup_err; - } - zPath++; - pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); - if( zErr==0 ) return pNode; - -lookup_err: - pParse->nErr++; - assert( zErr!=0 && pCtx!=0 ); - zMsg = jsonPathSyntaxError(zErr); - if( zMsg ){ - sqlite3_result_error(pCtx, zMsg, -1); - sqlite3_free(zMsg); - }else{ - sqlite3_result_error_nomem(pCtx); - } - return 0; -} - - -/* -** Report the wrong number of arguments for json_insert(), json_replace() -** or json_set(). -*/ -static void jsonWrongNumArgs( - sqlite3_context *pCtx, - const char *zFuncName -){ - char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", - zFuncName); - sqlite3_result_error(pCtx, zMsg, -1); - sqlite3_free(zMsg); -} - -/* -** Mark all NULL entries in the Object passed in as JNODE_REMOVE. -*/ -static void jsonRemoveAllNulls(JsonNode *pNode){ - int i, n; - assert( pNode->eType==JSON_OBJECT ); - n = pNode->n; - for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ - switch( pNode[i].eType ){ - case JSON_NULL: - pNode[i].jnFlags |= JNODE_REMOVE; - break; - case JSON_OBJECT: - jsonRemoveAllNulls(&pNode[i]); - break; - } - } -} - - /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ From 8c55945220bcd60ff8069f9879631edbd29169be Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 22 Sep 2023 11:20:35 +0000 Subject: [PATCH 005/347] Improvements to the JSON binary BLOB format design. FossilOrigin-Name: 2c89ae5d02f6a40ef869e2a162e2c72871df60572b27959fd1d7171f495ce881 --- manifest | 12 +- manifest.uuid | 2 +- src/json.c | 489 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 398 insertions(+), 105 deletions(-) diff --git a/manifest b/manifest index 00ee965eea..3c715192be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reorganize\sthe\scode\sto\sput\sthe\snew\sJSONB\sroutines\stogether,\sfor\seasier\sediting. -D 2023-09-21T18:16:35.240 +C Improvements\sto\sthe\sJSON\sbinary\sBLOB\sformat\sdesign. +D 2023-09-22T11:20:35.748 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 909c1ed65cdff9745534ae31783b394c0412307af67392202c38eb4a5a051682 +F src/json.c 4a25369c2f65c930f07f0b5b36e3a4ba86522f432035ac9204c9940c9d2dfb43 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 8131b3c272f47db2618886046a9713285ce120cb87d721484ee7444273290681 -R f3c29be4023c5b4b5aa9f93c4ce8cc1a +P dc23e783d4147d363856abe109586fc79a5b535b492beee0cf7a0234c0210667 +R 9fd8daf58d6e7ea7726891b7477fe4a5 U drh -Z 6a7efc86b292f902336443f2d5840fea +Z 3129fb1588c8eb084faa14e3a492d4c9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 00f1a8286b..b60349f423 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dc23e783d4147d363856abe109586fc79a5b535b492beee0cf7a0234c0210667 \ No newline at end of file +2c89ae5d02f6a40ef869e2a162e2c72871df60572b27959fd1d7171f495ce881 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9ea085753b..7be2cebeb6 100644 --- a/src/json.c +++ b/src/json.c @@ -127,13 +127,15 @@ struct JsonCleanup { #define JSONB_NULL 0 #define JSONB_TRUE 1 #define JSONB_FALSE 2 -#define JSONB_NUMBER 3 /* VarInt size + text */ -#define JSONB_NUMBER5 4 /* VarInt size + text (JSON5 formatted) */ -#define JSONB_TEXT 5 /* VarInt size + text (Raw and unescaped) */ -#define JSONB_TEXTJ 6 /* VarInt size + text (JSON formatted) */ -#define JSONB_TEXT5 7 /* VarInt size + text (JSON5 formatted) */ -#define JSONB_ARRAY 8 /* u32 size + content */ -#define JSONB_OBJECT 9 /* u32 size + content */ +#define JSONB_INT 3 +#define JSONB_INT5 4 +#define JSONB_FLOAT 5 +#define JSONB_FLOAT5 6 +#define JSONB_TEXT 7 +#define JSONB_TEXTJ 8 +#define JSONB_TEXT5 9 +#define JSONB_ARRAY 10 +#define JSONB_OBJECT 11 /* The "subtype" set for JSON values */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ @@ -2377,55 +2379,64 @@ static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){ return 0; } -/* Append a u32. Return 1 if an error occurs. +/* Append an node type byte together with the payload size. */ -static int jsonBlobAppendU32(JsonParse *pParse, u32 x){ - u8 a[4]; - a[3] = x & 0xff; - x >>= 8; - a[2] = x & 0xff; - x >>= 8; - a[1] = x & 0xff; - x >>= 8; - a[0] = x & 0xff; - return jsonBlobAppendNBytes(pParse, a, 4); -} - -/* Write a u32 and offset i. -*/ -static int jsonBlobWriteU32(JsonParse *pParse, u32 x, u32 i){ - u8 *a = &pParse->aBlob[i]; - a[3] = x & 0xff; - x >>= 8; - a[2] = x & 0xff; - x >>= 8; - a[1] = x & 0xff; - x >>= 8; - a[0] = x & 0xff; - return 0; -} - -/* Append a VarInt. Return 1 if an error occurs. -*/ -static int jsonBlobAppendVarint(JsonParse *pParse, u32 x){ +static void jsonBlobAppendNodeType( + JsonParse *pParse, + u8 eType, + u32 szPayload +){ u8 a[5]; - if( x<=0x7f ) return jsonBlobAppendOneByte(pParse, x & 0x7f); - a[0] = 0x80 | (x & 0x7f); - x >>= 7; - a[1] = x & 0x7f; - x >>= 7; - if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 2); - a[1] |= 0x80; - a[2] = x & 0x7f; - x >>= 7; - if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 3); - a[2] |= 0x80; - a[3] = x & 0x7f; - x >>= 7; - if( x==0 ) return jsonBlobAppendNBytes(pParse, a, 4); - a[3] |= 0x80; - a[4] = x & 0x7f; - return jsonBlobAppendNBytes(pParse, a, 5); + if( szPayload<=11 ){ + jsonBlobAppendOneByte(pParse, eType | (szPayload<<4)); + }else if( szPayload<=0xff ){ + a[0] = eType | 0xc0; + a[1] = szPayload & 0xff; + jsonBlobAppendNBytes(pParse, a, 2); + }else if( szPayload<=0xffff ){ + a[0] = eType | 0xd0; + a[1] = (szPayload >> 8) & 0xff; + a[2] = szPayload & 0xff; + jsonBlobAppendNBytes(pParse, a, 3); + }else{ + a[0] = eType | 0xe0; + a[1] = (szPayload >> 24) & 0xff; + a[2] = (szPayload >> 16) & 0xff; + a[3] = (szPayload >> 8) & 0xff; + a[4] = szPayload & 0xff; + jsonBlobAppendNBytes(pParse, a, 5); + } +} + +/* Change the payload size for the node at index i to be szPayload. +*/ +static void jsonBlobChangePayloadSize( + JsonParse *pParse, + u32 i, + u32 szPayload +){ + u8 *a = &pParse->aBlob[i]; + u8 szType = a[0]>>4; + if( szType<=11 ){ + assert( szPayload<=11 ); + a[0] = (a[0] & 0x0f) | (szPayload<<4); + }else if( szType==0xc ){ + assert( szPayload<=0xff ); + assert( i+1nBlob ); + a[1] = szPayload & 0xff; + }else if( szType==0xd ){ + assert( szPayload<=0xffff ); + assert( i+2nBlob ); + a[1] = (szPayload >> 8) & 0xff; + a[2] = szPayload & 0xff; + }else{ + assert( szType==0xe ); + assert( i+4nBlob ); + a[1] = (szPayload >> 24) & 0xff; + a[2] = (szPayload >> 16) & 0xff; + a[3] = (szPayload >> 8) & 0xff; + a[4] = szPayload & 0xff; + } } /* @@ -2447,7 +2458,7 @@ static int jsonBlobAppendVarint(JsonParse *pParse, u32 x){ static int jsonParseValueB(JsonParse *pParse, u32 i){ char c; u32 j; - int iThis; + u32 iThis, iStart; int x; u8 t; const char *z = pParse->zJson; @@ -2455,13 +2466,13 @@ json_parse_restart: switch( (u8)z[i] ){ case '{': { /* Parse object */ - jsonBlobAppendOneByte(pParse, JSONB_OBJECT); iThis = pParse->nBlob; - jsonBlobAppendU32(pParse, 0); + jsonBlobAppendNodeType(pParse, JSONB_OBJECT, (pParse->nJson-i)*2); if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } + iStart = pParse->nBlob; for(j=i+1;;j++){ u32 iBlob = pParse->nBlob; x = jsonParseValueB(pParse, j); @@ -2482,8 +2493,7 @@ json_parse_restart: k++; } assert( iBlob==pParse->nBlob ); - jsonBlobAppendOneByte(pParse, JSONB_TEXT5); - jsonBlobAppendVarint(pParse, k-j); + jsonBlobAppendNodeType(pParse, JSONB_TEXT5, k-j); jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j); pParse->hasNonstd = 1; x = k; @@ -2493,7 +2503,7 @@ json_parse_restart: } } if( pParse->oom ) return -1; - t = pParse->aBlob[iBlob]; + t = pParse->aBlob[iBlob] & 0x0f; if( tJSONB_TEXT5 ){ pParse->iErr = j; return -1; @@ -2549,15 +2559,17 @@ json_parse_restart: pParse->iErr = j; return -1; } - jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis); + if( pParse->nErr==0 ){ + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); + } pParse->iDepth--; return j+1; } case '[': { /* Parse array */ - jsonBlobAppendOneByte(pParse, JSONB_ARRAY); iThis = pParse->nBlob; - jsonBlobAppendU32(pParse, 0); + jsonBlobAppendNodeType(pParse, JSONB_ARRAY, pParse->nJson - i); + iStart = pParse->nBlob; if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; @@ -2601,7 +2613,9 @@ json_parse_restart: pParse->iErr = j; return -1; } - jsonBlobWriteU32(pParse, pParse->nBlob - iThis, iThis); + if( pParse->iErr==0 ){ + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); + } pParse->iDepth--; return j+1; } @@ -2609,7 +2623,7 @@ json_parse_restart: u8 opcode; char cDelim; pParse->hasNonstd = 1; - opcode = JNODE_JSON5; + opcode = JSONB_TEXT; goto parse_string; case '"': /* Parse string */ @@ -2647,8 +2661,7 @@ json_parse_restart: return -1; } } - jsonBlobAppendOneByte(pParse, opcode); - jsonBlobAppendVarint(pParse, j+1-i); + jsonBlobAppendNodeType(pParse, opcode, j+1-i); jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j+1-i); return j+1; } @@ -2669,16 +2682,15 @@ json_parse_restart: return -1; } case '+': { - u8 seenDP, seenE, jnFlags; + u8 seenE; pParse->hasNonstd = 1; - jnFlags = 0; + t = 0x01; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ goto parse_number; case '.': if( sqlite3Isdigit(z[i+1]) ){ pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; + t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ seenE = 0; - seenDP = JSON_REAL; goto parse_number_2; } pParse->iErr = i; @@ -2695,9 +2707,8 @@ json_parse_restart: case '8': case '9': /* Parse number */ - jnFlags = 0; + t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ parse_number: - seenDP = JSON_INT; seenE = 0; assert( '-' < '0' ); assert( '+' < '0' ); @@ -2707,9 +2718,9 @@ json_parse_restart: if( c<='0' ){ if( c=='0' ){ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ - assert( seenDP==JSON_INT ); + assert( t==0x00 ); pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t = 0x01; for(j=i+3; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; }else if( sqlite3Isdigit(z[i+1]) ){ @@ -2725,19 +2736,18 @@ json_parse_restart: && sqlite3StrNICmp(&z[i+1], "inf",3)==0 ){ pParse->hasNonstd = 1; - jsonBlobAppendOneByte(pParse, JSONB_NUMBER5); if( z[i]=='-' ){ - jsonBlobAppendVarint(pParse, 8); - jsonBlobAppendNBytes(pParse, (const u8*)"-9.0e999", 8); + jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 6); + jsonBlobAppendNBytes(pParse, (const u8*)"-9e999", 6); }else{ - jsonBlobAppendVarint(pParse, 7); - jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7); + jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 5); + jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); } return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); } if( z[i+1]=='.' ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x03; goto parse_number_2; } pParse->iErr = i; @@ -2749,7 +2759,7 @@ json_parse_restart: return -1; }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; for(j=i+4; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; } @@ -2761,18 +2771,18 @@ json_parse_restart: c = z[j]; if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ - if( seenDP==JSON_REAL ){ + if( (t & 0x02)!=0 ){ pParse->iErr = j; return -1; } - seenDP = JSON_REAL; + t |= 0x02; continue; } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; }else{ pParse->iErr = j; return -1; @@ -2782,7 +2792,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - seenDP = JSON_REAL; + t |= 0x02; seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ @@ -2800,19 +2810,17 @@ json_parse_restart: if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; }else{ pParse->iErr = j; return -1; } } parse_number_finish: - if( jnFlags & JNODE_JSON5 ){ - jsonBlobAppendOneByte(pParse, JSONB_NUMBER5); - }else{ - jsonBlobAppendOneByte(pParse, JSONB_NUMBER); - } - jsonBlobAppendVarint(pParse, j-i); + assert( JSONB_INT+0x01==JSONB_INT5 ); + assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); + assert( JSONB_INT+0x02==JSONB_FLOAT ); + jsonBlobAppendNodeType(pParse, JSONB_INT+t, j-i); jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i); return j; } @@ -2880,9 +2888,9 @@ json_parse_restart: } if( sqlite3Isalnum(z[i+nn]) ) continue; if( aNanInfName[k].eType==JSON_REAL ){ - jsonBlobAppendOneByte(pParse, JSONB_NUMBER); - jsonBlobAppendOneByte(pParse, 7); - jsonBlobAppendNBytes(pParse, (const u8*)"9.0e999", 7); + jsonBlobAppendOneByte(pParse, JSONB_FLOAT); + jsonBlobAppendOneByte(pParse, 5); + jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); }else{ jsonBlobAppendOneByte(pParse, JSONB_NULL); } @@ -2910,8 +2918,6 @@ static int jsonParseB( ){ int i; const char *zJson = pParse->zJson; - jsonBlobAppendOneByte(pParse, 0x4a); - jsonBlobAppendOneByte(pParse, 0x01); i = jsonParseValueB(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ @@ -2940,6 +2946,293 @@ static int jsonParseB( return 0; } +#if 0 +/* +** Convert the binary BLOB representation of JSON beginning at +** aBlob[0] (and extending for no more than nBlob bytes) into +** a pure JSON string. The string is appended to pOut. +*/ +static void jsonRenderBlob( + JsonParse *pParse, /* the complete parse of the JSON */ + u8 *aBlob, /* The binary encoded JSON to be rendered */ + u32 nBlob, /* Maximum size of aBlob[...] */ + JsonString *pOut /* Write JSON here */ +){ + u32 i; + if( nBlob<1 ){ + pOut->bErr = 1; + return; + } + switch( aBlob[0] ){ + case JSONB_NULL: + jsonAppendRawNZ(pOut, "null", 4); + break; + } + case JSONB_TRUE: { + jsonAppendRawNZ(pOut, "true", 4); + break; + } + case JSONB_FALSE: { + jsonAppendRawNZ(pOut, "false", 5); + break; + } + case JSONB_NUMBER: + case JSONB_TEXTJ: { + u32 sz, n; + n = jsonbDecodeVarint(&aBlob[1], nBlob-1, &sz); + if( 1+n+sz>nBlob ){ + pOut->bErr = 1; + return; + } + jsonAppendRaw(pOut, &aBlob[1+n], sz); + break; + + case JSON_STRING: { + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_RAW ){ + if( pNode->jnFlags & JNODE_LABEL ){ + jsonAppendChar(pOut, '"'); + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); + jsonAppendChar(pOut, '"'); + }else{ + jsonAppendString(pOut, pNode->u.zJContent, pNode->n); + } + }else if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); + }else{ + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); + } + break; + } + case JSON_REAL: { + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); + }else{ + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); + } + break; + } + case JSON_INT: { + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); + }else{ + assert( pNode->n>0 ); + jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); + } + break; + } + case JSON_ARRAY: { + u32 j = 1; + jsonAppendChar(pOut, '['); + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonAppendSeparator(pOut); + jsonRenderNode(pParse, &pNode[j], pOut); + } + j += jsonNodeSize(&pNode[j]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonAppendChar(pOut, ']'); + break; + } + case JSON_OBJECT: { + u32 j = 1; + jsonAppendChar(pOut, '{'); + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonAppendSeparator(pOut); + jsonRenderNode(pParse, &pNode[j], pOut); + jsonAppendChar(pOut, ':'); + jsonRenderNode(pParse, &pNode[j+1], pOut); + } + j += 1 + jsonNodeSize(&pNode[j+1]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonAppendChar(pOut, '}'); + break; + } + } +} +#endif /* 0 */ + + +#if 0 +/* +** Convert the JSON BLOB node an aBlob (size no greater than nBlob) +** into the return value from the function call. +*/ +static void jsonReturnFromBlob( + JsonParse *pParse, /* Complete JSON parse tree */ + JsonNode *pNode, /* Node to return */ + sqlite3_context *pCtx /* Return value for this function */ +){ + switch( pNode->eType ){ + default: { + assert( pNode->eType==JSON_NULL ); + sqlite3_result_null(pCtx); + break; + } + case JSON_TRUE: { + sqlite3_result_int(pCtx, 1); + break; + } + case JSON_FALSE: { + sqlite3_result_int(pCtx, 0); + break; + } + case JSON_INT: { + sqlite3_int64 i = 0; + int rc; + int bNeg = 0; + const char *z; + + assert( pNode->eU==1 ); + z = pNode->u.zJContent; + if( z[0]=='-' ){ z++; bNeg = 1; } + else if( z[0]=='+' ){ z++; } + rc = sqlite3DecOrHexToI64(z, &i); + if( rc<=1 ){ + sqlite3_result_int64(pCtx, bNeg ? -i : i); + }else if( rc==3 && bNeg ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + }else{ + goto to_double; + } + break; + } + case JSON_REAL: { + double r; + const char *z; + assert( pNode->eU==1 ); + to_double: + z = pNode->u.zJContent; + sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); + sqlite3_result_double(pCtx, r); + break; + } + case JSON_STRING: { + if( pNode->jnFlags & JNODE_RAW ){ + assert( pNode->eU==1 ); + sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, + SQLITE_TRANSIENT); + }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ + /* JSON formatted without any backslash-escapes */ + assert( pNode->eU==1 ); + sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, + SQLITE_TRANSIENT); + }else{ + /* Translate JSON formatted string into raw text */ + u32 i; + u32 n = pNode->n; + const char *z; + char *zOut; + u32 j; + u32 nOut = n; + assert( pNode->eU==1 ); + z = pNode->u.zJContent; + zOut = sqlite3_malloc( nOut+1 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + break; + } + for(i=1, j=0; i>6)); + zOut[j++] = 0x80 | (v&0x3f); + }else{ + u32 vlo; + if( (v&0xfc00)==0xd800 + && i>18); + zOut[j++] = 0x80 | ((v>>12)&0x3f); + zOut[j++] = 0x80 | ((v>>6)&0x3f); + zOut[j++] = 0x80 | (v&0x3f); + }else{ + zOut[j++] = 0xe0 | (v>>12); + zOut[j++] = 0x80 | ((v>>6)&0x3f); + zOut[j++] = 0x80 | (v&0x3f); + } + } + continue; + }else if( c=='b' ){ + c = '\b'; + }else if( c=='f' ){ + c = '\f'; + }else if( c=='n' ){ + c = '\n'; + }else if( c=='r' ){ + c = '\r'; + }else if( c=='t' ){ + c = '\t'; + }else if( c=='v' ){ + c = '\v'; + }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ + /* pass through unchanged */ + }else if( c=='0' ){ + c = 0; + }else if( c=='x' ){ + c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); + i += 2; + }else if( c=='\r' && z[i+1]=='\n' ){ + i++; + continue; + }else if( 0xe2==(u8)c ){ + assert( 0x80==(u8)z[i+1] ); + assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); + i += 2; + continue; + }else{ + continue; + } + } /* end if( c=='\\' ) */ + zOut[j++] = c; + } /* end for() */ + zOut[j] = 0; + sqlite3_result_text(pCtx, zOut, j, sqlite3_free); + } + break; + } + case JSON_ARRAY: + case JSON_OBJECT: { + jsonReturnJson(pParse, pNode, pCtx, 0); + break; + } + } +} +#endif /* 0 */ + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ From 90189be7ced5e35aa1d321f3ff6dccca3d1ac241 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 22 Sep 2023 12:16:56 +0000 Subject: [PATCH 006/347] Begin adding code to render binary JSON back into text. Very incomplete. This is an incremental check-in. FossilOrigin-Name: b817dd865ed60fc4da0b662a9edec0fceb8921b02ce98133bdd565988939fd0f --- manifest | 12 +- manifest.uuid | 2 +- src/json.c | 364 ++++++++++++++------------------------------------ 3 files changed, 110 insertions(+), 268 deletions(-) diff --git a/manifest b/manifest index 3c715192be..6f81a43706 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sthe\sJSON\sbinary\sBLOB\sformat\sdesign. -D 2023-09-22T11:20:35.748 +C Begin\sadding\scode\sto\srender\sbinary\sJSON\sback\sinto\stext.\s\sVery\sincomplete.\nThis\sis\san\sincremental\scheck-in. +D 2023-09-22T12:16:56.105 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4a25369c2f65c930f07f0b5b36e3a4ba86522f432035ac9204c9940c9d2dfb43 +F src/json.c 351f2b99846c6b1afdbab553c26f7cf87c303f0caa005171017a30b5d9a1aece F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 dc23e783d4147d363856abe109586fc79a5b535b492beee0cf7a0234c0210667 -R 9fd8daf58d6e7ea7726891b7477fe4a5 +P 2c89ae5d02f6a40ef869e2a162e2c72871df60572b27959fd1d7171f495ce881 +R b78d29e507a66c69faecba07ef4a8c9a U drh -Z 3129fb1588c8eb084faa14e3a492d4c9 +Z 8e9aefce50e8026f8d315b79ff0edc06 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b60349f423..757c0d1778 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2c89ae5d02f6a40ef869e2a162e2c72871df60572b27959fd1d7171f495ce881 \ No newline at end of file +b817dd865ed60fc4da0b662a9edec0fceb8921b02ce98133bdd565988939fd0f \ No newline at end of file diff --git a/src/json.c b/src/json.c index 7be2cebeb6..3f17d8b601 100644 --- a/src/json.c +++ b/src/json.c @@ -2946,292 +2946,104 @@ static int jsonParseB( return 0; } -#if 0 +/* The byte at index i is a node type-code. This routine +** determines the payload size for that node and writes that +** payload size in to *pSz. It returns the number of additional +** bytes following the typecode that are used to hold size information. +** Enhance, if the return value is N, then the start of the payload +** is at i+N+1. +*/ +static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ + u8 x; + u32 sz; + u32 n; + if( i>pParse->nBlob ){ + pParse->iErr = 1; + *pSz = 0; + return 0; + } + x = pParse->aBlob[i]>>4; + if( x<=11 ){ + sz = x; + n = 0; + }else if( x==12 ){ + if( i+1>pParse->nBlob ){ + pParse->iErr = 1; + *pSz = 0; + return 0; + } + sz = pParse->aBlob[i+1]; + n = 1; + }else if( x==13 ){ + if( i+2>pParse->nBlob ){ + pParse->iErr = 1; + *pSz = 0; + return 0; + } + sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; + n = 2; + }else{ + if( i+4>pParse->nBlob ){ + pParse->iErr = 1; + *pSz = 0; + return 0; + } + sz = (pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; + n = 4; + } + if( i+sz+n>pParse->nBlob ){ + sz = pParse->nBlob - (i+n); + pParse->iErr = 1; + } + *pSz = sz; + return n; +} + + /* ** Convert the binary BLOB representation of JSON beginning at ** aBlob[0] (and extending for no more than nBlob bytes) into ** a pure JSON string. The string is appended to pOut. */ -static void jsonRenderBlob( +static u32 jsonRenderBlob( JsonParse *pParse, /* the complete parse of the JSON */ - u8 *aBlob, /* The binary encoded JSON to be rendered */ - u32 nBlob, /* Maximum size of aBlob[...] */ + u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ ){ - u32 i; - if( nBlob<1 ){ + if( i>=pParse->nBlob ){ pOut->bErr = 1; - return; + return 0; } - switch( aBlob[0] ){ - case JSONB_NULL: + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_NULL: { jsonAppendRawNZ(pOut, "null", 4); - break; + return i+1; } case JSONB_TRUE: { jsonAppendRawNZ(pOut, "true", 4); - break; + return i+1; } case JSONB_FALSE: { jsonAppendRawNZ(pOut, "false", 5); - break; + return i+1; } - case JSONB_NUMBER: + case JSONB_INT: + case JSONB_FLOAT: case JSONB_TEXTJ: { u32 sz, n; - n = jsonbDecodeVarint(&aBlob[1], nBlob-1, &sz); - if( 1+n+sz>nBlob ){ - pOut->bErr = 1; - return; - } - jsonAppendRaw(pOut, &aBlob[1+n], sz); - break; + n = jsonbPayloadSize(pParse, i, &sz); + jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n+1], sz); + return i+n+sz; + } - case JSON_STRING: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->jnFlags & JNODE_LABEL ){ - jsonAppendChar(pOut, '"'); - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); - jsonAppendChar(pOut, '"'); - }else{ - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - } - }else if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_REAL: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_INT: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_ARRAY: { - u32 j = 1; - jsonAppendChar(pOut, '['); - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); - } - j += jsonNodeSize(&pNode[j]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, ']'); - break; - } - case JSON_OBJECT: { - u32 j = 1; - jsonAppendChar(pOut, '{'); - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); - jsonAppendChar(pOut, ':'); - jsonRenderNode(pParse, &pNode[j+1], pOut); - } - j += 1 + jsonNodeSize(&pNode[j+1]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, '}'); - break; - } - } -} -#endif /* 0 */ - - -#if 0 -/* -** Convert the JSON BLOB node an aBlob (size no greater than nBlob) -** into the return value from the function call. -*/ -static void jsonReturnFromBlob( - JsonParse *pParse, /* Complete JSON parse tree */ - JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx /* Return value for this function */ -){ - switch( pNode->eType ){ default: { - assert( pNode->eType==JSON_NULL ); - sqlite3_result_null(pCtx); - break; - } - case JSON_TRUE: { - sqlite3_result_int(pCtx, 1); - break; - } - case JSON_FALSE: { - sqlite3_result_int(pCtx, 0); - break; - } - case JSON_INT: { - sqlite3_int64 i = 0; - int rc; - int bNeg = 0; - const char *z; - - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - if( z[0]=='-' ){ z++; bNeg = 1; } - else if( z[0]=='+' ){ z++; } - rc = sqlite3DecOrHexToI64(z, &i); - if( rc<=1 ){ - sqlite3_result_int64(pCtx, bNeg ? -i : i); - }else if( rc==3 && bNeg ){ - sqlite3_result_int64(pCtx, SMALLEST_INT64); - }else{ - goto to_double; - } - break; - } - case JSON_REAL: { - double r; - const char *z; - assert( pNode->eU==1 ); - to_double: - z = pNode->u.zJContent; - sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); - sqlite3_result_double(pCtx, r); - break; - } - case JSON_STRING: { - if( pNode->jnFlags & JNODE_RAW ){ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, - SQLITE_TRANSIENT); - }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ - /* JSON formatted without any backslash-escapes */ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, - SQLITE_TRANSIENT); - }else{ - /* Translate JSON formatted string into raw text */ - u32 i; - u32 n = pNode->n; - const char *z; - char *zOut; - u32 j; - u32 nOut = n; - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - zOut = sqlite3_malloc( nOut+1 ); - if( zOut==0 ){ - sqlite3_result_error_nomem(pCtx); - break; - } - for(i=1, j=0; i>6)); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - u32 vlo; - if( (v&0xfc00)==0xd800 - && i>18); - zOut[j++] = 0x80 | ((v>>12)&0x3f); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - zOut[j++] = 0xe0 | (v>>12); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - } - } - continue; - }else if( c=='b' ){ - c = '\b'; - }else if( c=='f' ){ - c = '\f'; - }else if( c=='n' ){ - c = '\n'; - }else if( c=='r' ){ - c = '\r'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='v' ){ - c = '\v'; - }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ - /* pass through unchanged */ - }else if( c=='0' ){ - c = 0; - }else if( c=='x' ){ - c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); - i += 2; - }else if( c=='\r' && z[i+1]=='\n' ){ - i++; - continue; - }else if( 0xe2==(u8)c ){ - assert( 0x80==(u8)z[i+1] ); - assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); - i += 2; - continue; - }else{ - continue; - } - } /* end if( c=='\\' ) */ - zOut[j++] = c; - } /* end for() */ - zOut[j] = 0; - sqlite3_result_text(pCtx, zOut, j, sqlite3_free); - } - break; - } - case JSON_ARRAY: - case JSON_OBJECT: { - jsonReturnJson(pParse, pNode, pCtx, 0); break; } } + return 0; } -#endif /* 0 */ + /**************************************************************************** ** SQL functions used for testing and debugging @@ -3342,7 +3154,7 @@ static void jsonTest1Func( ** Scalar SQL function implementations ****************************************************************************/ -/* SQL Function: jsonb_test(TEXT_JSON) +/* SQL Function: jsonb_test1(TEXT_JSON) ** ** Parse TEXT JSON into the BLOB format and return the resulting BLOB. ** Development testing only. @@ -3374,6 +3186,35 @@ static void jsonbTest1( jsonParseReset(pParse); } +/* SQL Function: jsonb_test2(BLOB_JSON) +** +** Render BLOB_JSON back into text. +** Development testing only. +*/ +static void jsonbTest2( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonParse *pParse; + const u8 *aBlob; + int nBlob; + JsonParse x; + JsonString s; + UNUSED_PARAMETER(argc); + + aBlob = (const u8*)sqlite3_value_blob(argv[0]); + if( aBlob==0 ) return; + nBlob = sqlite3_value_bytes(argv[0]); + pParse = &x; + memset(&x, 0, sizeof(x)); + x.aBlob = (u8*)aBlob; + x.nBlob = nBlob; + jsonInit(&s, ctx); + jsonRenderBlob(pParse, 0, &s); + jsonResult(&s); +} + /* ** Implementation of the json_QUOTE(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting @@ -4782,6 +4623,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 2, 0, jsonTypeFunc), JFUNCTION(json_valid, 1, 0, jsonValidFunc), JFUNCTION(jsonb_test1, 1, 0, jsonbTest1), + JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func), From c1c1d4d4a6e01a35729e4833b902901ce2cc62fa Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 22 Sep 2023 14:33:39 +0000 Subject: [PATCH 007/347] Add the ability to render a binary BLOB back into valid canonical JSON. FossilOrigin-Name: 0b70cb77a4c8e3f17932f1ecca3942e0b0b03de637fb9656a130fe045f7ef826 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 205 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 177 insertions(+), 42 deletions(-) diff --git a/manifest b/manifest index 6f81a43706..85abeb56b9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\sadding\scode\sto\srender\sbinary\sJSON\sback\sinto\stext.\s\sVery\sincomplete.\nThis\sis\san\sincremental\scheck-in. -D 2023-09-22T12:16:56.105 +C Add\sthe\sability\sto\srender\sa\sbinary\sBLOB\sback\sinto\svalid\scanonical\sJSON. +D 2023-09-22T14:33:39.536 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 351f2b99846c6b1afdbab553c26f7cf87c303f0caa005171017a30b5d9a1aece +F src/json.c 574ed4c90fc5a18d40665557c56987d6ad1cd78644fdd698b755002538252de0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 2c89ae5d02f6a40ef869e2a162e2c72871df60572b27959fd1d7171f495ce881 -R b78d29e507a66c69faecba07ef4a8c9a +P b817dd865ed60fc4da0b662a9edec0fceb8921b02ce98133bdd565988939fd0f +R f9832ae6b1e64d76b924944a3c617b61 U drh -Z 8e9aefce50e8026f8d315b79ff0edc06 +Z 86dcd161b824c66e770c11b16ee3a065 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 757c0d1778..3622ea106f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b817dd865ed60fc4da0b662a9edec0fceb8921b02ce98133bdd565988939fd0f \ No newline at end of file +0b70cb77a4c8e3f17932f1ecca3942e0b0b03de637fb9656a130fe045f7ef826 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3f17d8b601..6bfa0f6ce8 100644 --- a/src/json.c +++ b/src/json.c @@ -124,18 +124,19 @@ struct JsonCleanup { /* JSON BLOB node types */ -#define JSONB_NULL 0 -#define JSONB_TRUE 1 -#define JSONB_FALSE 2 -#define JSONB_INT 3 -#define JSONB_INT5 4 -#define JSONB_FLOAT 5 -#define JSONB_FLOAT5 6 -#define JSONB_TEXT 7 -#define JSONB_TEXTJ 8 -#define JSONB_TEXT5 9 -#define JSONB_ARRAY 10 -#define JSONB_OBJECT 11 +#define JSONB_NULL 0 /* "null" */ +#define JSONB_TRUE 1 /* "true" */ +#define JSONB_FALSE 2 /* "false" */ +#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ +#define JSONB_INT5 4 /* integer in 0x000 notation */ +#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ +#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ +#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ +#define JSONB_TEXTJ 8 /* Text with JSON escapes */ +#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ +#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ +#define JSONB_ARRAY 11 /* An array */ +#define JSONB_OBJECT 12 /* An object */ /* The "subtype" set for JSON values */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ @@ -2493,7 +2494,7 @@ json_parse_restart: k++; } assert( iBlob==pParse->nBlob ); - jsonBlobAppendNodeType(pParse, JSONB_TEXT5, k-j); + jsonBlobAppendNodeType(pParse, JSONB_TEXTJ, k-j); jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j); pParse->hasNonstd = 1; x = k; @@ -2504,7 +2505,7 @@ json_parse_restart: } if( pParse->oom ) return -1; t = pParse->aBlob[iBlob] & 0x0f; - if( tJSONB_TEXT5 ){ + if( tJSONB_TEXTRAW ){ pParse->iErr = j; return -1; } @@ -2661,8 +2662,8 @@ json_parse_restart: return -1; } } - jsonBlobAppendNodeType(pParse, opcode, j+1-i); - jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j+1-i); + jsonBlobAppendNodeType(pParse, opcode, j-1-i); + jsonBlobAppendNBytes(pParse, (const u8*)&z[i+1], j-1-i); return j+1; } case 't': { @@ -2684,7 +2685,7 @@ json_parse_restart: case '+': { u8 seenE; pParse->hasNonstd = 1; - t = 0x01; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ + t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ goto parse_number; case '.': if( sqlite3Isdigit(z[i+1]) ){ @@ -2820,6 +2821,7 @@ json_parse_restart: assert( JSONB_INT+0x01==JSONB_INT5 ); assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); assert( JSONB_INT+0x02==JSONB_FLOAT ); + if( z[i]=='+' ) i++; jsonBlobAppendNodeType(pParse, JSONB_INT+t, j-i); jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i); return j; @@ -2948,10 +2950,8 @@ static int jsonParseB( /* The byte at index i is a node type-code. This routine ** determines the payload size for that node and writes that -** payload size in to *pSz. It returns the number of additional -** bytes following the typecode that are used to hold size information. -** Enhance, if the return value is N, then the start of the payload -** is at i+N+1. +** payload size in to *pSz. It returns the offset from i to the +** beginning of the payload. */ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ u8 x; @@ -2960,37 +2960,37 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ if( i>pParse->nBlob ){ pParse->iErr = 1; *pSz = 0; - return 0; + return 1; } x = pParse->aBlob[i]>>4; if( x<=11 ){ sz = x; - n = 0; + n = 1; }else if( x==12 ){ if( i+1>pParse->nBlob ){ pParse->iErr = 1; *pSz = 0; - return 0; + return 1; } sz = pParse->aBlob[i+1]; - n = 1; + n = 2; }else if( x==13 ){ if( i+2>pParse->nBlob ){ pParse->iErr = 1; *pSz = 0; - return 0; + return 1; } sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; - n = 2; + n = 3; }else{ if( i+4>pParse->nBlob ){ pParse->iErr = 1; *pSz = 0; - return 0; + return 1; } sz = (pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; - n = 4; + n = 5; } if( i+sz+n>pParse->nBlob ){ sz = pParse->nBlob - (i+n); @@ -3011,6 +3011,8 @@ static u32 jsonRenderBlob( u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ ){ + u32 sz, n, j, iEnd; + if( i>=pParse->nBlob ){ pOut->bErr = 1; return 0; @@ -3029,19 +3031,152 @@ static u32 jsonRenderBlob( return i+1; } case JSONB_INT: - case JSONB_FLOAT: - case JSONB_TEXTJ: { - u32 sz, n; + case JSONB_FLOAT: { n = jsonbPayloadSize(pParse, i, &sz); - jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n+1], sz); - return i+n+sz; + jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); + break; + } + case JSONB_INT5: { /* Integer literal in hexadecimal notation */ + int k; + sqlite3_uint64 u = 0; + n = jsonbPayloadSize(pParse, i, &sz); + const char *zIn = (const char*)&pParse->aBlob[i+n]; + if( zIn[0]=='-' ){ + zIn++; + sz--; + jsonAppendChar(pOut, '-'); + } + for(k=2; kaBlob[i+n]; + if( zIn[0]=='-' ){ + zIn++; + sz--; + jsonAppendChar(pOut, '-'); + } + if( zIn[0]=='.' ){ + jsonAppendChar(pOut, '0'); + } + for(k=0; k0 ){ + jsonAppendRawNZ(pOut, zIn, sz); + } + break; + } + case JSONB_TEXT: + case JSONB_TEXTJ: { + n = jsonbPayloadSize(pParse, i, &sz); + jsonAppendChar(pOut, '"'); + jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); + jsonAppendChar(pOut, '"'); + break; + } + case JSONB_TEXT5: { + const char *zIn; + u32 k; + n = jsonbPayloadSize(pParse, i, &sz); + zIn = (const char*)&pParse->aBlob[i+n]; + jsonAppendChar(pOut, '"'); + while( sz>0 ){ + for(k=0; k0 ){ + jsonAppendRawNZ(pOut, zIn, k); + zIn += k; + sz -= k; + if( sz==0 ) break; + } + assert( zIn[0]=='\\' ); + switch( (u8)zIn[1] ){ + case '\'': + jsonAppendChar(pOut, '\''); + break; + case 'v': + jsonAppendRawNZ(pOut, "\\u0009", 6); + break; + case 'x': + jsonAppendRawNZ(pOut, "\\u00", 4); + jsonAppendRawNZ(pOut, &zIn[2], 2); + zIn += 2; + sz -= 2; + break; + case '0': + jsonAppendRawNZ(pOut, "\\u0000", 6); + break; + case '\r': + if( zIn[2]=='\n' ){ + zIn++; + sz--; + } + break; + case '\n': + break; + case 0xe2: + assert( sz>=4 ); + assert( 0x80==(u8)zIn[2] ); + assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); + zIn += 2; + sz -= 2; + break; + default: + jsonAppendRawNZ(pOut, zIn, 2); + break; + } + zIn += 2; + sz -= 2; + } + jsonAppendChar(pOut, '"'); + break; + } + case JSONB_ARRAY: { + jsonAppendChar(pOut, '['); + n = jsonbPayloadSize(pParse, i, &sz); + j = i+n; + iEnd = j+sz; + while( 1 /* exit by break */ ){ + j = jsonRenderBlob(pParse, j, pOut); + if( j>=iEnd ) break; + jsonAppendChar(pOut, ','); + } + jsonAppendChar(pOut, ']'); + break; + } + case JSONB_OBJECT: { + int x = 0; + jsonAppendChar(pOut, '{'); + n = jsonbPayloadSize(pParse, i, &sz); + j = i+n; + iEnd = j+sz; + while( 1 /* edit by break */ ){ + j = jsonRenderBlob(pParse, j, pOut); + if( j>=iEnd ) break; + jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); + } + jsonAppendChar(pOut, '}'); + break; } default: { + n = jsonbPayloadSize(pParse, i, &sz); break; } } - return 0; + return i+n+sz; } From e367e453e140bb39a9df756713d28b75cff7d2e4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 22 Sep 2023 16:20:48 +0000 Subject: [PATCH 008/347] Fix minor parse-to-BLOB bugs. FossilOrigin-Name: 8b53b2e6600c324ff7864840d98a3f03896b9792fcb60b70cc1f6227b3bd4ca1 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 85abeb56b9..1e08af59f7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sability\sto\srender\sa\sbinary\sBLOB\sback\sinto\svalid\scanonical\sJSON. -D 2023-09-22T14:33:39.536 +C Fix\sminor\sparse-to-BLOB\sbugs. +D 2023-09-22T16:20:48.168 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 574ed4c90fc5a18d40665557c56987d6ad1cd78644fdd698b755002538252de0 +F src/json.c 522ebfebd77224594e2fcd9ea18e201ac8b997d5f1cf2c71d8eba00e874b54b2 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 b817dd865ed60fc4da0b662a9edec0fceb8921b02ce98133bdd565988939fd0f -R f9832ae6b1e64d76b924944a3c617b61 +P 0b70cb77a4c8e3f17932f1ecca3942e0b0b03de637fb9656a130fe045f7ef826 +R e81e3fd90e402a6e846b93a0d3beab83 U drh -Z 86dcd161b824c66e770c11b16ee3a065 +Z 285487434c996c07680e15a392e180f6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3622ea106f..94c65ef3d9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0b70cb77a4c8e3f17932f1ecca3942e0b0b03de637fb9656a130fe045f7ef826 \ No newline at end of file +8b53b2e6600c324ff7864840d98a3f03896b9792fcb60b70cc1f6227b3bd4ca1 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6bfa0f6ce8..ac8976d03c 100644 --- a/src/json.c +++ b/src/json.c @@ -2480,7 +2480,7 @@ json_parse_restart: if( x<=0 ){ if( x==(-2) ){ j = pParse->iErr; - if( pParse->nBlob!=(u32)iThis+1 ) pParse->hasNonstd = 1; + if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; break; } j += json5Whitespace(&z[j]); @@ -2560,7 +2560,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - if( pParse->nErr==0 ){ + if( pParse->oom==0 ){ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); } pParse->iDepth--; @@ -2581,7 +2581,7 @@ json_parse_restart: if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; - if( pParse->nBlob!=iThis+4 ) pParse->hasNonstd = 1; + if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1; break; } if( x!=(-1) ) pParse->iErr = j; @@ -2614,7 +2614,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - if( pParse->iErr==0 ){ + if( pParse->oom==0 ){ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); } pParse->iDepth--; @@ -3148,11 +3148,11 @@ static u32 jsonRenderBlob( n = jsonbPayloadSize(pParse, i, &sz); j = i+n; iEnd = j+sz; - while( 1 /* exit by break */ ){ + while( j=iEnd ) break; jsonAppendChar(pOut, ','); } + if( sz>0 ) pOut->nUsed--; jsonAppendChar(pOut, ']'); break; } @@ -3162,11 +3162,11 @@ static u32 jsonRenderBlob( n = jsonbPayloadSize(pParse, i, &sz); j = i+n; iEnd = j+sz; - while( 1 /* edit by break */ ){ + while( j=iEnd ) break; jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); } + if( sz>0 ) pOut->nUsed--; jsonAppendChar(pOut, '}'); break; } From 5933581cf035d1479752ebdd3bce84ae609c215b Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 25 Sep 2023 13:23:29 +0000 Subject: [PATCH 009/347] As a temporary measure, try to translate the BLOB JSON format into the legacy node format for processing. FossilOrigin-Name: 14f2e95a9e531ef0d3fa7f1249f23c073a50c31b2109eefc2f258cada635ac2f --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 225 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 198 insertions(+), 41 deletions(-) diff --git a/manifest b/manifest index 1e08af59f7..3fd91c08fe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sminor\sparse-to-BLOB\sbugs. -D 2023-09-22T16:20:48.168 +C As\sa\stemporary\smeasure,\stry\sto\stranslate\sthe\sBLOB\sJSON\sformat\sinto\sthe\nlegacy\snode\sformat\sfor\sprocessing. +D 2023-09-25T13:23:29.913 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 522ebfebd77224594e2fcd9ea18e201ac8b997d5f1cf2c71d8eba00e874b54b2 +F src/json.c 23efc117c4641118c2154938decc80fb3b29b1a420bcd0eaefe24ef808d1ada4 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 0b70cb77a4c8e3f17932f1ecca3942e0b0b03de637fb9656a130fe045f7ef826 -R e81e3fd90e402a6e846b93a0d3beab83 +P 8b53b2e6600c324ff7864840d98a3f03896b9792fcb60b70cc1f6227b3bd4ca1 +R 31821ddd534254fa82bb41b5d469060a U drh -Z 285487434c996c07680e15a392e180f6 +Z 25f12fc9cea10e56c4709ee1d21954ff # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 94c65ef3d9..1d3c76b2dd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8b53b2e6600c324ff7864840d98a3f03896b9792fcb60b70cc1f6227b3bd4ca1 \ No newline at end of file +14f2e95a9e531ef0d3fa7f1249f23c073a50c31b2109eefc2f258cada635ac2f \ No newline at end of file diff --git a/src/json.c b/src/json.c index ac8976d03c..afa152624e 100644 --- a/src/json.c +++ b/src/json.c @@ -220,6 +220,7 @@ struct JsonParse { u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ u8 useMod; /* Actually use the edits contain inside aNode */ u8 hasMod; /* aNode contains edits from the original zJson */ + u8 isBinary; /* True if zJson is the binary encoding */ u32 nJPRef; /* Number of references to this object */ int nJson; /* Length of the zJson string in bytes */ int nAlt; /* Length of alternative JSON string zAlt, in bytes */ @@ -1809,6 +1810,9 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ } } +/* Forward reference */ +static int jsonParseValueFromBinary(JsonParse *pParse, u32 i); + /* ** Parse a complete JSON string. Return 0 on success or non-zero if there ** are any errors. If an error occurs, free all memory held by pParse, @@ -1823,7 +1827,13 @@ static int jsonParse( ){ int i; const char *zJson = pParse->zJson; - i = jsonParseValue(pParse, 0); + if( pParse->isBinary ){ + pParse->aBlob = (u8*)pParse->zJson; + pParse->nBlob = pParse->nJson; + i = jsonParseValueFromBinary(pParse, 0); + }else{ + i = jsonParseValue(pParse, 0); + } if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); @@ -1872,6 +1882,10 @@ static int jsonParseFindParents(JsonParse *pParse){ #define JSON_CACHE_ID (-429938) /* First cache entry */ #define JSON_CACHE_SZ 4 /* Max number of cache entries */ +/* Forward reference */ +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); + + /* ** Obtain a complete parse of the JSON found in the pJson argument ** @@ -1906,8 +1920,8 @@ static JsonParse *jsonParseCached( sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ int bUnedited /* No prior edits allowed */ ){ - char *zJson = (char*)sqlite3_value_text(pJson); - int nJson = sqlite3_value_bytes(pJson); + char *zJson; + int nJson; JsonParse *p; JsonParse *pMatch = 0; int iKey; @@ -1915,6 +1929,16 @@ static JsonParse *jsonParseCached( u32 iMinHold = 0xffffffff; u32 iMaxHold = 0; int bJsonRCStr; + int isBinary; + + if( jsonFuncArgMightBeBinary(pJson) ){ + zJson = (char*)sqlite3_value_blob(pJson); + isBinary = 1; + }else{ + zJson = (char*)sqlite3_value_text(pJson); + isBinary = 0; + } + nJson = sqlite3_value_bytes(pJson); if( zJson==0 ) return 0; for(iKey=0; iKeybJsonIsRCStr = 1; }else{ p->zJson = (char*)&p[1]; - memcpy(p->zJson, zJson, nJson+1); + memcpy(p->zJson, zJson, nJson+(isBinary==0)); } p->nJPRef = 1; + p->isBinary = isBinary; + p->nJson = nJson; if( jsonParse(p, pErrCtx) ){ if( pErrCtx==0 ){ p->nErr = 1; @@ -1985,7 +2011,6 @@ static JsonParse *jsonParseCached( jsonParseFree(p); return 0; } - p->nJson = nJson; p->iHold = iMaxHold+1; /* Transfer ownership of the new JsonParse to the cache */ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, @@ -2951,16 +2976,15 @@ static int jsonParseB( /* The byte at index i is a node type-code. This routine ** determines the payload size for that node and writes that ** payload size in to *pSz. It returns the offset from i to the -** beginning of the payload. +** beginning of the payload. Return 0 on error. */ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; if( i>pParse->nBlob ){ - pParse->iErr = 1; *pSz = 0; - return 1; + return 0; } x = pParse->aBlob[i]>>4; if( x<=11 ){ @@ -2968,25 +2992,22 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ n = 1; }else if( x==12 ){ if( i+1>pParse->nBlob ){ - pParse->iErr = 1; *pSz = 0; - return 1; + return 0; } sz = pParse->aBlob[i+1]; n = 2; }else if( x==13 ){ if( i+2>pParse->nBlob ){ - pParse->iErr = 1; *pSz = 0; - return 1; + return 0; } sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; n = 3; }else{ if( i+4>pParse->nBlob ){ - pParse->iErr = 1; *pSz = 0; - return 1; + return 0; } sz = (pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; @@ -2994,7 +3015,6 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ } if( i+sz+n>pParse->nBlob ){ sz = pParse->nBlob - (i+n); - pParse->iErr = 1; } *pSz = sz; return n; @@ -3179,6 +3199,124 @@ static u32 jsonRenderBlob( return i+n+sz; } +/* Return true if the input pJson +** +** For performance reasons, this routine does not do a detailed check of the +** input BLOB to ensure that it is well-formed. Hence, false positives are +** possible. False negatives should never occur, however. +*/ +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ + u32 sz, n; + const u8 *aBlob; + int nBlob; + JsonParse s; + if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; + nBlob = sqlite3_value_bytes(pJson); + if( nBlob<1 ) return 0; + aBlob = sqlite3_value_blob(pJson); + if( (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; + memset(&s, 0, sizeof(s)); + s.aBlob = (u8*)aBlob; + s.nBlob = nBlob; + n = jsonbPayloadSize(&s, 0, &sz); + if( n==0 ) return 0; + return sz+n==(u32)nBlob; +} + +/* Parse a single element of binary JSON into the legacy Node structure. +** Return the index of the first byte past the end of the binary JSON element. +** +** This routine is a temporary translator while the legacy Node encoding +** is still in use. It will be removed after all processing translates +** to the new BLOB encoding. +*/ +static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ + u8 t; /* Node type */ + u32 sz; /* Node size */ + u32 x; /* Index of payload start */ + + const char *zPayload; + x = jsonbPayloadSize(pParse, i, &sz); + if( x==0 ) return -1; + t = pParse->zJson[i] & 0x0f; + zPayload = &pParse->zJson[i+x]; + switch( t ){ + case JSONB_NULL: { + jsonParseAddNode(pParse, JSON_NULL, 0, 0); + break; + } + case JSONB_TRUE: { + jsonParseAddNode(pParse, JSON_TRUE, 0, 0); + break; + } + case JSONB_FALSE: { + jsonParseAddNode(pParse, JSON_FALSE, 0, 0); + break; + } + case JSONB_INT: { + jsonParseAddNode(pParse, JSON_INT, sz, zPayload); + break; + } + case JSONB_INT5: { + pParse->hasNonstd = 1; + jsonParseAddNode(pParse, JSON_INT | (JNODE_JSON5<<8), sz, zPayload); + break; + } + case JSONB_FLOAT: { + jsonParseAddNode(pParse, JSON_REAL, sz, zPayload); + break; + } + case JSONB_FLOAT5: { + pParse->hasNonstd = 1; + jsonParseAddNode(pParse, JSON_REAL | (JNODE_JSON5<<8), sz, zPayload); + break; + } + case JSONB_TEXT: { + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); + break; + } + case JSONB_TEXTJ: { + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); + break; + } + case JSONB_TEXT5: { + pParse->hasNonstd = 1; + jsonParseAddNode(pParse, JSON_STRING | ((JNODE_ESCAPE|JNODE_JSON5)<<8), + sz, zPayload); + break; + } + case JSONB_TEXTRAW: { + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); + break; + } + case JSONB_ARRAY: { + int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); + u32 j = i+x; + while( jaNode[iThis].n = pParse->nNode - (u32)iThis - 1; + break; + } + case JSONB_OBJECT: { + int iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); + u32 j = i+x, k = 0; + while( jaNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; + } + j = (u32)r; + } + pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + break; + } + } + return i+x+sz; +} /**************************************************************************** ** SQL functions used for testing and debugging @@ -3289,12 +3427,23 @@ static void jsonTest1Func( ** Scalar SQL function implementations ****************************************************************************/ -/* SQL Function: jsonb_test1(TEXT_JSON) +/* SQL Function: jsonb(JSON) ** -** Parse TEXT JSON into the BLOB format and return the resulting BLOB. -** Development testing only. +** Convert the input argument into JSONB (the SQLite binary encoding of +** JSON). +** +** If the input is TEXT, or NUMERIC, try to parse it as JSON. If the fails, +** raise an error. Otherwise, return the resulting BLOB value. +** +** If the input is a BLOB, check to see if the input is a plausible +** JSONB. If it is, return it unchanged. Raise an error if it is not. +** Note that there could be internal inconsistencies in the BLOB - this +** routine does not do a full byte-for-byte validity check of the +** JSON blob. +** +** If the input is NULL, return NULL. */ -static void jsonbTest1( +static void jsonbFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv @@ -3305,20 +3454,28 @@ static void jsonbTest1( JsonParse x; UNUSED_PARAMETER(argc); - zJson = (const char*)sqlite3_value_text(argv[0]); - if( zJson==0 ) return; - nJson = sqlite3_value_bytes(argv[0]); - pParse = &x; - memset(&x, 0, sizeof(x)); - x.zJson = (char*)zJson; - x.nJson = nJson; - if( jsonParseB(pParse, ctx)==0 && pParse->aBlob!=0 ){ - sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); - pParse->aBlob = 0; - pParse->nBlob = 0; - pParse->nBlobAlloc = 0; + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ + /* No-op */ + }else if( jsonFuncArgMightBeBinary(argv[0]) ){ + sqlite3_result_value(ctx, argv[0]); + }else{ + zJson = (const char*)sqlite3_value_text(argv[0]); + if( zJson==0 ) return; + nJson = sqlite3_value_bytes(argv[0]); + pParse = &x; + memset(&x, 0, sizeof(x)); + x.zJson = (char*)zJson; + x.nJson = nJson; + if( jsonParseB(pParse, ctx) ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + }else{ + sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; + } + jsonParseReset(pParse); } - jsonParseReset(pParse); } /* SQL Function: jsonb_test2(BLOB_JSON) @@ -3351,7 +3508,7 @@ static void jsonbTest2( } /* -** Implementation of the json_QUOTE(VALUE) function. Return a JSON value +** Implementation of the json_quote(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting ** double-quotes around strings and returning the unquoted string "null" ** when given a NULL input. @@ -4757,7 +4914,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 1, 0, jsonTypeFunc), JFUNCTION(json_type, 2, 0, jsonTypeFunc), JFUNCTION(json_valid, 1, 0, jsonValidFunc), - JFUNCTION(jsonb_test1, 1, 0, jsonbTest1), + JFUNCTION(jsonb, 1, 0, jsonbFunc), JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), From 09f23d2a368ca28d2d1cbaef0073b9cc71efd835 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 25 Sep 2023 17:14:17 +0000 Subject: [PATCH 010/347] When parsing JSON text into the BLOB format, only use node type JSONB_TEXTJ for an unquoted object label if the object label contains escape sequences. FossilOrigin-Name: a82ebbac3c542ec7f86d1e8414d7fd166db48450115ee3b26d12b5bb445f5896 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 27 ++++++++++++++++++++++----- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 3fd91c08fe..9cc27fb47c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C As\sa\stemporary\smeasure,\stry\sto\stranslate\sthe\sBLOB\sJSON\sformat\sinto\sthe\nlegacy\snode\sformat\sfor\sprocessing. -D 2023-09-25T13:23:29.913 +C When\sparsing\sJSON\stext\sinto\sthe\sBLOB\sformat,\sonly\suse\snode\stype\sJSONB_TEXTJ\nfor\san\sunquoted\sobject\slabel\sif\sthe\sobject\slabel\scontains\sescape\ssequences. +D 2023-09-25T17:14:17.033 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 23efc117c4641118c2154938decc80fb3b29b1a420bcd0eaefe24ef808d1ada4 +F src/json.c ee823133ed6f56ffb42c99fa78814667be70cd1b8106714cff459ee35f5a063e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2121,8 +2121,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 8b53b2e6600c324ff7864840d98a3f03896b9792fcb60b70cc1f6227b3bd4ca1 -R 31821ddd534254fa82bb41b5d469060a +P 14f2e95a9e531ef0d3fa7f1249f23c073a50c31b2109eefc2f258cada635ac2f +R 0f073ae43da95216a48accf876602e9c U drh -Z 25f12fc9cea10e56c4709ee1d21954ff +Z 7639209733392bafef0d3cec4e521036 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1d3c76b2dd..93079e8a7a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14f2e95a9e531ef0d3fa7f1249f23c073a50c31b2109eefc2f258cada635ac2f \ No newline at end of file +a82ebbac3c542ec7f86d1e8414d7fd166db48450115ee3b26d12b5bb445f5896 \ No newline at end of file diff --git a/src/json.c b/src/json.c index afa152624e..cfd37754f8 100644 --- a/src/json.c +++ b/src/json.c @@ -2465,6 +2465,21 @@ static void jsonBlobChangePayloadSize( } } +/* +** If z[0] is 'u' and is followed by exactly 4 hexadecimal character, +** then set *pOp to JSONB_TEXTJ and return true. If not, do not make +** any changes to *pOp and return false. +*/ +static int jsonIs4HexB(const char *z, int *pOp){ + if( z[0]!='u' ) return 0; + if( !sqlite3Isxdigit(z[1]) ) return 0; + if( !sqlite3Isxdigit(z[2]) ) return 0; + if( !sqlite3Isxdigit(z[3]) ) return 0; + if( !sqlite3Isxdigit(z[4]) ) return 0; + *pOp = JSONB_TEXTJ; + return 1; +} + /* ** Parse a single JSON text value which begins at pParse->zJson[i] into ** its equivalent BLOB representation in pParse->aBlob[]. The parse is @@ -2503,23 +2518,25 @@ json_parse_restart: u32 iBlob = pParse->nBlob; x = jsonParseValueB(pParse, j); if( x<=0 ){ + int op; if( x==(-2) ){ j = pParse->iErr; if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; break; } j += json5Whitespace(&z[j]); - if( sqlite3JsonId1(z[j]) - || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) + op = JSONB_TEXT; + if( sqlite3JsonId1(z[j]) + || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op)) ){ int k = j+1; while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) - || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) + || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op)) ){ k++; } assert( iBlob==pParse->nBlob ); - jsonBlobAppendNodeType(pParse, JSONB_TEXTJ, k-j); + jsonBlobAppendNodeType(pParse, op, k-j); jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j); pParse->hasNonstd = 1; x = k; @@ -3276,7 +3293,7 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ break; } case JSONB_TEXTJ: { - jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); + jsonParseAddNode(pParse, JSON_STRING | (JNODE_ESCAPE<<8), sz, zPayload); break; } case JSONB_TEXT5: { From 42156fd90c65caa8f9f86a76624408f4dd8a3ef5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 26 Sep 2023 19:30:46 +0000 Subject: [PATCH 011/347] Add in many jsonb_xxxx() interfaces. Still uses the internal JsonNode representation for transformations and search, but it does at least conform to the desired API design. Largely untested. FossilOrigin-Name: e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 225 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 197 insertions(+), 42 deletions(-) diff --git a/manifest b/manifest index 06d1240d09..80c8adf66b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sjsonb\sbranch. -D 2023-09-26T15:13:04.116 +C Add\sin\smany\sjsonb_xxxx()\sinterfaces.\s\sStill\suses\sthe\sinternal\sJsonNode\nrepresentation\sfor\stransformations\sand\ssearch,\sbut\sit\sdoes\sat\sleast\sconform\nto\sthe\sdesired\sAPI\sdesign.\s\sLargely\suntested. +D 2023-09-26T19:30:46.857 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c ee823133ed6f56ffb42c99fa78814667be70cd1b8106714cff459ee35f5a063e +F src/json.c 6fb31345d252dc6992c707ec7e826ec9f2283b026092e5105f9905664a2210c5 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2122,8 +2122,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 a82ebbac3c542ec7f86d1e8414d7fd166db48450115ee3b26d12b5bb445f5896 7ad38254c37153efa72291d09800693ca60894359548eda877d59defa8c70d49 -R 2f21564c602d3b30115c3421d174134f +P ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f +R 3743d1571a08e262e44d6bfa0fe7cd5d U drh -Z bdae2aab159970d0c164367006c6960b +Z 4bd7fe1646cada0525f1a26ea76a3776 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4de851b60c..a8e9449ee7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac242c4d47ec36aab1c2fa5e65e7b595e686f49473b75bd63708d05c59ce3f0f \ No newline at end of file +e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17 \ No newline at end of file diff --git a/src/json.c b/src/json.c index cfd37754f8..0b5068a9f0 100644 --- a/src/json.c +++ b/src/json.c @@ -159,6 +159,16 @@ static const char * const jsonType[] = { #define JNODE_LABEL 0x20 /* Is a label of an object */ #define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */ +/* +** Bit values for the flags passed into jsonExtractFunc() or +** jsonSetFunc() via the user-data value. +*/ +#define JSON_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ +#define JSON_BLOB 0x08 /* Use the BLOB output format */ + /* A single node of parsed JSON. An array of these nodes describes ** a parse of JSON + edits. @@ -847,6 +857,13 @@ static void jsonRenderNode( } } +/* Forward reference */ +static void jsonRenderNodeAsBlob( + JsonParse *pParse, /* the complete parse of the JSON */ + JsonNode *pNode, /* The node to render */ + JsonParse *pOut /* Write the BLOB rendering of JSON here */ +); + /* ** Return a JsonNode and all its descendants as a JSON string. */ @@ -856,12 +873,22 @@ static void jsonReturnJson( sqlite3_context *pCtx, /* Return value for this function */ int bGenerateAlt /* Also store the rendered text in zAlt */ ){ + int flags; JsonString s; if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); return; } - if( pParse->nErr==0 ){ + if( pParse->nErr ){ + return; + } + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); + if( flags & JSON_BLOB ){ + JsonParse x; + memset(&x, 0, sizeof(x)); + jsonRenderNodeAsBlob(pParse, pNode, &x); + sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); + }else{ jsonInit(&s, pCtx); jsonRenderNode(pParse, pNode, &s); if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ @@ -1811,7 +1838,7 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ } /* Forward reference */ -static int jsonParseValueFromBinary(JsonParse *pParse, u32 i); +static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); /* ** Parse a complete JSON string. Return 0 on success or non-zero if there @@ -1830,7 +1857,7 @@ static int jsonParse( if( pParse->isBinary ){ pParse->aBlob = (u8*)pParse->zJson; pParse->nBlob = pParse->nJson; - i = jsonParseValueFromBinary(pParse, 0); + i = jsonParseValueFromBlob(pParse, 0); }else{ i = jsonParseValue(pParse, 0); } @@ -3247,7 +3274,7 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ ** is still in use. It will be removed after all processing translates ** to the new BLOB encoding. */ -static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ +static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ u8 t; /* Node type */ u32 sz; /* Node size */ u32 x; /* Index of payload start */ @@ -3310,7 +3337,7 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); u32 j = i+x; while( jaNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; @@ -3335,6 +3362,137 @@ static int jsonParseValueFromBinary(JsonParse *pParse, u32 i){ return i+x+sz; } +/* +** Convert pNode that belongs to pParse into the BLOB representation. +** The BLOB representation is added to the pOut->aBlob[] array. +*/ +static void jsonRenderNodeAsBlob( + JsonParse *pParse, /* the complete parse of the JSON */ + JsonNode *pNode, /* The node to render */ + JsonParse *pOut /* Write the BLOB rendering of JSON here */ +){ + assert( pNode!=0 ); + while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ + u32 idx = (u32)(pNode - pParse->aNode); + u32 i = pParse->iSubst; + while( 1 /*exit-by-break*/ ){ + assert( inNode ); + assert( pParse->aNode[i].eType==JSON_SUBST ); + assert( pParse->aNode[i].eU==4 ); + assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ + pNode = &pParse->aNode[i+1]; + break; + } + i = pParse->aNode[i].u.iPrev; + } + } + switch( pNode->eType ){ + default: { + assert( pNode->eType==JSON_NULL ); + jsonBlobAppendNodeType(pOut, JSONB_NULL, 0); + break; + } + case JSON_TRUE: { + jsonBlobAppendNodeType(pOut, JSONB_TRUE, 0); + break; + } + case JSON_FALSE: { + jsonBlobAppendNodeType(pOut, JSONB_FALSE, 0); + break; + } + case JSON_STRING: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_RAW ){ + if( memchr(pNode->u.zJContent, '"', pNode->n)==0 + && memchr(pNode->u.zJContent, '\\', pNode->n)==0 + ){ + op = JSONB_TEXT; + }else{ + op = JSONB_TEXTRAW; + } + }else if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_TEXT5; + }else{ + op = JSONB_TEXTJ; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_REAL: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_FLOAT5; + }else{ + assert( pNode->n>0 ); + op = JSONB_FLOAT; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_INT: { + int op; + assert( pNode->eU==1 ); + if( pNode->jnFlags & JNODE_JSON5 ){ + op = JSONB_INT5; + }else{ + assert( pNode->n>0 ); + op = JSONB_INT; + } + jsonBlobAppendNodeType(pOut, op, pNode->n); + jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); + break; + } + case JSON_ARRAY: { + u32 j = 1; + u32 iStart, iThis = pOut->nBlob; + jsonBlobAppendNodeType(pOut, JSONB_ARRAY, 0x7fffffff); + iStart = pOut->nBlob; + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); + } + j += jsonNodeSize(&pNode[j]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); + break; + } + case JSON_OBJECT: { + u32 j = 1; + u32 iStart, iThis = pOut->nBlob; + jsonBlobAppendNodeType(pOut, JSONB_OBJECT, 0x7fffffff); + iStart = pOut->nBlob; + for(;;){ + while( j<=pNode->n ){ + if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); + jsonRenderNodeAsBlob(pParse, &pNode[j+1], pOut); + } + j += 1 + jsonNodeSize(&pNode[j+1]); + } + if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pNode->eU==2 ); + pNode = &pParse->aNode[pNode->u.iAppend]; + j = 1; + } + jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); + break; + } + } +} + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ @@ -3614,15 +3772,6 @@ static void jsonArrayLengthFunc( sqlite3_result_int64(ctx, n); } -/* -** Bit values for the flags passed into jsonExtractFunc() or -** jsonSetFunc() via the user-data value. -*/ -#define JSON_JSON 0x01 /* Result is always JSON */ -#define JSON_SQL 0x02 /* Result is always SQL */ -#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ -#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ - /* ** json_extract(JSON, PATH, ...) ** "->"(JSON,PATH) @@ -4913,26 +5062,32 @@ static sqlite3_module jsonTreeModule = { void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - JFUNCTION(json, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 0, jsonValidFunc), - JFUNCTION(jsonb, 1, 0, jsonbFunc), - JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1, 0, jsonErrorFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), + JFUNCTION(jsonb, 1, JSON_BLOB, jsonbFunc), + JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), + JFUNCTION(jsonb_insert, -1, JSON_BLOB, jsonSetFunc), + JFUNCTION(jsonb_patch, 2, JSON_BLOB, jsonPatchFunc), + JFUNCTION(jsonb_quote, 1, JSON_BLOB, jsonQuoteFunc), + JFUNCTION(jsonb_remove, -1, JSON_BLOB, jsonRemoveFunc), + JFUNCTION(jsonb_replace, -1, JSON_BLOB, jsonReplaceFunc), + JFUNCTION(jsonb_set, -1, JSON_ISSET|JSON_BLOB, jsonSetFunc), #if SQLITE_DEBUG JFUNCTION(json_parse, 1, 0, jsonParseFunc), JFUNCTION(json_test1, 1, 0, jsonTest1Func), From 733da8d31a58930aae98a139a7aa58ec4b08a141 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 27 Sep 2023 16:55:13 +0000 Subject: [PATCH 012/347] Performance tests for JSONB added. FossilOrigin-Name: 7c1be8e361db87458ac9d8fcee080c2b558936539c852bb80f0f7941d61bf15d --- manifest | 13 +++++++------ manifest.uuid | 2 +- test/json/json-q1-b.txt | 25 +++++++++++++++++++++++++ test/json/json-speed-check.sh | 18 ++++++++++++++---- 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 test/json/json-q1-b.txt diff --git a/manifest b/manifest index fd4fd00030..b1cbae3a58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\spartial-index\sconstant\svalue\sfixes\sfrom\strunk\sinto\sthe\sjsonb\sbranch. -D 2023-09-26T19:46:38.407 +C Performance\stests\sfor\sJSONB\sadded. +D 2023-09-27T16:55:13.845 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1300,8 +1300,9 @@ F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd7 F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35e30ee2 F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f +F test/json/json-q1-b.txt d1394d4ade1c9617539b19b48e0dd2df4f6ea918860978722e7a97d60618ca83 F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 -F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x +F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x F test/json101.test dc9d5a2a5b1fd1b54dbd71c538b17933cc98d84b4c1f821ead754933663dca55 F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe @@ -2122,8 +2123,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 e6045b4e1bf3a8e33926fc12b3c039f5e1002eaecbe277ffa82b0ec271a29d17 f459d0806cf044fd07743e4c91d0a5a6ddf45b3b41004bde4278f190d99a4cf5 -R 1d55abb00dbf638a471cf92ac6ee2784 +P 700bdbd7383f66a0da675c197204da4e7b6ed757155145ee98d572de32a5d0ae +R cd229cd0127bc955a2b70dc896603e55 U drh -Z 2f2ccec484af750dd462546f4baf8eb1 +Z 76a0e61dc557712a0090ea7a119c4cd2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c0d71c6abf..ccac07358c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -700bdbd7383f66a0da675c197204da4e7b6ed757155145ee98d572de32a5d0ae \ No newline at end of file +7c1be8e361db87458ac9d8fcee080c2b558936539c852bb80f0f7941d61bf15d \ No newline at end of file diff --git a/test/json/json-q1-b.txt b/test/json/json-q1-b.txt new file mode 100644 index 0000000000..c86a2db44f --- /dev/null +++ b/test/json/json-q1-b.txt @@ -0,0 +1,25 @@ +.mode qbox +.timer on +.param set $label 'q87' +SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL; + +CREATE TEMP TABLE t2(x JSON TEXT); +WITH RECURSIVE + c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<25000), + array1(y) AS ( + SELECT json_group_array( + json_object('x',x,'y',random(),'z',hex(randomblob(50))) + ) + FROM c + ), + c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<5) +INSERT INTO t2(x) + SELECT jsonb(json_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6)) + FROM array1, c2; +CREATE INDEX t2x1 ON t2(x->>'a'); +CREATE INDEX t2x2 ON t2(x->>'b'); +CREATE INDEX t2x3 ON t2(x->>'e'); +CREATE INDEX t2x4 ON t2(x->>'f'); +UPDATE t2 SET x=jsonb_replace(x,'$.f',(x->>'f')+1); +UPDATE t2 SET x=jsonb_set(x,'$.e',(x->>'f')-1); +UPDATE t2 SET x=jsonb_remove(x,'$.d'); diff --git a/test/json/json-speed-check.sh b/test/json/json-speed-check.sh index f7e7f1be58..c57bfecd7a 100755 --- a/test/json/json-speed-check.sh +++ b/test/json/json-speed-check.sh @@ -40,6 +40,7 @@ doCachegrind=1 doVdbeProfile=0 doWal=1 doDiff=1 +doJsonB=0 while test "$1" != ""; do case $1 in --nodiff) @@ -54,6 +55,9 @@ while test "$1" != ""; do --gcc7) CC=gcc-7 ;; + --jsonb) + doJsonB=1 + ;; -*) CC_OPTS="$CC_OPTS $1" ;; @@ -69,12 +73,18 @@ rm -f cachegrind.out.* jsonshell $CC -g -Os -Wall -I. $CC_OPTS ./shell.c ./sqlite3.c -o jsonshell -ldl -lpthread ls -l jsonshell | tee -a summary-$NAME.txt home=`echo $0 | sed -e 's,/[^/]*$,,'` -echo ./jsonshell json100mb.db "<$home/json-q1.txt" -valgrind --tool=cachegrind ./jsonshell json100mb.db <$home/json-q1.txt \ - 2>&1 | tee -a summary-$NAME.txt +if test $doJsonB -eq 1; then + echo ./jsonshell json100mb_b.db "<$home/json-q1-b.txt" + valgrind --tool=cachegrind ./jsonshell json100mb_b.db <$home/json-q1-b.txt \ + 2>&1 | tee -a summary-$NAME.txt +else + echo ./jsonshell json100mb.db "<$home/json-q1.txt" + valgrind --tool=cachegrind ./jsonshell json100mb.db <$home/json-q1.txt \ + 2>&1 | tee -a summary-$NAME.txt +fi cg_anno.tcl cachegrind.out.* >jout-$NAME.txt echo '*****************************************************' >>jout-$NAME.txt sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>jout-$NAME.txt -if test "$NAME" != "$BASELINE"; then +if test "$NAME" != "$BASELINE" -a $doDiff -ne 0; then fossil xdiff --tk -c 20 jout-$BASELINE.txt jout-$NAME.txt fi From 1854837b5ad3d13058e5b027f0fa003bfbf19342 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 28 Sep 2023 10:20:56 +0000 Subject: [PATCH 013/347] Work toward getting json_extract() to operate directly on the BLOB, omitting the translation into a JsonNode array. FossilOrigin-Name: c1feba70f55a8e5f4696d48e4706855415d173ac8ac3c2656787c242a883b4f5 --- manifest | 17 +- manifest.uuid | 2 +- src/json.c | 333 ++++++++++++++++++++++++++++++++++++++++ test/json/json-q1-b.txt | 2 +- 4 files changed, 345 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index b1cbae3a58..0ab3584199 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\stests\sfor\sJSONB\sadded. -D 2023-09-27T16:55:13.845 +C Work\stoward\sgetting\sjson_extract()\sto\soperate\sdirectly\son\sthe\sBLOB,\somitting\nthe\stranslation\sinto\sa\sJsonNode\sarray. +D 2023-09-28T10:20:56.267 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6fb31345d252dc6992c707ec7e826ec9f2283b026092e5105f9905664a2210c5 +F src/json.c 1f174d19f143e4968ae391de3d504563f320c6dbc2ba437231b40f19631a6dc4 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1300,7 +1300,7 @@ F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd7 F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35e30ee2 F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f -F test/json/json-q1-b.txt d1394d4ade1c9617539b19b48e0dd2df4f6ea918860978722e7a97d60618ca83 +F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x F test/json101.test dc9d5a2a5b1fd1b54dbd71c538b17933cc98d84b4c1f821ead754933663dca55 @@ -2123,8 +2123,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 700bdbd7383f66a0da675c197204da4e7b6ed757155145ee98d572de32a5d0ae -R cd229cd0127bc955a2b70dc896603e55 +P 7c1be8e361db87458ac9d8fcee080c2b558936539c852bb80f0f7941d61bf15d +R b9d3bc6ee4123d23cc875353cfd5e9aa +T *branch * jsonb-direct-extract +T *sym-jsonb-direct-extract * +T -sym-jsonb * U drh -Z 76a0e61dc557712a0090ea7a119c4cd2 +Z 3a4258d315e3af636c1707e0dba68e38 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ccac07358c..066de478dc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7c1be8e361db87458ac9d8fcee080c2b558936539c852bb80f0f7941d61bf15d \ No newline at end of file +c1feba70f55a8e5f4696d48e4706855415d173ac8ac3c2656787c242a883b4f5 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0b5068a9f0..a6b91e76eb 100644 --- a/src/json.c +++ b/src/json.c @@ -3493,6 +3493,335 @@ static void jsonRenderNodeAsBlob( } } +/* +** Error returns from jsonLookupBlobStep() +*/ +#define JSON_BLOB_ERROR 0xffffffff +#define JSON_BLOB_NOTFOUND 0xfffffffe +#define JSON_BLOB_PATHERROR 0xfffffffd + +/* +** Search along zPath to find the Json element specified. Return an +** index into pParse->aBlob[] for the start of that element's value. +** +** Return JSON_BLOB_NOTFOUND if no such element exists. +*/ +static u32 jsonLookupBlobStep( + JsonParse *pParse, /* The JSON to search */ + u32 iRoot, /* Begin the search at this element of aBlob[] */ + const char *zPath, /* The path to search */ + const char **pzErr /* Make *pzErr point to any syntax error in zPath */ +){ + u32 i, j, k, nKey, sz, n, iEnd; + const char *zKey; + u8 x; + + if( pParse->oom ) return 0; + if( zPath[0]==0 ) return iRoot; + if( zPath[0]=='.' ){ + x = pParse->aBlob[iRoot]; + if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND; + zPath++; + if( zPath[0]=='"' ){ + zKey = zPath + 1; + for(i=1; zPath[i] && zPath[i]!='"'; i++){} + nKey = i-1; + if( zPath[i] ){ + i++; + }else{ + *pzErr = zPath; + return 0; + } + testcase( nKey==0 ); + }else{ + zKey = zPath; + for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} + nKey = i; + if( nKey==0 ){ + *pzErr = zPath; + return 0; + } + } + n = jsonbPayloadSize(pParse, iRoot, &sz); + j = iRoot + n; + iEnd = j+sz; + while( jaBlob[j] & 0x0f; + if( xJSONB_TEXTRAW ) return JSON_BLOB_ERROR; + n = jsonbPayloadSize(pParse, j, &sz); + k = j+n; + if( k+sz>=iEnd ) return JSON_BLOB_ERROR; + if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ + j = k+sz; + if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; + n = jsonbPayloadSize(pParse, j, &sz); + if( j+n+sz>iEnd ) return JSON_BLOB_ERROR; + return jsonLookupBlobStep(pParse, j, &zPath[i], pzErr); + } + j = k+sz; + if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; + n = jsonbPayloadSize(pParse, j, &sz); + j += n+sz; + } + if( j>iEnd ) return JSON_BLOB_ERROR; + }else if( zPath[0]=='[' ){ + k = 0; + i = 1; + while( sqlite3Isdigit(zPath[i]) ){ + k = k*10 + zPath[i] - '0'; + i++; + } + if( i<2 || zPath[i]!=']' ){ +#if 0 + if( zPath[1]=='#' ){ + JsonNode *pBase = pRoot; + int iBase = iRoot; + if( pRoot->eType!=JSON_ARRAY ) return 0; + for(;;){ + while( j<=pBase->n ){ + if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; + j += jsonNodeSize(&pBase[j]); + } + if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; + if( pParse->useMod==0 ) break; + assert( pBase->eU==2 ); + iBase = pBase->u.iAppend; + pBase = &pParse->aNode[iBase]; + j = 1; + } + j = 2; + if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ + unsigned int x = 0; + j = 3; + do{ + x = x*10 + zPath[j] - '0'; + j++; + }while( sqlite3Isdigit(zPath[j]) ); + if( x>i ) return 0; + i -= x; + } + if( zPath[j]!=']' ){ + *pzErr = zPath; + return 0; + } + }else{ + *pzErr = zPath; + return 0; + } +#endif + *pzErr = zPath; + return JSON_BLOB_PATHERROR; + } + x = pParse->aBlob[iRoot] & 0x0f; + if( x!=JSONB_ARRAY ) return JSON_BLOB_NOTFOUND; + n = jsonbPayloadSize(pParse, iRoot, &sz); + j = iRoot+n; + iEnd = j+sz; + while( jiEnd ) return JSON_BLOB_ERROR; + }else{ + *pzErr = zPath; + return JSON_BLOB_PATHERROR; + } + return JSON_BLOB_NOTFOUND; +} + +/* +** +*/ +static void jsonReturnFromBlob( + JsonParse *pParse, /* Complete JSON parse tree */ + u32 i, /* Index of the node */ + sqlite3_context *pCtx /* Return value for this function */ +){ + u32 n, sz; + int rc; + sqlite3 *db = sqlite3_context_db_handle(pCtx); + + n = jsonbPayloadSize(pParse, i, &sz); + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_NULL: { + sqlite3_result_null(pCtx); + break; + } + case JSONB_TRUE: { + sqlite3_result_int(pCtx, 1); + break; + } + case JSONB_FALSE: { + sqlite3_result_int(pCtx, 0); + break; + } + case JSONB_INT5: + case JSONB_INT: { + sqlite3_int64 iRes = 0; + char *z; + int bNeg = 0; + char x = (char)pParse->aBlob[i+n]; + if( x=='-' && ALWAYS(sz>0) ){ n++; sz--; bNeg = 1; } + else if( x=='+' && ALWAYS(sz>0) ){ n++; sz--; } + z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); + if( z==0 ) return; + rc = sqlite3DecOrHexToI64(z, &iRes); + sqlite3DbFree(db, z); + if( rc<=1 ){ + sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes); + }else if( rc==3 && bNeg ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + }else{ + goto to_double; + } + break; + } + case JSONB_FLOAT5: + case JSONB_FLOAT: { + double r; + char *z; + to_double: + z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); + if( z==0 ) return; + sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); + sqlite3DbFree(db, z); + sqlite3_result_double(pCtx, r); + break; + } + case JSONB_TEXTRAW: + case JSONB_TEXT: { + sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz, + SQLITE_TRANSIENT); + break; + } + case JSONB_TEXT5: + case JSONB_TEXTJ: { + /* Translate JSON formatted string into raw text */ + u32 iIn, iOut; + const char *z; + char *zOut; + u32 nOut = sz; + z = (const char*)&pParse->aBlob[i+n]; + zOut = sqlite3_malloc( nOut+1 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + break; + } + for(iIn=iOut=0; iIn>6)); + zOut[iOut++] = 0x80 | (v&0x3f); + }else{ + u32 vlo; + if( (v&0xfc00)==0xd800 + && i>18); + zOut[iOut++] = 0x80 | ((v>>12)&0x3f); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); + }else{ + zOut[iOut++] = 0xe0 | (v>>12); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); + } + } + continue; + }else if( c=='b' ){ + c = '\b'; + }else if( c=='f' ){ + c = '\f'; + }else if( c=='n' ){ + c = '\n'; + }else if( c=='r' ){ + c = '\r'; + }else if( c=='t' ){ + c = '\t'; + }else if( c=='v' ){ + c = '\v'; + }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ + /* pass through unchanged */ + }else if( c=='0' ){ + c = 0; + }else if( c=='x' ){ + c = (jsonHexToInt(z[iIn+1])<<4) | jsonHexToInt(z[iIn+2]); + iIn += 2; + }else if( c=='\r' && z[i+1]=='\n' ){ + iIn++; + continue; + }else if( 0xe2==(u8)c ){ + assert( 0x80==(u8)z[i+1] ); + assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); + iIn += 2; + continue; + }else{ + continue; + } + } /* end if( c=='\\' ) */ + zOut[iOut++] = c; + } /* end for() */ + zOut[iOut] = 0; + sqlite3_result_text(pCtx, zOut, iOut, sqlite3_free); + break; + } + case JSONB_ARRAY: + case JSONB_OBJECT: { + sqlite3_result_blob(pCtx, &pParse->aBlob[i+n], sz, SQLITE_TRANSIENT); + break; + } + } +} + +/* Do a JSON_EXTRACT(JSON, PATH) on a when JSON is a BLOB. +*/ +static void jsonExtractFromBlob( + sqlite3_context *ctx, + sqlite3_value *pJson, + sqlite3_value *pPath, + int flags +){ + const char *zPath = (const char*)sqlite3_value_text(pPath); + const char *zErr = 0; + u32 i; + JsonParse px; + if( zPath==0 ) return; + if( zPath[0]=='$' ) zPath++; + memset(&px, 0, sizeof(px)); + px.nBlob = sqlite3_value_bytes(pJson); + px.aBlob = (u8*)sqlite3_value_blob(pJson); + if( px.aBlob==0 ) return; + i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + if( i>$label FROM data1 WHERE x->>$label IS NOT NULL; CREATE TEMP TABLE t2(x JSON TEXT); From 2dc60ec57fa75fa659c5c5c66d702eeaaa3347fa Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 28 Sep 2023 15:56:35 +0000 Subject: [PATCH 014/347] Improvements to json_extract() to better support JSONB. Still not 100% working. FossilOrigin-Name: 8c82576176539c4d132b14d46adbf31366c4bcaa59a61dd639dc9cc308fe8825 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 49 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index 0ab3584199..d05d5a4eac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\stoward\sgetting\sjson_extract()\sto\soperate\sdirectly\son\sthe\sBLOB,\somitting\nthe\stranslation\sinto\sa\sJsonNode\sarray. -D 2023-09-28T10:20:56.267 +C Improvements\sto\sjson_extract()\sto\sbetter\ssupport\sJSONB.\sStill\snot\s100%\sworking. +D 2023-09-28T15:56:35.608 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 1f174d19f143e4968ae391de3d504563f320c6dbc2ba437231b40f19631a6dc4 +F src/json.c c9d70a47aabc97b51cd21a33be7602db4760a980920b44b497dbb331a91c12d8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,11 +2123,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 7c1be8e361db87458ac9d8fcee080c2b558936539c852bb80f0f7941d61bf15d -R b9d3bc6ee4123d23cc875353cfd5e9aa -T *branch * jsonb-direct-extract -T *sym-jsonb-direct-extract * -T -sym-jsonb * +P c1feba70f55a8e5f4696d48e4706855415d173ac8ac3c2656787c242a883b4f5 +R df67f115b3402f37b4410f24659e50ec U drh -Z 3a4258d315e3af636c1707e0dba68e38 +Z a8b3beeec30b8fa8de8cb3c4ca40d323 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 066de478dc..a7573f00b7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c1feba70f55a8e5f4696d48e4706855415d173ac8ac3c2656787c242a883b4f5 \ No newline at end of file +8c82576176539c4d132b14d46adbf31366c4bcaa59a61dd639dc9cc308fe8825 \ No newline at end of file diff --git a/src/json.c b/src/json.c index a6b91e76eb..33c9404105 100644 --- a/src/json.c +++ b/src/json.c @@ -3634,7 +3634,34 @@ static u32 jsonLookupBlobStep( } /* -** +** Convert a JSON BLOB into text and make that text the return value +** of an SQL function. +*/ +static void jsonReturnTextJsonFromBlob( + sqlite3_context *ctx, + const u8 *aBlob, + u32 nBlob +){ + JsonParse x; + JsonString s; + + if( aBlob==0 ) return; + memset(&x, 0, sizeof(x)); + x.aBlob = (u8*)aBlob; + x.nBlob = nBlob; + jsonInit(&s, ctx); + jsonRenderBlob(&x, 0, &s); + jsonResult(&s); +} + + +/* +** Return the value of the BLOB node at index i. +** +** If the value is a primitive, return it as an SQL value. +** If the value is an array or object, return it as either +** JSON text or the BLOB encoding, depending on the JSON_B flag +** on the userdata. */ static void jsonReturnFromBlob( JsonParse *pParse, /* Complete JSON parse tree */ @@ -3785,7 +3812,12 @@ static void jsonReturnFromBlob( } case JSONB_ARRAY: case JSONB_OBJECT: { - sqlite3_result_blob(pCtx, &pParse->aBlob[i+n], sz, SQLITE_TRANSIENT); + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); + if( flags & JSON_BLOB ){ + sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); + }else{ + jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); + } break; } } @@ -3992,23 +4024,13 @@ static void jsonbTest2( int argc, sqlite3_value **argv ){ - JsonParse *pParse; const u8 *aBlob; int nBlob; - JsonParse x; - JsonString s; UNUSED_PARAMETER(argc); aBlob = (const u8*)sqlite3_value_blob(argv[0]); - if( aBlob==0 ) return; nBlob = sqlite3_value_bytes(argv[0]); - pParse = &x; - memset(&x, 0, sizeof(x)); - x.aBlob = (u8*)aBlob; - x.nBlob = nBlob; - jsonInit(&s, ctx); - jsonRenderBlob(pParse, 0, &s); - jsonResult(&s); + jsonReturnTextJsonFromBlob(ctx, aBlob, nBlob); } /* @@ -5401,6 +5423,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), JFUNCTION(json_error_position,1, 0, jsonErrorFunc), JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(jsonb_extract, -1, JSON_BLOB, jsonExtractFunc), JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), JFUNCTION(json_insert, -1, 0, jsonSetFunc), From 59862e6d224f7bdbd368cc2827ea06a3a2c82d29 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 28 Sep 2023 17:07:43 +0000 Subject: [PATCH 015/347] Miscellaneous bugs fixed. FossilOrigin-Name: 5c0815fa2e422d81198a43a2c04a022e319fcbcadfd4be4437f2e663892ca26b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 22 ++++++++++++++++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index d05d5a4eac..c1019d925c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sjson_extract()\sto\sbetter\ssupport\sJSONB.\sStill\snot\s100%\sworking. -D 2023-09-28T15:56:35.608 +C Miscellaneous\sbugs\sfixed. +D 2023-09-28T17:07:43.443 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c c9d70a47aabc97b51cd21a33be7602db4760a980920b44b497dbb331a91c12d8 +F src/json.c 946e0bbc5070db333a370e2f7b806ad07d2e9fb93d057cf52a9e5fb58fd43768 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 c1feba70f55a8e5f4696d48e4706855415d173ac8ac3c2656787c242a883b4f5 -R df67f115b3402f37b4410f24659e50ec +P 8c82576176539c4d132b14d46adbf31366c4bcaa59a61dd639dc9cc308fe8825 +R e6233e4f5d16d81e42c25a8ff039c998 U drh -Z a8b3beeec30b8fa8de8cb3c4ca40d323 +Z beeeb6a9ecf43431f000f927a20c9a15 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a7573f00b7..5bf7e31c95 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8c82576176539c4d132b14d46adbf31366c4bcaa59a61dd639dc9cc308fe8825 \ No newline at end of file +5c0815fa2e422d81198a43a2c04a022e319fcbcadfd4be4437f2e663892ca26b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 33c9404105..322fbc5602 100644 --- a/src/json.c +++ b/src/json.c @@ -518,6 +518,14 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ ** features. */ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ + char *zBuf = sqlite3_malloc64( N+1 ); + if( zBuf==0 ){ + p->bErr = 1; + return; + } + memcpy(zBuf, zIn, N); + zBuf[N] = 0; + zIn = zBuf; if( zIn[0]=='+' ){ zIn++; N--; @@ -539,6 +547,7 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ } assert( N>0 ); jsonAppendRawNZ(p, zIn, N); + sqlite3_free(zBuf); } /* @@ -958,12 +967,16 @@ static void jsonReturn( int rc; int bNeg = 0; const char *z; + char *zz; + sqlite3 *db = sqlite3_context_db_handle(pCtx); assert( pNode->eU==1 ); - z = pNode->u.zJContent; + zz = sqlite3DbStrNDup(db, pNode->u.zJContent, pNode->n); + z = zz; if( z[0]=='-' ){ z++; bNeg = 1; } else if( z[0]=='+' ){ z++; } rc = sqlite3DecOrHexToI64(z, &i); + sqlite3DbFree(db, zz); if( rc<=1 ){ sqlite3_result_int64(pCtx, bNeg ? -i : i); }else if( rc==3 && bNeg ){ @@ -1862,7 +1875,7 @@ static int jsonParse( i = jsonParseValue(pParse, 0); } if( pParse->oom ) i = -1; - if( i>0 ){ + if( !pParse->isBinary && i>0 ){ assert( pParse->iDepth==0 ); while( fast_isspace(zJson[i]) ) i++; if( zJson[i] ){ @@ -3359,6 +3372,7 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ break; } } + pParse->aBlob[i] = 0; return i+x+sz; } @@ -3450,7 +3464,7 @@ static void jsonRenderNodeAsBlob( case JSON_ARRAY: { u32 j = 1; u32 iStart, iThis = pOut->nBlob; - jsonBlobAppendNodeType(pOut, JSONB_ARRAY, 0x7fffffff); + jsonBlobAppendNodeType(pOut, JSONB_ARRAY, pParse->nJson*2); iStart = pOut->nBlob; for(;;){ while( j<=pNode->n ){ @@ -3471,7 +3485,7 @@ static void jsonRenderNodeAsBlob( case JSON_OBJECT: { u32 j = 1; u32 iStart, iThis = pOut->nBlob; - jsonBlobAppendNodeType(pOut, JSONB_OBJECT, 0x7fffffff); + jsonBlobAppendNodeType(pOut, JSONB_OBJECT, pParse->nJson*2); iStart = pOut->nBlob; for(;;){ while( j<=pNode->n ){ From 6b1db922286ac88505854e93ce017e11e4b18cb5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 28 Sep 2023 17:23:46 +0000 Subject: [PATCH 016/347] Extract directly from BLOB is now complete and appears to work. FossilOrigin-Name: 3de58ec99444b16dfcda1e226420e2343450b77abd3faf33a88b6d18339ef17c --- manifest | 12 +++++----- manifest.uuid | 2 +- src/json.c | 61 ++++++++++++++++++++++++--------------------------- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/manifest b/manifest index c1019d925c..f6be12b744 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Miscellaneous\sbugs\sfixed. -D 2023-09-28T17:07:43.443 +C Extract\sdirectly\sfrom\sBLOB\sis\snow\scomplete\sand\sappears\sto\swork. +D 2023-09-28T17:23:46.244 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 946e0bbc5070db333a370e2f7b806ad07d2e9fb93d057cf52a9e5fb58fd43768 +F src/json.c 7e68346bfc4f030e454c68751ab7082af52b9925ff28bff88eef03258e486d7c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 8c82576176539c4d132b14d46adbf31366c4bcaa59a61dd639dc9cc308fe8825 -R e6233e4f5d16d81e42c25a8ff039c998 +P 5c0815fa2e422d81198a43a2c04a022e319fcbcadfd4be4437f2e663892ca26b +R 0d79abb184ac0c9db2e32b0ee612448c U drh -Z beeeb6a9ecf43431f000f927a20c9a15 +Z dfb6861bdfa5add7990cbc313b482bb0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5bf7e31c95..6de749eb76 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5c0815fa2e422d81198a43a2c04a022e319fcbcadfd4be4437f2e663892ca26b \ No newline at end of file +3de58ec99444b16dfcda1e226420e2343450b77abd3faf33a88b6d18339ef17c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 322fbc5602..e43240c71c 100644 --- a/src/json.c +++ b/src/json.c @@ -3507,6 +3507,21 @@ static void jsonRenderNodeAsBlob( } } +/* +** Given that a JSONB_ARRAY object starts at offset i, return +** the number of entries in that array. +*/ +static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ + u32 n, sz, i, iEnd; + u32 k = 0; + n = jsonbPayloadSize(pParse, iRoot, &sz); + iEnd = iRoot+n+sz; + for(i=iRoot+n; iiEnd ) return JSON_BLOB_ERROR; }else if( zPath[0]=='[' ){ + x = pParse->aBlob[iRoot] & 0x0f; + if( x!=JSONB_ARRAY ) return JSON_BLOB_NOTFOUND; + n = jsonbPayloadSize(pParse, iRoot, &sz); k = 0; i = 1; while( sqlite3Isdigit(zPath[i]) ){ @@ -3586,49 +3604,28 @@ static u32 jsonLookupBlobStep( i++; } if( i<2 || zPath[i]!=']' ){ -#if 0 if( zPath[1]=='#' ){ - JsonNode *pBase = pRoot; - int iBase = iRoot; - if( pRoot->eType!=JSON_ARRAY ) return 0; - for(;;){ - while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; - j += jsonNodeSize(&pBase[j]); - } - if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pBase->eU==2 ); - iBase = pBase->u.iAppend; - pBase = &pParse->aNode[iBase]; - j = 1; - } - j = 2; + k = jsonbArrayCount(pParse, iRoot); + i = 2; if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ unsigned int x = 0; - j = 3; + i = 3; do{ - x = x*10 + zPath[j] - '0'; - j++; - }while( sqlite3Isdigit(zPath[j]) ); - if( x>i ) return 0; - i -= x; + x = x*10 + zPath[i] - '0'; + i++; + }while( sqlite3Isdigit(zPath[i]) ); + if( x>k ) return 0; + k -= x; } - if( zPath[j]!=']' ){ + if( zPath[i]!=']' ){ *pzErr = zPath; - return 0; + return JSON_BLOB_PATHERROR; } }else{ *pzErr = zPath; - return 0; + return JSON_BLOB_PATHERROR; } -#endif - *pzErr = zPath; - return JSON_BLOB_PATHERROR; } - x = pParse->aBlob[iRoot] & 0x0f; - if( x!=JSONB_ARRAY ) return JSON_BLOB_NOTFOUND; - n = jsonbPayloadSize(pParse, iRoot, &sz); j = iRoot+n; iEnd = j+sz; while( j Date: Thu, 28 Sep 2023 18:23:52 +0000 Subject: [PATCH 017/347] Allow the sqlite3_user_data() function to be invoked with a NULL argument or with an sqlite3_context pointer from a virtual table. It returns NULL in both cases. FossilOrigin-Name: 2f49687371ada65fef374336c28b352c48ab98dc31282ac82397035efe04ba11 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbeapi.c | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 9246cdfdde..6ac2aacfe0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\schanges\sinto\sthe\sjsonb\sbranch\sfor\sthe\scompiler\swarning\sfixes\nfrom\stwo\sdays\sago. -D 2023-09-28T17:41:45.378 +C Allow\sthe\ssqlite3_user_data()\sfunction\sto\sbe\sinvoked\swith\sa\sNULL\sargument\nor\swith\san\ssqlite3_context\spointer\sfrom\sa\svirtual\stable.\s\sIt\sreturns\sNULL\nin\sboth\scases. +D 2023-09-28T18:23:52.254 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -783,7 +783,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c cd112eb00d20fc5cc44f631d0e713838602637328b0f127c2f3c2aa8cea3cc91 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c 4184402246172220418c0ef49ff4cf1a19ced9a4ac6c843c2f0773fb5c543f37 +F src/vdbeapi.c 7fdd1eec6b967a5455ccc45a43b84fd32746d6749a423104333f9ba5cec56c9f F src/vdbeaux.c 5b415e09b5b9d5be6c0f4fcbf18ea9d7d16f6a29ced2f14a3b2041020f63e9c1 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce F src/vdbemem.c 317b9f48708139db6239ade40c7980b4bc8233168383690d588dad6d8437f722 @@ -2123,8 +2123,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 5853065b7ee326c04ddfcde64c178f487af04cd3adc3cc99f559907484ec169d b488b9fb71652eca90d2bf73d32f3d748badf517859dc833c58e021b0e017194 -R 9fc8b28e091a22aa2df6890c9cd30c84 +P cee113cc315b04fd75ccc172cf4529bf15b2050bf274433496c31a282e281ab8 +R 447caeefd53e356f21199c52281cba5f U drh -Z 296260cf4c2437a82708d346d0a00f10 +Z 98132ac7eca8ce40c36975540dff67b4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8230cfa03a..f38a3e887b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cee113cc315b04fd75ccc172cf4529bf15b2050bf274433496c31a282e281ab8 \ No newline at end of file +2f49687371ada65fef374336c28b352c48ab98dc31282ac82397035efe04ba11 \ No newline at end of file diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 1213dbe6d1..6b32506f9f 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -845,8 +845,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){ ** pointer to it. */ void *sqlite3_user_data(sqlite3_context *p){ - assert( p && p->pFunc ); - return p->pFunc->pUserData; + return (p && p->pFunc) ? p->pFunc->pUserData : 0; } /* From 8cdab142863a98b84baf047ce443ee37ed141ab1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 28 Sep 2023 19:11:36 +0000 Subject: [PATCH 018/347] Fix some minor memory issues so that all legacy tests now pass. FossilOrigin-Name: 1744bfc669346ff221f28d45fd978863e876a2d2f0b82bcf0e5ee6f0326900cc --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 10 +++++++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 6ac2aacfe0..5be61e1831 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\ssqlite3_user_data()\sfunction\sto\sbe\sinvoked\swith\sa\sNULL\sargument\nor\swith\san\ssqlite3_context\spointer\sfrom\sa\svirtual\stable.\s\sIt\sreturns\sNULL\nin\sboth\scases. -D 2023-09-28T18:23:52.254 +C Fix\ssome\sminor\smemory\sissues\sso\sthat\sall\slegacy\stests\snow\spass. +D 2023-09-28T19:11:36.692 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7e68346bfc4f030e454c68751ab7082af52b9925ff28bff88eef03258e486d7c +F src/json.c 6ecde7714aed5238881b936edc3f9bf85c2ece02f2b1a41e10f5d5f3e8f89513 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 cee113cc315b04fd75ccc172cf4529bf15b2050bf274433496c31a282e281ab8 -R 447caeefd53e356f21199c52281cba5f +P 2f49687371ada65fef374336c28b352c48ab98dc31282ac82397035efe04ba11 +R 3d149364fb767414041d170ea764743b U drh -Z 98132ac7eca8ce40c36975540dff67b4 +Z 6ffc9f7e51690a482c053773cfbc0f4b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f38a3e887b..fe5f150f03 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2f49687371ada65fef374336c28b352c48ab98dc31282ac82397035efe04ba11 \ No newline at end of file +1744bfc669346ff221f28d45fd978863e876a2d2f0b82bcf0e5ee6f0326900cc \ No newline at end of file diff --git a/src/json.c b/src/json.c index e43240c71c..3d44489e6f 100644 --- a/src/json.c +++ b/src/json.c @@ -543,10 +543,10 @@ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ assert( rc==2 ); jsonAppendRawNZ(p, "9.0e999", 7); } - return; + }else{ + assert( N>0 ); + jsonAppendRawNZ(p, zIn, N); } - assert( N>0 ); - jsonAppendRawNZ(p, zIn, N); sqlite3_free(zBuf); } @@ -972,6 +972,10 @@ static void jsonReturn( assert( pNode->eU==1 ); zz = sqlite3DbStrNDup(db, pNode->u.zJContent, pNode->n); + if( zz==0 ){ + sqlite3_result_error_nomem(pCtx); + return; + } z = zz; if( z[0]=='-' ){ z++; bNeg = 1; } else if( z[0]=='+' ){ z++; } From ecce6022d5c198b541ae74a0174f9949f7df5376 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 29 Sep 2023 11:17:43 +0000 Subject: [PATCH 019/347] Describe the JSONB encoding in a header comment to the json.c source file. FossilOrigin-Name: 1c0cba3461d6111b3aeb77726880221f1240355f0b57e060febbdeb12fb688c0 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 5be61e1831..2b9bd7fd66 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\sminor\smemory\sissues\sso\sthat\sall\slegacy\stests\snow\spass. -D 2023-09-28T19:11:36.692 +C Describe\sthe\sJSONB\sencoding\sin\sa\sheader\scomment\sto\sthe\sjson.c\ssource\sfile. +D 2023-09-29T11:17:43.227 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6ecde7714aed5238881b936edc3f9bf85c2ece02f2b1a41e10f5d5f3e8f89513 +F src/json.c 6d2643118360760d589829d5a10d612a0270729ce11b303553502eae70c8f899 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 2f49687371ada65fef374336c28b352c48ab98dc31282ac82397035efe04ba11 -R 3d149364fb767414041d170ea764743b +P 1744bfc669346ff221f28d45fd978863e876a2d2f0b82bcf0e5ee6f0326900cc +R 88ae9ef60dd50edd212e3ca11b3078d3 U drh -Z 6ffc9f7e51690a482c053773cfbc0f4b +Z 174eaa9aa981071dd243f3ac21aacaae # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fe5f150f03..a42d3e4da1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1744bfc669346ff221f28d45fd978863e876a2d2f0b82bcf0e5ee6f0326900cc \ No newline at end of file +1c0cba3461d6111b3aeb77726880221f1240355f0b57e060febbdeb12fb688c0 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3d44489e6f..393e451663 100644 --- a/src/json.c +++ b/src/json.c @@ -15,11 +15,105 @@ ** This file began as an extension in ext/misc/json1.c in 2015. That ** extension proved so useful that it has now been moved into the core. ** -** For the time being, all JSON is stored as pure text. (We might add -** a JSONB type in the future which stores a binary encoding of JSON in -** a BLOB, but there is no support for JSONB in the current implementation. -** This implementation parses JSON text at 250 MB/s, so it is hard to see -** how JSONB might improve on that.) +** The original design stored all JSON as pure text, canonical RFC-8259. +** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16). +** All generated JSON text still conforms strictly to RFC-8259, but text +** with JSON-5 extensions is accepted as input. +** +** Beginning with version 3.44.0 (pending), these routines also accept +** BLOB values that have JSON encoded using a binary representation we +** call JSONB. The name JSONB comes from PostgreSQL, however the on-disk +** format SQLite JSONB is completely different and incompatible with +** PostgreSQL JSONB. +** +** Decoding and interpreting JSONB is still O(N) where N is the size of +** the input, the same as text JSON. However, the constant of proportionality +** for JSONB is much smaller due to faster parsing. The size of each +** element in JSONB is encoded in its header, so there is no need to search +** for delimiters using persnickety syntax rules. JSONB seems to be about +** 3x faster than text JSON as a result. JSONB is also tends to be slightly +** smaller than text JSON, by 5% or 10%, but there are corner cases where +** JSONB can be slightly larger. Roughtly speaking, though, a JSONB blob +** and the equivalent RFC-8259 text string take up the same amount of space +** on disk. +** +** +** THE JSONB ENCODING: +** +** Every JSON element is encoded in JSONB as a header and a payload. +** The header is between 1 and 9 bytes in size. The payload is zero +** or more bytes. +** +** The lower 4 bits of the first byte of the header determines the +** element type: +** +** 0: NULL +** 1: TRUE +** 2: FALSE +** 3: INT -- RFC-8259 integer literal +** 4: INT5 -- JSON5 integer literal +** 5: FLOAT -- RFC-8259 floating point literal +** 6: FLOAT5 -- JSON5 floating point literal +** 7: TEXT -- Text literal acceptable to both SQL and JSON +** 8: TEXTJ -- Text literal with RFC-8259 escape codes +** 9: TEXT5 -- Text literal with JSON5 and RFC-8259 escapes +** 10: TEXTRAW -- Text literal with unescaped ', ", or \ characters +** 11: ARRAY +** 12: OBJECT +** +** The other three possible values (13-15) are reserved for future +** enhancements. +** +** The upper 4 bits of the first byte determine the size of the header +** and sometimes also the size of the payload. If X is the first byte +** of the element and if X>>4 is between 0 and 11, then the payload +** will be that many bytes in size and the header is exactly one byte +** in size. Other four values for X>>4 (12-15) indicate that the header +** is more than one byte in size and that the payload size is determined +** by the remainder of the header, interpreted as a unsigned big-endian +** integer. +** +** Value of X>>4 Size integer Total header size +** ------------- -------------------- ----------------- +** 12 1 byte (0-255) 2 +** 13 2 byte (0-65535) 3 +** 14 4 byte (0-4294967295) 5 +** 15 8 byte (0-1.8e19) 9 +** +** The payload size need not be expressed in its minimal form. For example, +** if the payload size is 10, the size can be expressed in any of 5 different +** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte, +** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by +** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and +** a single byte of 0x0a. The shorter forms are preferred, of course, but +** sometimes when generating JSONB, the payload size is not known in advance +** and it is convenient to reserve sufficient header space to cover the +** largest possible payload size and then come back later and patch up +** the size when it becomes known, resulting in a non-minimal encoding. +** +** The value (X>>4)==15 is not actually used in the current implementation +** (as SQLite is currently unable handle BLOBs larger than about 2GB) +** but is included in the design to allow for future enhancements. +** +** The payload follows the header. NULL, TRUE, and FALSE have no payload and +** their payload size must always be zero. The payload for INT, INT5, +** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the +** "..." or '...' delimiters are omitted from the various text encodings. +** The payload for ARRAY and OBJECT is a list of additional elements that +** are the content for the array or object. The payload for an OBJECT +** must be an even number of elements. The first element of each pair is +** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW. +** +** A valid JSONB blob consists of a single element, as described above. +** Usually this will be an ARRAY or OBJECT element which has many more +** elements as its content. But the overall blob is just a single element. +** +** Input validation for JSONB blobs simply checks that the element type +** code is between 0 and 12 and that the total size of the element +** (header plus payload) is the same as the size of the BLOB. If those +** checks are true, the BLOB is assumed to be JSONB and processing continues. +** Errors are only raised if some other miscoding is discovered during +** processing. */ #ifndef SQLITE_OMIT_JSON #include "sqliteInt.h" From ae5f55e2271d0fd6788a76dc00f61947dcca68d7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 29 Sep 2023 12:45:14 +0000 Subject: [PATCH 020/347] Improvements to comments and procedure names for clarity in the JSON implementation. FossilOrigin-Name: 9b620d813ef483f1277c1683c5e926a882f07f3b90804dea0c91b325ff8e45a4 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 285 +++++++++++++++++++++++++++----------------------- 3 files changed, 161 insertions(+), 138 deletions(-) diff --git a/manifest b/manifest index 2b9bd7fd66..7fcd2bd9c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Describe\sthe\sJSONB\sencoding\sin\sa\sheader\scomment\sto\sthe\sjson.c\ssource\sfile. -D 2023-09-29T11:17:43.227 +C Improvements\sto\scomments\sand\sprocedure\snames\sfor\sclarity\sin\sthe\sJSON\nimplementation. +D 2023-09-29T12:45:14.794 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6d2643118360760d589829d5a10d612a0270729ce11b303553502eae70c8f899 +F src/json.c 92d7c2ea8db842cd6a7a7b664bf43d5ae75e097981f001a5ab3860bd222132ca F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 1744bfc669346ff221f28d45fd978863e876a2d2f0b82bcf0e5ee6f0326900cc -R 88ae9ef60dd50edd212e3ca11b3078d3 +P 1c0cba3461d6111b3aeb77726880221f1240355f0b57e060febbdeb12fb688c0 +R d73ce77d34a070de20c2504f2815e166 U drh -Z 174eaa9aa981071dd243f3ac21aacaae +Z 100adbc30a124e4f2d5cc09bea6ade0f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a42d3e4da1..7f67a4ccc1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1c0cba3461d6111b3aeb77726880221f1240355f0b57e060febbdeb12fb688c0 \ No newline at end of file +9b620d813ef483f1277c1683c5e926a882f07f3b90804dea0c91b325ff8e45a4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 393e451663..97a6aebf4e 100644 --- a/src/json.c +++ b/src/json.c @@ -33,9 +33,8 @@ ** for delimiters using persnickety syntax rules. JSONB seems to be about ** 3x faster than text JSON as a result. JSONB is also tends to be slightly ** smaller than text JSON, by 5% or 10%, but there are corner cases where -** JSONB can be slightly larger. Roughtly speaking, though, a JSONB blob -** and the equivalent RFC-8259 text string take up the same amount of space -** on disk. +** JSONB can be slightly larger. So you are not far mistaken to say that +** a JSONB blob is the same size as the equivalent RFC-8259 text. ** ** ** THE JSONB ENCODING: @@ -55,9 +54,9 @@ ** 5: FLOAT -- RFC-8259 floating point literal ** 6: FLOAT5 -- JSON5 floating point literal ** 7: TEXT -- Text literal acceptable to both SQL and JSON -** 8: TEXTJ -- Text literal with RFC-8259 escape codes -** 9: TEXT5 -- Text literal with JSON5 and RFC-8259 escapes -** 10: TEXTRAW -- Text literal with unescaped ', ", or \ characters +** 8: TEXTJ -- Text containing RFC-8259 escapes +** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes +** 10: TEXTRAW -- Text containing unescaped syntax characters ** 11: ARRAY ** 12: OBJECT ** @@ -118,10 +117,26 @@ #ifndef SQLITE_OMIT_JSON #include "sqliteInt.h" +/* JSONB element types +*/ +#define JSONB_NULL 0 /* "null" */ +#define JSONB_TRUE 1 /* "true" */ +#define JSONB_FALSE 2 /* "false" */ +#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ +#define JSONB_INT5 4 /* integer in 0x000 notation */ +#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ +#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ +#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ +#define JSONB_TEXTJ 8 /* Text with JSON escapes */ +#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ +#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ +#define JSONB_ARRAY 11 /* An array */ +#define JSONB_OBJECT 12 /* An object */ + /* ** Growing our own isspace() routine this way is twice as fast as ** the library isspace() function, resulting in a 7% overall performance -** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). +** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). */ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, @@ -168,11 +183,12 @@ static const char jsonIsOk[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - +/* Put code used only for testing inside the JSON_VVA() macro. +*/ #if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) -# define VVA(X) +# define JSON_VVA(X) #else -# define VVA(X) X +# define JSON_VVA(X) X #endif /* Objects */ @@ -204,7 +220,7 @@ struct JsonCleanup { void *pArg; /* Argument to xOp() */ }; -/* JSON type values +/* JSON type values for JsonNode.eType */ #define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */ #define JSON_NULL 1 @@ -216,33 +232,18 @@ struct JsonCleanup { #define JSON_ARRAY 7 #define JSON_OBJECT 8 -/* JSON BLOB node types -*/ -#define JSONB_NULL 0 /* "null" */ -#define JSONB_TRUE 1 /* "true" */ -#define JSONB_FALSE 2 /* "false" */ -#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ -#define JSONB_INT5 4 /* integer in 0x000 notation */ -#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ -#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ -#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ -#define JSONB_TEXTJ 8 /* Text with JSON escapes */ -#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ -#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ -#define JSONB_ARRAY 11 /* An array */ -#define JSONB_OBJECT 12 /* An object */ - -/* The "subtype" set for JSON values */ -#define JSON_SUBTYPE 74 /* Ascii for "J" */ - -/* -** Names of the various JSON types: +/* Human-readalbe names for the JsonNode types: */ static const char * const jsonType[] = { "subst", "null", "true", "false", "integer", "real", "text", "array", "object" }; +/* The "subtype" set for text JSON values passed through using +** sqlite3_result_subtype() and sqlite3_value_subtype(). +*/ +#define JSON_SUBTYPE 74 /* Ascii for "J" */ + /* Bit values for the JsonNode.jnFlag field */ #define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ @@ -350,9 +351,10 @@ struct JsonParse { ** Utility routines for dealing with JsonString objects **************************************************************************/ -/* Set the JsonString object to an empty string +/* Turn uninitialized bulk memory into a valid JsonString object +** holding a zero-length string. */ -static void jsonZero(JsonString *p){ +static void jsonStringZero(JsonString *p){ p->zBuf = p->zSpace; p->nAlloc = sizeof(p->zSpace); p->nUsed = 0; @@ -361,39 +363,39 @@ static void jsonZero(JsonString *p){ /* Initialize the JsonString object */ -static void jsonInit(JsonString *p, sqlite3_context *pCtx){ +static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ p->pCtx = pCtx; p->bErr = 0; - jsonZero(p); + jsonStringZero(p); } /* Free all allocated memory and reset the JsonString object back to its ** initial state. */ -static void jsonReset(JsonString *p){ +static void jsonStringReset(JsonString *p){ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); - jsonZero(p); + jsonStringZero(p); } /* Report an out-of-memory (OOM) condition */ -static void jsonOom(JsonString *p){ +static void jsonStringOom(JsonString *p){ p->bErr = 1; sqlite3_result_error_nomem(p->pCtx); - jsonReset(p); + jsonStringReset(p); } /* Enlarge pJson->zBuf so that it can hold at least N more bytes. ** Return zero on success. Return non-zero on an OOM error */ -static int jsonGrow(JsonString *p, u32 N){ +static int jsonStringGrow(JsonString *p, u32 N){ u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; char *zNew; if( p->bStatic ){ if( p->bErr ) return 1; zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ - jsonOom(p); + jsonStringOom(p); return SQLITE_NOMEM; } memcpy(zNew, p->zBuf, (size_t)p->nUsed); @@ -403,7 +405,7 @@ static int jsonGrow(JsonString *p, u32 N){ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); if( p->zBuf==0 ){ p->bErr = 1; - jsonZero(p); + jsonStringZero(p); return SQLITE_NOMEM; } } @@ -413,20 +415,20 @@ static int jsonGrow(JsonString *p, u32 N){ /* Append N bytes from zIn onto the end of the JsonString string. */ -static SQLITE_NOINLINE void jsonAppendExpand( +static SQLITE_NOINLINE void jsonStringExpandAndAppend( JsonString *p, const char *zIn, u32 N ){ assert( N>0 ); - if( jsonGrow(p,N) ) return; + if( jsonStringGrow(p,N) ) return; memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ if( N==0 ) return; if( N+p->nUsed >= p->nAlloc ){ - jsonAppendExpand(p,zIn,N); + jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; @@ -435,7 +437,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ assert( N>0 ); if( N+p->nUsed >= p->nAlloc ){ - jsonAppendExpand(p,zIn,N); + jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; @@ -447,7 +449,7 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ */ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ va_list ap; - if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return; + if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return; va_start(ap, zFormat); sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap); va_end(ap); @@ -457,7 +459,7 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ /* Append a single character */ static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ - if( jsonGrow(p,1) ) return; + if( jsonStringGrow(p,1) ) return; p->zBuf[p->nUsed++] = c; } static void jsonAppendChar(JsonString *p, char c){ @@ -480,7 +482,7 @@ static int jsonForceRCStr(JsonString *p){ if( p->bStatic==0 ) return 1; p->nAlloc = 0; p->nUsed++; - jsonGrow(p, p->nUsed); + jsonStringGrow(p, p->nUsed); p->nUsed--; return p->bStatic==0; } @@ -504,7 +506,8 @@ static void jsonAppendSeparator(JsonString *p){ */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 i; - if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return; + if( zIn==0 ) return; + if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; p->zBuf[p->nUsed++] = '"'; for(i=0; izBuf[p->nUsed++] = c; }else if( c=='"' || c=='\\' ){ json_simple_escape: - if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return; + if( (p->nUsed+N+3-i > p->nAlloc) && jsonStringGrow(p,N+3-i)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ @@ -533,7 +536,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ c = aSpecial[c]; goto json_simple_escape; } - if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return; + if( (p->nUsed+N+7+i > p->nAlloc) && jsonStringGrow(p,N+7-i)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = 'u'; p->zBuf[p->nUsed++] = '0'; @@ -680,10 +683,10 @@ static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ /* -** Append a function parameter value to the JSON string under -** construction. +** Append an sqlite3_value (such as a function parameter) to the JSON +** string under construction in p. */ -static void jsonAppendValue( +static void jsonAppendSqlValue( JsonString *p, /* Append to this JSON string */ sqlite3_value *pValue /* Value to append */ ){ @@ -716,7 +719,7 @@ static void jsonAppendValue( if( p->bErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); p->bErr = 2; - jsonReset(p); + jsonStringReset(p); } break; } @@ -724,11 +727,12 @@ static void jsonAppendValue( } -/* Make the JSON in p the result of the SQL function. +/* Make the text in p (which is probably a generated JSON text string) +** the result of the SQL function. ** -** The JSON string is reset. +** The JsonString is reset. */ -static void jsonResult(JsonString *p){ +static void jsonReturnString(JsonString *p){ if( p->bErr==0 ){ if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, @@ -743,7 +747,7 @@ static void jsonResult(JsonString *p){ if( p->bErr==1 ){ sqlite3_result_error_nomem(p->pCtx); } - jsonReset(p); + jsonStringReset(p); } /************************************************************************** @@ -842,7 +846,7 @@ static int jsonParseAddCleanup( ** append to pOut. Subsubstructure is also included. Return ** the number of JsonNode objects that are encoded. */ -static void jsonRenderNode( +static void jsonRenderNodeAsText( JsonParse *pParse, /* the complete parse of the JSON */ JsonNode *pNode, /* The node to render */ JsonString *pOut /* Write JSON here */ @@ -922,7 +926,7 @@ static void jsonRenderNode( while( j<=pNode->n ){ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); + jsonRenderNodeAsText(pParse, &pNode[j], pOut); } j += jsonNodeSize(&pNode[j]); } @@ -942,9 +946,9 @@ static void jsonRenderNode( while( j<=pNode->n ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); + jsonRenderNodeAsText(pParse, &pNode[j], pOut); jsonAppendChar(pOut, ':'); - jsonRenderNode(pParse, &pNode[j+1], pOut); + jsonRenderNodeAsText(pParse, &pNode[j+1], pOut); } j += 1 + jsonNodeSize(&pNode[j+1]); } @@ -968,9 +972,14 @@ static void jsonRenderNodeAsBlob( ); /* -** Return a JsonNode and all its descendants as a JSON string. +** Make the return value of an SQL function be the JSON encoded by pNode. +** +** By default, the node is rendered as RFC-8259 JSON text (canonical +** JSON text without any JSON-5 enhancements). However if the +** JSON_BLOB flag is set in the user-data for the function, then the +** node is rendered into the JSONB format and returned as a BLOB. */ -static void jsonReturnJson( +static void jsonReturnNodeAsJson( JsonParse *pParse, /* The complete JSON */ JsonNode *pNode, /* Node to return */ sqlite3_context *pCtx, /* Return value for this function */ @@ -992,13 +1001,13 @@ static void jsonReturnJson( jsonRenderNodeAsBlob(pParse, pNode, &x); sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); }else{ - jsonInit(&s, pCtx); - jsonRenderNode(pParse, pNode, &s); + jsonStringInit(&s, pCtx); + jsonRenderNodeAsText(pParse, pNode, &s); if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ pParse->zAlt = sqlite3RCStrRef(s.zBuf); pParse->nAlt = s.nUsed; } - jsonResult(&s); + jsonReturnString(&s); sqlite3_result_subtype(pCtx, JSON_SUBTYPE); } } @@ -1035,9 +1044,16 @@ static u32 jsonHexToInt4(const char *z){ } /* -** Make the JsonNode the return value of the function. +** Make the return value from an SQL function be the SQL value of +** JsonNode pNode. +** +** If pNode is an atom (not an array or object) then the value returned +** is a pure SQL value - an SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT, or +** SQLITE_NULL. However, if pNode is a JSON array or object, then the +** value returned is either RFC-8259 JSON text or a BLOB in the JSONB +** format, depending on the JSON_BLOB flag of the function user-data. */ -static void jsonReturn( +static void jsonReturnNodeAsSql( JsonParse *pParse, /* Complete JSON parse tree */ JsonNode *pNode, /* Node to return */ sqlite3_context *pCtx /* Return value for this function */ @@ -1194,7 +1210,7 @@ static void jsonReturn( } case JSON_ARRAY: case JSON_OBJECT: { - jsonReturnJson(pParse, pNode, pCtx, 0); + jsonReturnNodeAsJson(pParse, pNode, pCtx, 0); break; } } @@ -1265,7 +1281,7 @@ static int jsonParseAddNode( assert( p!=0 ); p->eType = (u8)(eType & 0xff); p->jnFlags = (u8)(eType >> 8); - VVA( p->eU = zContent ? 1 : 0 ); + JSON_VVA( p->eU = zContent ? 1 : 0 ); p->n = n; p->u.zJContent = zContent; return pParse->nNode++; @@ -1490,7 +1506,7 @@ static const struct NanInfName { ** -4 ',' seen ** -5 ':' seen */ -static int jsonParseValue(JsonParse *pParse, u32 i){ +static int jsonParseValueFromText(JsonParse *pParse, u32 i){ char c; u32 j; int iThis; @@ -1509,7 +1525,7 @@ json_parse_restart: } for(j=i+1;;j++){ u32 nNode = pParse->nNode; - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x<=0 ){ if( x==(-2) ){ j = pParse->iErr; @@ -1552,7 +1568,7 @@ json_parse_restart: goto parse_object_value; } } - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -1560,7 +1576,7 @@ json_parse_restart: j = pParse->iErr+1; } parse_object_value: - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -1579,7 +1595,7 @@ json_parse_restart: break; } } - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -1606,7 +1622,7 @@ json_parse_restart: } memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; @@ -1630,7 +1646,7 @@ json_parse_restart: break; } } - x = jsonParseValue(pParse, j); + x = jsonParseValueFromText(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -1807,7 +1823,10 @@ json_parse_restart: } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ - if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ + if( ALWAYS(z[j-1]=='.') + && ALWAYS(j-2>=i) + && sqlite3Isdigit(z[j-2]) + ){ pParse->hasNonstd = 1; jnFlags |= JNODE_JSON5; }else{ @@ -1952,12 +1971,14 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); /* -** Parse a complete JSON string. Return 0 on success or non-zero if there -** are any errors. If an error occurs, free all memory held by pParse, -** but not pParse itself. +** Parse JSON (either pure RFC-8259 JSON text, or JSON-5 text, or a JSONB +** blob) into the JsonNode representation. ** -** pParse must be initialized to an empty parse object prior to calling -** this routine. +** Return 0 on success or non-zero if there are any errors. +** If an error occurs, free all memory held by pParse, but not pParse itself. +** +** pParse must be initialized with pParse->zJson set to the input text or +** blob prior to calling this routine. */ static int jsonParse( JsonParse *pParse, /* Initialize and fill this JsonParse object */ @@ -1970,7 +1991,7 @@ static int jsonParse( pParse->nBlob = pParse->nJson; i = jsonParseValueFromBlob(pParse, 0); }else{ - i = jsonParseValue(pParse, 0); + i = jsonParseValueFromText(pParse, 0); } if( pParse->oom ) i = -1; if( !pParse->isBinary && i>0 ){ @@ -2279,7 +2300,7 @@ static JsonNode *jsonLookupStep( assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart; pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); + JSON_VVA( pRoot->eU = 2 ); pParse->aNode[iLabel].jnFlags |= JNODE_RAW; } return pNode; @@ -2298,7 +2319,9 @@ static JsonNode *jsonLookupStep( if( pRoot->eType!=JSON_ARRAY ) return 0; for(;;){ while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; + if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ + i++; + } j += jsonNodeSize(&pBase[j]); } if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; @@ -2360,7 +2383,7 @@ static JsonNode *jsonLookupStep( assert( pRoot->eU==0 ); pRoot->u.iAppend = iStart; pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); + JSON_VVA( pRoot->eU = 2 ); } return pNode; } @@ -3758,9 +3781,9 @@ static void jsonReturnTextJsonFromBlob( memset(&x, 0, sizeof(x)); x.aBlob = (u8*)aBlob; x.nBlob = nBlob; - jsonInit(&s, ctx); + jsonStringInit(&s, ctx); jsonRenderBlob(&x, 0, &s); - jsonResult(&s); + jsonReturnString(&s); } @@ -4050,7 +4073,7 @@ static void jsonParseFunc( printf("iSubst = %u\n", p->iSubst); printf("iHold = %u\n", p->iHold); jsonDebugPrintNodeEntries(p->aNode, p->nNode); - jsonReturnJson(p, p->aNode, ctx, 1); + jsonReturnNodeAsJson(p, p->aNode, ctx, 1); } /* @@ -4156,9 +4179,9 @@ static void jsonQuoteFunc( JsonString jx; UNUSED_PARAMETER(argc); - jsonInit(&jx, ctx); - jsonAppendValue(&jx, argv[0]); - jsonResult(&jx); + jsonStringInit(&jx, ctx); + jsonAppendSqlValue(&jx, argv[0]); + jsonReturnString(&jx); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -4175,14 +4198,14 @@ static void jsonArrayFunc( int i; JsonString jx; - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '['); for(i=0; i $.LABEL // PG compatible ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); if( sqlite3Isdigit(zPath[0]) ){ jsonAppendRawNZ(&jx, "$[", 2); jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); @@ -4295,27 +4318,27 @@ static void jsonExtractFunc( jsonAppendChar(&jx, 0); } pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); - jsonReset(&jx); + jsonStringReset(&jx); }else{ pNode = jsonLookup(p, zPath, 0, ctx); } if( pNode ){ if( flags & JSON_JSON ){ - jsonReturnJson(p, pNode, ctx, 0); + jsonReturnNodeAsJson(p, pNode, ctx, 0); }else{ - jsonReturn(p, pNode, ctx); + jsonReturnNodeAsSql(p, pNode, ctx); sqlite3_result_subtype(ctx, 0); } } }else{ pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx); + if( p->nErr==0 && pNode ) jsonReturnNodeAsSql(p, pNode, ctx); } }else{ /* Two or more PATH arguments results in a JSON array with each ** element of the array being the value selected by one of the PATHs */ int i; - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '['); for(i=1; inErr ) break; jsonAppendSeparator(&jx); if( pNode ){ - jsonRenderNode(p, pNode, &jx); + jsonRenderNodeAsText(p, pNode, &jx); }else{ jsonAppendRawNZ(&jx, "null", 4); } } if( i==argc ){ jsonAppendChar(&jx, ']'); - jsonResult(&jx); + jsonReturnString(&jx); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } - jsonReset(&jx); + jsonStringReset(&jx); } } @@ -4399,7 +4422,7 @@ static JsonNode *jsonMergePatch( pParse->aNode[iStart].n = 1+nApnd; pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; pParse->aNode[iRoot].u.iAppend = iStart; - VVA( pParse->aNode[iRoot].eU = 2 ); + JSON_VVA( pParse->aNode[iRoot].eU = 2 ); iRoot = iStart; pTarget = &pParse->aNode[iTarget]; } @@ -4435,7 +4458,7 @@ static void jsonPatchFunc( if( pResult && pX->oom==0 ){ jsonDebugPrintParse(pX); jsonDebugPrintNode(pResult); - jsonReturnJson(pX, pResult, ctx, 0); + jsonReturnNodeAsJson(pX, pResult, ctx, 0); }else{ sqlite3_result_error_nomem(ctx); } @@ -4462,12 +4485,12 @@ static void jsonObjectFunc( "of arguments", -1); return; } - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '{'); for(i=0; iaNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1); } remove_done: jsonDebugPrintParse(p); @@ -4640,7 +4663,7 @@ static void jsonReplaceFunc( jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1); replace_err: jsonDebugPrintParse(pParse); } @@ -4692,7 +4715,7 @@ static void jsonSetFunc( } } jsonDebugPrintParse(pParse); - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1); jsonSetDone: /* no cleanup required */; @@ -4830,13 +4853,13 @@ static void jsonArrayStep( pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ - jsonInit(pStr, ctx); + jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; - jsonAppendValue(pStr, argv[0]); + jsonAppendSqlValue(pStr, argv[0]); } } static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ @@ -4936,7 +4959,7 @@ static void jsonObjectStep( pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ - jsonInit(pStr, ctx); + jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); @@ -4946,7 +4969,7 @@ static void jsonObjectStep( n = (u32)sqlite3_value_bytes(argv[0]); jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); - jsonAppendValue(pStr, argv[1]); + jsonAppendSqlValue(pStr, argv[1]); } } static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ @@ -5110,7 +5133,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ if( pUp->eType==JSON_ARRAY ){ assert( pUp->eU==0 || pUp->eU==3 ); testcase( pUp->eU==3 ); - VVA( pUp->eU = 3 ); + JSON_VVA( pUp->eU = 3 ); if( iUp==p->i-1 ){ pUp->u.iKey = 0; }else{ @@ -5208,7 +5231,7 @@ static int jsonEachColumn( case JEACH_KEY: { if( p->i==0 ) break; if( p->eType==JSON_OBJECT ){ - jsonReturn(&p->sParse, pThis, ctx); + jsonReturnNodeAsSql(&p->sParse, pThis, ctx); }else if( p->eType==JSON_ARRAY ){ u32 iKey; if( p->bRecursive ){ @@ -5224,7 +5247,7 @@ static int jsonEachColumn( } case JEACH_VALUE: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturn(&p->sParse, pThis, ctx); + jsonReturnNodeAsSql(&p->sParse, pThis, ctx); break; } case JEACH_TYPE: { @@ -5235,7 +5258,7 @@ static int jsonEachColumn( case JEACH_ATOM: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->eType>=JSON_ARRAY ) break; - jsonReturn(&p->sParse, pThis, ctx); + jsonReturnNodeAsSql(&p->sParse, pThis, ctx); break; } case JEACH_ID: { @@ -5251,7 +5274,7 @@ static int jsonEachColumn( } case JEACH_FULLKEY: { JsonString x; - jsonInit(&x, ctx); + jsonStringInit(&x, ctx); if( p->bRecursive ){ jsonEachComputePath(p, &x, p->i); }else{ @@ -5266,15 +5289,15 @@ static int jsonEachColumn( jsonAppendObjectPathElement(&x, pThis); } } - jsonResult(&x); + jsonReturnString(&x); break; } case JEACH_PATH: { if( p->bRecursive ){ JsonString x; - jsonInit(&x, ctx); + jsonStringInit(&x, ctx); jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); - jsonResult(&x); + jsonReturnString(&x); break; } /* For json_each() path and root are the same so fall through @@ -5444,7 +5467,7 @@ static int jsonEachFilter( p->eType = pNode->eType; if( p->eType>=JSON_ARRAY ){ assert( pNode->eU==0 ); - VVA( pNode->eU = 3 ); + JSON_VVA( pNode->eU = 3 ); pNode->u.iKey = 0; p->iEnd = p->i + pNode->n + 1; if( p->bRecursive ){ From a99e2fb576d793299a18a47f62330d3093a7a36e Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 29 Sep 2023 16:37:22 +0000 Subject: [PATCH 021/347] Additional refactoring and cleanup. FossilOrigin-Name: 45dd1760875b1ad899a10189c6f5a0a9a0677903794fb5a06ffacd39952a7882 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 113 +++++++++++++++++++++++++------------------------- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/manifest b/manifest index 7fcd2bd9c7..8a5c90e20d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scomments\sand\sprocedure\snames\sfor\sclarity\sin\sthe\sJSON\nimplementation. -D 2023-09-29T12:45:14.794 +C Additional\srefactoring\sand\scleanup. +D 2023-09-29T16:37:22.006 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 92d7c2ea8db842cd6a7a7b664bf43d5ae75e097981f001a5ab3860bd222132ca +F src/json.c d7de85731741831d299cfd76520f6af7e72b3b175f3cbfe22d0454e8abbb6090 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 1c0cba3461d6111b3aeb77726880221f1240355f0b57e060febbdeb12fb688c0 -R d73ce77d34a070de20c2504f2815e166 +P 9b620d813ef483f1277c1683c5e926a882f07f3b90804dea0c91b325ff8e45a4 +R bcca84e0a588bc606d4e22707cc86ed4 U drh -Z 100adbc30a124e4f2d5cc09bea6ade0f +Z 4c6f8cb9c19d993b3edfba11c172dd33 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7f67a4ccc1..9118fea8e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b620d813ef483f1277c1683c5e926a882f07f3b90804dea0c91b325ff8e45a4 \ No newline at end of file +45dd1760875b1ad899a10189c6f5a0a9a0677903794fb5a06ffacd39952a7882 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 97a6aebf4e..b381e7b28e 100644 --- a/src/json.c +++ b/src/json.c @@ -2657,7 +2657,7 @@ static int jsonIs4HexB(const char *z, int *pOp){ ** -4 ',' seen ** -5 ':' seen */ -static int jsonParseValueB(JsonParse *pParse, u32 i){ +static int jsonTranslateTextValueToBlob(JsonParse *pParse, u32 i){ char c; u32 j; u32 iThis, iStart; @@ -2677,7 +2677,7 @@ json_parse_restart: iStart = pParse->nBlob; for(j=i+1;;j++){ u32 iBlob = pParse->nBlob; - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x<=0 ){ int op; if( x==(-2) ){ @@ -2723,7 +2723,7 @@ json_parse_restart: goto parse_object_value; } } - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -2731,7 +2731,7 @@ json_parse_restart: j = pParse->iErr+1; } parse_object_value: - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -2750,7 +2750,7 @@ json_parse_restart: break; } } - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -2780,7 +2780,7 @@ json_parse_restart: return -1; } for(j=i+1;;j++){ - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; @@ -2804,7 +2804,7 @@ json_parse_restart: break; } } - x = jsonParseValueB(pParse, j); + x = jsonTranslateTextValueToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -3117,13 +3117,13 @@ json_parse_restart: ** pParse must be initialized to an empty parse object prior to calling ** this routine. */ -static int jsonParseB( +static int jsonConvertTextToBlob( JsonParse *pParse, /* Initialize and fill this JsonParse object */ sqlite3_context *pCtx /* Report errors here */ ){ int i; const char *zJson = pParse->zJson; - i = jsonParseValueB(pParse, 0); + i = jsonTranslateTextValueToBlob(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); @@ -4089,6 +4089,25 @@ static void jsonTest1Func( UNUSED_PARAMETER(argc); sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); } + +/* SQL Function: jsonb_test2(BLOB_JSON) +** +** Render BLOB_JSON back into text. +** Development testing only. +*/ +static void jsonbTest2( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + const u8 *aBlob; + int nBlob; + UNUSED_PARAMETER(argc); + + aBlob = (const u8*)sqlite3_value_blob(argv[0]); + nBlob = sqlite3_value_bytes(argv[0]); + jsonReturnTextJsonFromBlob(ctx, aBlob, nBlob); +} #endif /* SQLITE_DEBUG */ /**************************************************************************** @@ -4134,7 +4153,7 @@ static void jsonbFunc( memset(&x, 0, sizeof(x)); x.zJson = (char*)zJson; x.nJson = nJson; - if( jsonParseB(pParse, ctx) ){ + if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); @@ -4146,25 +4165,6 @@ static void jsonbFunc( } } -/* SQL Function: jsonb_test2(BLOB_JSON) -** -** Render BLOB_JSON back into text. -** Development testing only. -*/ -static void jsonbTest2( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - const u8 *aBlob; - int nBlob; - UNUSED_PARAMETER(argc); - - aBlob = (const u8*)sqlite3_value_blob(argv[0]); - nBlob = sqlite3_value_bytes(argv[0]); - jsonReturnTextJsonFromBlob(ctx, aBlob, nBlob); -} - /* ** Implementation of the json_quote(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting @@ -5549,36 +5549,35 @@ static sqlite3_module jsonTreeModule = { void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - JFUNCTION(json, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(jsonb_extract, -1, JSON_BLOB, jsonExtractFunc), - JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 0, jsonValidFunc), - JFUNCTION(jsonb, 1, JSON_BLOB, jsonbFunc), - JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), - JFUNCTION(jsonb_insert, -1, JSON_BLOB, jsonSetFunc), - JFUNCTION(jsonb_patch, 2, JSON_BLOB, jsonPatchFunc), - JFUNCTION(jsonb_quote, 1, JSON_BLOB, jsonQuoteFunc), - JFUNCTION(jsonb_remove, -1, JSON_BLOB, jsonRemoveFunc), - JFUNCTION(jsonb_replace, -1, JSON_BLOB, jsonReplaceFunc), + JFUNCTION(json, 1, 0, jsonRemoveFunc), + JFUNCTION(jsonb, 1, JSON_BLOB, jsonbFunc), + JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1, 0, jsonErrorFunc), + JFUNCTION(json_extract, -1, 0, jsonExtractFunc), + JFUNCTION(jsonb_extract, -1, JSON_BLOB, jsonExtractFunc), + JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 0, jsonSetFunc), + JFUNCTION(jsonb_insert, -1, JSON_BLOB, jsonSetFunc), + JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 0, jsonPatchFunc), + JFUNCTION(jsonb_patch, 2, JSON_BLOB, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), + JFUNCTION(jsonb_remove, -1, JSON_BLOB, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), + JFUNCTION(jsonb_replace, -1, JSON_BLOB, jsonReplaceFunc), + JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), JFUNCTION(jsonb_set, -1, JSON_ISSET|JSON_BLOB, jsonSetFunc), + JFUNCTION(json_type, 1, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 0, jsonValidFunc), #if SQLITE_DEBUG - JFUNCTION(json_parse, 1, 0, jsonParseFunc), - JFUNCTION(json_test1, 1, 0, jsonTest1Func), + JFUNCTION(json_parse, 1, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 0, jsonTest1Func), + JFUNCTION(jsonb_test2, 1, 0, jsonbTest2), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, From ee50aad86544f188fa87644376fa3a7879abb043 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 29 Sep 2023 19:47:25 +0000 Subject: [PATCH 022/347] The u.zJContent field of JsonNode for a string or label should NOT include the quotation mark delimiters. Ever. This is an inefficiency that really ought to be fixed on trunk, but that can wait until this branch lands. FossilOrigin-Name: 96f545f6f839dab4829861361ee3d7a56840217c5f954f334616e77d23c5fe29 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 50 +++++++++++++++++++++++--------------------------- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/manifest b/manifest index 8a5c90e20d..97725044f7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Additional\srefactoring\sand\scleanup. -D 2023-09-29T16:37:22.006 +C The\su.zJContent\sfield\sof\sJsonNode\sfor\sa\sstring\sor\slabel\sshould\sNOT\sinclude\nthe\squotation\smark\sdelimiters.\s\sEver.\s\sThis\sis\san\sinefficiency\sthat\sreally\nought\sto\sbe\sfixed\son\strunk,\sbut\sthat\scan\swait\suntil\sthis\sbranch\slands. +D 2023-09-29T19:47:25.994 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d7de85731741831d299cfd76520f6af7e72b3b175f3cbfe22d0454e8abbb6090 +F src/json.c 2112b76a0d0ed938df844dcac57170ef825c6fb92f0bb1e2c35360d77fa8abbd F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 9b620d813ef483f1277c1683c5e926a882f07f3b90804dea0c91b325ff8e45a4 -R bcca84e0a588bc606d4e22707cc86ed4 +P 45dd1760875b1ad899a10189c6f5a0a9a0677903794fb5a06ffacd39952a7882 +R 0ad6f1d70a03e9858beb1d83ab248cfc U drh -Z 4c6f8cb9c19d993b3edfba11c172dd33 +Z ecabd724f6e30a2efa3505d39eb1f7e4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9118fea8e8..fd367b5df4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -45dd1760875b1ad899a10189c6f5a0a9a0677903794fb5a06ffacd39952a7882 \ No newline at end of file +96f545f6f839dab4829861361ee3d7a56840217c5f954f334616e77d23c5fe29 \ No newline at end of file diff --git a/src/json.c b/src/json.c index b381e7b28e..9b988c1b66 100644 --- a/src/json.c +++ b/src/json.c @@ -435,7 +435,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ } } static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ - assert( N>0 ); + if( N==0 ) return; if( N+p->nUsed >= p->nAlloc ){ jsonStringExpandAndAppend(p,zIn,N); }else{ @@ -557,8 +557,6 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ u32 i; jsonAppendChar(p, '"'); - zIn++; - N -= 2; while( N>0 ){ for(i=0; i0 ){ @@ -894,8 +892,9 @@ static void jsonRenderNodeAsText( }else if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); }else{ - assert( pNode->n>0 ); + jsonAppendChar(pOut, '"'); jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); + jsonAppendChar(pOut, '"'); } break; } @@ -1118,7 +1117,7 @@ static void jsonReturnNodeAsSql( }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ /* JSON formatted without any backslash-escapes */ assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, + sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, SQLITE_TRANSIENT); }else{ /* Translate JSON formatted string into raw text */ @@ -1135,7 +1134,7 @@ static void jsonReturnNodeAsSql( sqlite3_result_error_nomem(pCtx); break; } - for(i=1, j=0; ieU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->n!=nKey ) return 0; - return strncmp(pNode->u.zJContent, zKey, nKey)==0; - }else{ - if( pNode->n!=nKey+2 ) return 0; - return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; - } + if( pNode->n!=nKey ) return 0; + return strncmp(pNode->u.zJContent, zKey, nKey)==0; } static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ if( p1->jnFlags & JNODE_RAW ){ @@ -5169,26 +5163,28 @@ static void jsonAppendObjectPathElement( JsonString *pStr, JsonNode *pNode ){ - int jj, nn; + int nn; const char *z; + int bNeedQuote = 0; assert( pNode->eType==JSON_STRING ); assert( pNode->jnFlags & JNODE_LABEL ); assert( pNode->eU==1 ); z = pNode->u.zJContent; nn = pNode->n; - if( (pNode->jnFlags & JNODE_RAW)==0 ){ - assert( nn>=2 ); - assert( z[0]=='"' || z[0]=='\'' ); - assert( z[nn-1]=='"' || z[0]=='\'' ); - if( nn>2 && sqlite3Isalpha(z[1]) ){ - for(jj=2; jjjnFlags & JNODE_RAW ){ + /* no-op */ + }else if( nn==0 || !sqlite3Isalpha(z[0]) ){ + bNeedQuote = 1; + }else{ + int jj; + for(jj=1; jj Date: Fri, 29 Sep 2023 22:37:18 +0000 Subject: [PATCH 023/347] Update json_each() and json_tree() so that they work with JSONB inputs. FossilOrigin-Name: bb5e50ff56dff95d954aacdd4c5461790f953cef8d7b89da000d8d587fcdf9b8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 13 +++++++++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 97725044f7..96d7117b35 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\su.zJContent\sfield\sof\sJsonNode\sfor\sa\sstring\sor\slabel\sshould\sNOT\sinclude\nthe\squotation\smark\sdelimiters.\s\sEver.\s\sThis\sis\san\sinefficiency\sthat\sreally\nought\sto\sbe\sfixed\son\strunk,\sbut\sthat\scan\swait\suntil\sthis\sbranch\slands. -D 2023-09-29T19:47:25.994 +C Update\sjson_each()\sand\sjson_tree()\sso\sthat\sthey\swork\swith\sJSONB\sinputs. +D 2023-09-29T22:37:18.176 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 2112b76a0d0ed938df844dcac57170ef825c6fb92f0bb1e2c35360d77fa8abbd +F src/json.c 50fb4f1c25eb749e0186fe68f095325d40ebe59aa3a53063f00f382cfd2c36fd F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 45dd1760875b1ad899a10189c6f5a0a9a0677903794fb5a06ffacd39952a7882 -R 0ad6f1d70a03e9858beb1d83ab248cfc +P 96f545f6f839dab4829861361ee3d7a56840217c5f954f334616e77d23c5fe29 +R 9027bab5f11df4bc07814add94e083b5 U drh -Z ecabd724f6e30a2efa3505d39eb1f7e4 +Z d0258d856fbcf0571a1fd4c95a27f4d8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fd367b5df4..a8c16bbd39 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -96f545f6f839dab4829861361ee3d7a56840217c5f954f334616e77d23c5fe29 \ No newline at end of file +bb5e50ff56dff95d954aacdd4c5461790f953cef8d7b89da000d8d587fcdf9b8 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9b988c1b66..a790157a50 100644 --- a/src/json.c +++ b/src/json.c @@ -5402,12 +5402,19 @@ static int jsonEachFilter( const char *z; const char *zRoot = 0; sqlite3_int64 n; + int isBinary; UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; - z = (const char*)sqlite3_value_text(argv[0]); + if( jsonFuncArgMightBeBinary(argv[0]) ){ + z = (const char*)sqlite3_value_blob(argv[0]); + isBinary = 1; + }else{ + z = (const char*)sqlite3_value_text(argv[0]); + isBinary = 0; + } if( z==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; @@ -5417,9 +5424,11 @@ static int jsonEachFilter( n = sqlite3_value_bytes(argv[0]); p->sParse.zJson = sqlite3RCStrNew( n+1 ); if( p->sParse.zJson==0 ) return SQLITE_NOMEM; - memcpy(p->sParse.zJson, z, (size_t)n+1); + memcpy(p->sParse.zJson, z, (size_t)n+(isBinary==0)); + p->sParse.nJson = n; } p->sParse.bJsonIsRCStr = 1; + p->sParse.isBinary = isBinary; p->zJson = p->sParse.zJson; if( jsonParse(&p->sParse, 0) ){ int rc = SQLITE_NOMEM; From 7e86d3fc6961dc64a45cfefd6ceeea375167ffc4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 30 Sep 2023 14:34:39 +0000 Subject: [PATCH 024/347] Finish adding jsonb_ versions for all JSON routines that return JSON text. FossilOrigin-Name: 6daa7b69695e1b68dba317abbcad4d0205c91963d4a9eb2d595a3ec10fa0fdf4 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 96d7117b35..668f82b009 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sjson_each()\sand\sjson_tree()\sso\sthat\sthey\swork\swith\sJSONB\sinputs. -D 2023-09-29T22:37:18.176 +C Finish\sadding\sjsonb_\sversions\sfor\sall\sJSON\sroutines\sthat\sreturn\sJSON\stext. +D 2023-09-30T14:34:39.518 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 50fb4f1c25eb749e0186fe68f095325d40ebe59aa3a53063f00f382cfd2c36fd +F src/json.c d4de26b1c8a18949b80a89d457c4714a00cc65c8b32b965a5f921556daeb48f8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 96f545f6f839dab4829861361ee3d7a56840217c5f954f334616e77d23c5fe29 -R 9027bab5f11df4bc07814add94e083b5 +P bb5e50ff56dff95d954aacdd4c5461790f953cef8d7b89da000d8d587fcdf9b8 +R 29e50b8d59965f3c05293a631078e5bd U drh -Z d0258d856fbcf0571a1fd4c95a27f4d8 +Z bae80fe0062bce2a09237dc9749d4f41 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a8c16bbd39..dadf8fc937 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb5e50ff56dff95d954aacdd4c5461790f953cef8d7b89da000d8d587fcdf9b8 \ No newline at end of file +6daa7b69695e1b68dba317abbcad4d0205c91963d4a9eb2d595a3ec10fa0fdf4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index a790157a50..0089584e62 100644 --- a/src/json.c +++ b/src/json.c @@ -724,6 +724,8 @@ static void jsonAppendSqlValue( } } +/* Forward reference */ +static void jsonReturnStringAsBlob(JsonString*); /* Make the text in p (which is probably a generated JSON text string) ** the result of the SQL function. @@ -732,7 +734,10 @@ static void jsonAppendSqlValue( */ static void jsonReturnString(JsonString *p){ if( p->bErr==0 ){ - if( p->bStatic ){ + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); + if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(p); + }else if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); }else if( jsonForceRCStr(p) ){ @@ -3145,6 +3150,25 @@ static int jsonConvertTextToBlob( return 0; } +/* +** The input string pStr is a well-formed JSON text string. Convert +** this into the JSONB format and make it the return value of the +** SQL function. +*/ +static void jsonReturnStringAsBlob(JsonString *pStr){ + JsonParse px; + memset(&px, 0, sizeof(px)); + px.zJson = pStr->zBuf; + px.nJson = pStr->nUsed; + (void)jsonTranslateTextValueToBlob(&px, 0); + if( px.oom ){ + sqlite3_free(px.aBlob); + sqlite3_result_error_nomem(pStr->pCtx); + }else{ + sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, sqlite3_free); + } +} + /* The byte at index i is a node type-code. This routine ** determines the payload size for that node and writes that ** payload size in to *pSz. It returns the offset from i to the @@ -4860,11 +4884,16 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ + int flags; pStr->pCtx = ctx; jsonAppendChar(pStr, ']'); + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->bErr ){ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); assert( pStr->bStatic ); + }else if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(pStr); + return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : @@ -4970,10 +4999,16 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ + int flags; jsonAppendChar(pStr, '}'); + pStr->pCtx = ctx; + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->bErr ){ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); assert( pStr->bStatic ); + }else if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(pStr); + return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : @@ -5557,6 +5592,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json, 1, 0, jsonRemoveFunc), JFUNCTION(jsonb, 1, JSON_BLOB, jsonbFunc), JFUNCTION(json_array, -1, 0, jsonArrayFunc), + JFUNCTION(jsonb_array, -1, JSON_BLOB, jsonArrayFunc), JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), JFUNCTION(json_error_position,1, 0, jsonErrorFunc), @@ -5567,6 +5603,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_insert, -1, 0, jsonSetFunc), JFUNCTION(jsonb_insert, -1, JSON_BLOB, jsonSetFunc), JFUNCTION(json_object, -1, 0, jsonObjectFunc), + JFUNCTION(jsonb_object, -1, JSON_BLOB, jsonObjectFunc), JFUNCTION(json_patch, 2, 0, jsonPatchFunc), JFUNCTION(jsonb_patch, 2, JSON_BLOB, jsonPatchFunc), JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), @@ -5587,7 +5624,13 @@ void sqlite3RegisterJsonFunctions(void){ WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), WAGGREGATE(json_group_object, 2, 0, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC) }; From d8f26350a0831acec6fe51f637f73aa48a9b091e Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 30 Sep 2023 16:50:17 +0000 Subject: [PATCH 025/347] Restore the trunk version of sqlite3_user_data(). Fix the xColumn for virtual tables so that the sqlite3_context contains a valid but NULL user data pointer. FossilOrigin-Name: 15ffd932fecc82a5791b2024f55d2ec80887e8eb7345de68d6f5cac4912cfbe8 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbe.c | 3 +++ src/vdbeapi.c | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 668f82b009..c46034ef3b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Finish\sadding\sjsonb_\sversions\sfor\sall\sJSON\sroutines\sthat\sreturn\sJSON\stext. -D 2023-09-30T14:34:39.518 +C Restore\sthe\strunk\sversion\sof\ssqlite3_user_data().\s\sFix\sthe\sxColumn\sfor\svirtual\ntables\sso\sthat\sthe\ssqlite3_context\scontains\sa\svalid\sbut\sNULL\suser\sdata\spointer. +D 2023-09-30T16:50:17.069 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -780,10 +780,10 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c e87f66258c37f87724f46e849572c3ece4c74ef5614ba41eb221e98f0dbc95de F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 -F src/vdbe.c cd112eb00d20fc5cc44f631d0e713838602637328b0f127c2f3c2aa8cea3cc91 +F src/vdbe.c 03e66cfa55542c3ae089a1926d06459f8d67cdbad9d52ddc8a7559b7505e90f7 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c 7fdd1eec6b967a5455ccc45a43b84fd32746d6749a423104333f9ba5cec56c9f +F src/vdbeapi.c b82f465cfaa77cf88c414bc53cb8515523e6840956283cfc7e8d284f72640735 F src/vdbeaux.c 5b415e09b5b9d5be6c0f4fcbf18ea9d7d16f6a29ced2f14a3b2041020f63e9c1 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce F src/vdbemem.c 317b9f48708139db6239ade40c7980b4bc8233168383690d588dad6d8437f722 @@ -2123,8 +2123,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 bb5e50ff56dff95d954aacdd4c5461790f953cef8d7b89da000d8d587fcdf9b8 -R 29e50b8d59965f3c05293a631078e5bd +P 6daa7b69695e1b68dba317abbcad4d0205c91963d4a9eb2d595a3ec10fa0fdf4 +R 156c1068608d73b6c04a73abb658869e U drh -Z bae80fe0062bce2a09237dc9749d4f41 +Z 48c7e5fa14a791868247c634dc1c52ad # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dadf8fc937..121b0eb585 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6daa7b69695e1b68dba317abbcad4d0205c91963d4a9eb2d595a3ec10fa0fdf4 \ No newline at end of file +15ffd932fecc82a5791b2024f55d2ec80887e8eb7345de68d6f5cac4912cfbe8 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 6463136507..f2d94237a8 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -8290,6 +8290,7 @@ case OP_VColumn: { /* ncycle */ const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; + FuncDef nullFunc; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur!=0 ); @@ -8307,6 +8308,8 @@ case OP_VColumn: { /* ncycle */ memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; sContext.enc = encoding; + nullFunc.pUserData = 0; + sContext.pFunc = &nullFunc; assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 6b32506f9f..91843f5636 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -845,7 +845,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){ ** pointer to it. */ void *sqlite3_user_data(sqlite3_context *p){ - return (p && p->pFunc) ? p->pFunc->pUserData : 0; + return p->pFunc->pUserData; } /* From a35ae44150c9afa5b74dd364f015767371dbe97d Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 30 Sep 2023 18:13:35 +0000 Subject: [PATCH 026/347] Changes so that fts5 can handle tokens with embedded '\0' bytes. FossilOrigin-Name: c027c092c4af53bd6ae3cc6e2b4439167d9eeb0f9de549b6a2c2a72a67ee886c --- ext/fts5/fts5Int.h | 1 + ext/fts5/fts5_hash.c | 49 +++++---- ext/fts5/fts5_index.c | 23 ++-- ext/fts5/fts5_tcl.c | 173 +++++++++++++++++++++++++++++- ext/fts5/test/fts5origintext.test | 116 ++++++++++++++++++++ manifest | 24 +++-- manifest.uuid | 2 +- 7 files changed, 345 insertions(+), 43 deletions(-) create mode 100644 ext/fts5/test/fts5origintext.test diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 8bbafbaaf4..1687168d5f 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -645,6 +645,7 @@ void sqlite3Fts5HashScanNext(Fts5Hash*); int sqlite3Fts5HashScanEof(Fts5Hash*); void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ + int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); diff --git a/ext/fts5/fts5_hash.c b/ext/fts5/fts5_hash.c index 7e50c36608..f6224f1275 100644 --- a/ext/fts5/fts5_hash.c +++ b/ext/fts5/fts5_hash.c @@ -36,10 +36,15 @@ struct Fts5Hash { /* ** Each entry in the hash table is represented by an object of the -** following type. Each object, its key (a nul-terminated string) and -** its current data are stored in a single memory allocation. The -** key immediately follows the object in memory. The position list -** data immediately follows the key data in memory. +** following type. Each object, its key, and its current data are stored +** in a single memory allocation. The key immediately follows the object +** in memory. The position list data immediately follows the key data +** in memory. +** +** The key is Fts5HashEntry.nKey bytes in size. It consists of a single +** byte identifying the index (either the main term index or a prefix-index), +** followed by the term data. For example: "0token". There is no +** nul-terminator - in this case nKey=6. ** ** The data that follows the key is in a similar, but not identical format ** to the doclist data stored in the database. It is: @@ -174,8 +179,7 @@ static int fts5HashResize(Fts5Hash *pHash){ unsigned int iHash; Fts5HashEntry *p = apOld[i]; apOld[i] = p->pHashNext; - iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), - (int)strlen(fts5EntryKey(p))); + iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey); p->pHashNext = apNew[iHash]; apNew[iHash] = p; } @@ -259,7 +263,7 @@ int sqlite3Fts5HashWrite( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ char *zKey = fts5EntryKey(p); if( zKey[0]==bByte - && p->nKey==nToken + && p->nKey==nToken+1 && memcmp(&zKey[1], pToken, nToken)==0 ){ break; @@ -289,9 +293,9 @@ int sqlite3Fts5HashWrite( zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); - p->nKey = nToken; + p->nKey = nToken+1; zKey[nToken+1] = '\0'; - p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry); + p->nData = nToken+1 + sizeof(Fts5HashEntry); p->pHashNext = pHash->aSlot[iHash]; pHash->aSlot[iHash] = p; pHash->nEntry++; @@ -408,12 +412,17 @@ static Fts5HashEntry *fts5HashEntryMerge( *ppOut = p1; p1 = 0; }else{ - int i = 0; char *zKey1 = fts5EntryKey(p1); char *zKey2 = fts5EntryKey(p2); - while( zKey1[i]==zKey2[i] ) i++; + int nMin = MIN(p1->nKey, p2->nKey); - if( ((u8)zKey1[i])>((u8)zKey2[i]) ){ + int cmp = memcmp(zKey1, zKey2, nMin); + if( cmp==0 ){ + cmp = p1->nKey - p2->nKey; + } + assert( cmp!=0 ); + + if( cmp>0 ){ /* p2 is smaller */ *ppOut = p2; ppOut = &p2->pScanNext; @@ -457,7 +466,7 @@ static int fts5HashEntrySort( Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ if( pTerm==0 - || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) + || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) ){ Fts5HashEntry *pEntry = pIter; pEntry->pScanNext = 0; @@ -496,12 +505,11 @@ int sqlite3Fts5HashQuery( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); - assert( p->nKey+1==(int)strlen(zKey) ); - if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; + if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; } if( p ){ - int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; + int nHashPre = sizeof(Fts5HashEntry) + nTerm; int nList = p->nData - nHashPre; u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); if( pRet ){ @@ -562,19 +570,22 @@ int sqlite3Fts5HashScanEof(Fts5Hash *p){ void sqlite3Fts5HashScanEntry( Fts5Hash *pHash, const char **pzTerm, /* OUT: term (nul-terminated) */ + int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ){ Fts5HashEntry *p; if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); - int nTerm = (int)strlen(zKey); + int nTerm = p->nKey; fts5HashAddPoslistSize(pHash, p, 0); *pzTerm = zKey; - *ppDoclist = (const u8*)&zKey[nTerm+1]; - *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); + *pnTerm = nTerm; + *ppDoclist = (const u8*)&zKey[nTerm]; + *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); }else{ *pzTerm = 0; + *pnTerm = 0; *ppDoclist = 0; *pnDoclist = 0; } diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index f527709237..9db5925b94 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -2177,15 +2177,16 @@ static void fts5SegIterNext_None( }else{ const u8 *pList = 0; const char *zTerm = 0; + int nTerm = 0; int nList; sqlite3Fts5HashScanNext(p->pHash); - sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); if( pList==0 ) goto next_none_eof; pIter->pLeaf->p = (u8*)pList; pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList; - sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); + sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); } @@ -2251,11 +2252,12 @@ static void fts5SegIterNext( }else if( pIter->pSeg==0 ){ const u8 *pList = 0; const char *zTerm = 0; + int nTerm = 0; int nList = 0; assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ sqlite3Fts5HashScanNext(p->pHash); - sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); } if( pList==0 ){ fts5DataRelease(pIter->pLeaf); @@ -2265,8 +2267,7 @@ static void fts5SegIterNext( pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList+1; - sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), - (u8*)zTerm); + sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); *pbNewTerm = 1; } @@ -2711,8 +2712,7 @@ static void fts5SegIterHashInit( const u8 *pList = 0; p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); - sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); - n = (z ? (int)strlen((const char*)z) : 0); + sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); if( pList ){ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); if( pLeaf ){ @@ -5313,10 +5313,10 @@ static void fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, + int nTerm, i64 iRowid ){ const int f = FTS5INDEX_QUERY_SKIPHASH; - int nTerm = (int)strlen(zTerm); Fts5Iter *pIter = 0; /* Used to find term instance */ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); @@ -5390,8 +5390,7 @@ static void fts5FlushOneHash(Fts5Index *p){ int nDoclist; /* Size of doclist in bytes */ /* Get the term and doclist for this entry. */ - sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); - nTerm = (int)strlen(zTerm); + sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); if( bSecureDelete==0 ){ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); if( p->rc!=SQLITE_OK ) break; @@ -5421,7 +5420,7 @@ static void fts5FlushOneHash(Fts5Index *p){ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ if( iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; continue; diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c index 80c600dbb1..fb4bea8e9e 100644 --- a/ext/fts5/fts5_tcl.c +++ b/ext/fts5/fts5_tcl.c @@ -1117,6 +1117,176 @@ static int SQLITE_TCLAPI f5tRegisterTok( return TCL_OK; } +typedef struct OriginTextCtx OriginTextCtx; +struct OriginTextCtx { + sqlite3 *db; + fts5_api *pApi; +}; + +typedef struct OriginTextTokenizer OriginTextTokenizer; +struct OriginTextTokenizer { + Fts5Tokenizer *pTok; /* Underlying tokenizer object */ + fts5_tokenizer tokapi; /* API implementation for pTok */ +}; + +/* +** Delete the OriginTextCtx object indicated by the only argument. +*/ +static void f5tOrigintextTokenizerDelete(void *pCtx){ + OriginTextCtx *p = (OriginTextCtx*)pCtx; + ckfree(p); +} + +static int f5tOrigintextCreate( + void *pCtx, + const char **azArg, + int nArg, + Fts5Tokenizer **ppOut +){ + OriginTextCtx *p = (OriginTextCtx*)pCtx; + OriginTextTokenizer *pTok = 0; + void *pTokCtx = 0; + int rc = SQLITE_OK; + + pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer)); + if( pTok==0 ){ + rc = SQLITE_NOMEM; + }else if( nArg<1 ){ + rc = SQLITE_ERROR; + }else{ + /* Locate the underlying tokenizer */ + rc = p->pApi->xFindTokenizer(p->pApi, azArg[0], &pTokCtx, &pTok->tokapi); + } + + /* Create the new tokenizer instance */ + if( rc==SQLITE_OK ){ + rc = pTok->tokapi.xCreate(pTokCtx, &azArg[1], nArg-1, &pTok->pTok); + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pTok); + pTok = 0; + } + *ppOut = (Fts5Tokenizer*)pTok; + return rc; +} + +static void f5tOrigintextDelete(Fts5Tokenizer *pTokenizer){ + OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer; + if( p->pTok ){ + p->tokapi.xDelete(p->pTok); + } + sqlite3_free(p); +} + +typedef struct OriginTextCb OriginTextCb; +struct OriginTextCb { + void *pCtx; + const char *pText; + int nText; + int (*xToken)(void *, int, const char *, int, int, int); + + char *aBuf; /* Buffer to use */ + int nBuf; /* Allocated size of aBuf[] */ +}; + +static int xOriginToken( + void *pCtx, /* Copy of 2nd argument to xTokenize() */ + int tflags, /* Mask of FTS5_TOKEN_* flags */ + const char *pToken, /* Pointer to buffer containing token */ + int nToken, /* Size of token in bytes */ + int iStart, /* Byte offset of token within input text */ + int iEnd /* Byte offset of end of token within input */ +){ + OriginTextCb *p = (OriginTextCb*)pCtx; + int ret = 0; + + if( nToken==(iEnd-iStart) && 0==memcmp(pToken, &p->pText[iStart], nToken) ){ + /* Token exactly matches document text. Pass it through as is. */ + ret = p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd); + }else{ + int nReq = nToken + 1 + (iEnd-iStart); + if( nReq>p->nBuf ){ + sqlite3_free(p->aBuf); + p->aBuf = sqlite3_malloc(nReq*2); + if( p->aBuf==0 ) return SQLITE_NOMEM; + p->nBuf = nReq*2; + } + + memcpy(p->aBuf, pToken, nToken); + p->aBuf[nToken] = '\0'; + memcpy(&p->aBuf[nToken+1], &p->pText[iStart], iEnd-iStart); + ret = p->xToken(p->pCtx, tflags, p->aBuf, nReq, iStart, iEnd); + } + + return ret; +} + + +static int f5tOrigintextTokenize( + Fts5Tokenizer *pTokenizer, + void *pCtx, + int flags, /* Mask of FTS5_TOKENIZE_* flags */ + const char *pText, int nText, + int (*xToken)(void *, int, const char *, int, int, int) +){ + OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer; + OriginTextCb cb; + int ret; + + memset(&cb, 0, sizeof(cb)); + cb.pCtx = pCtx; + cb.pText = pText; + cb.nText = nText; + cb.xToken = xToken; + + ret = p->tokapi.xTokenize(p->pTok,(void*)&cb,flags,pText,nText,xOriginToken); + sqlite3_free(cb.aBuf); + return ret; +} + +/* +** sqlite3_fts5_register_origintext DB +** +** Description... +*/ +static int SQLITE_TCLAPI f5tRegisterOriginText( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db = 0; + fts5_api *pApi = 0; + int rc; + fts5_tokenizer tok = {0, 0, 0}; + OriginTextCtx *pCtx = 0; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR; + + pCtx = (OriginTextCtx*)ckalloc(sizeof(OriginTextCtx)); + pCtx->db = db; + pCtx->pApi = pApi; + + tok.xCreate = f5tOrigintextCreate; + tok.xDelete = f5tOrigintextDelete; + tok.xTokenize = f5tOrigintextTokenize; + rc = pApi->xCreateTokenizer( + pApi, "origintext", (void*)pCtx, &tok, f5tOrigintextTokenizerDelete + ); + + Tcl_ResetResult(interp); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); + return TCL_ERROR; + } + return TCL_OK; +} + /* ** Entry point. */ @@ -1133,7 +1303,8 @@ int Fts5tcl_Init(Tcl_Interp *interp){ { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 }, { "sqlite3_fts5_token_hash", f5tTokenHash, 0 }, { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 }, - { "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 } + { "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 }, + { "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 } }; int i; F5tTokenizerContext *pContext; diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test new file mode 100644 index 0000000000..791b850c76 --- /dev/null +++ b/ext/fts5/test/fts5origintext.test @@ -0,0 +1,116 @@ +# 2014 Jan 08 +# +# 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. +# +#*********************************************************************** +# +# Tests focused on phrase queries. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5origintext + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +sqlite3_fts5_register_origintext db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); +} + +do_execsql_test 1.1 { + INSERT INTO ft VALUES('Hello world'); +} + +do_execsql_test 1.2 { + INSERT INTO ft(ft) VALUES('integrity-check'); +} + +proc b {x} { string map [list "\0" "."] $x } +db func b b + +do_execsql_test 1.3 { + select b(term) from vocab; +} { + hello.Hello + world +} + +#------------------------------------------------------------------------- +reset_db + +# Return a random integer between 0 and n-1. +# +proc random {n} { + expr {abs(int(rand()*$n))} +} + +proc select_one {list} { + set n [llength $list] + lindex $list [random $n] +} + +proc term {} { + set first_letter { + a b c d e f g h i j k l m n o p q r s t u v w x y z + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + } + + set term [select_one $first_letter] + append term [random 100] +} + +proc document {} { + set nTerm [expr [random 5] + 5] + set doc "" + for {set ii 0} {$ii < $nTerm} {incr ii} { + lappend doc [term] + } + set doc +} +db func document document + +sqlite3_fts5_register_origintext db +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + INSERT INTO ft(ft, rank) VALUES('pgsz', 128); + CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); +} + +do_test 2.1 { + for {set ii 0} {$ii < 500} {incr ii} { + execsql { INSERT INTO ft VALUES( document() ) } + } +} {} + +do_execsql_test 2.2 { + INSERT INTO ft(ft) VALUES('integrity-check'); +} + +do_execsql_test 2.3 { + INSERT INTO ft(ft, rank) VALUES('merge', 16); +} + +do_execsql_test 2.4 { + INSERT INTO ft(ft) VALUES('integrity-check'); +} + +do_execsql_test 2.5 { + INSERT INTO ft(ft) VALUES('optimize'); +} + +proc b {x} { string map [list "\0" "."] $x } +db func b b +#execsql_pp { SELECT b(term) FROM vocab } + +finish_test + diff --git a/manifest b/manifest index 2eae6bfcf8..bf21569e58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sJNI\sbinding\sto\scompile\swithout\sSQLITE_ENABLE_PREUPDATE_HOOK.\sAdd\sbuild\soption\sto\sdisable\sall\soptional\sENABLE\sflags. -D 2023-09-30T17:08:29.126 +C Changes\sso\sthat\sfts5\scan\shandle\stokens\swith\sembedded\s'\\0'\sbytes. +D 2023-09-30T18:13:35.306 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -88,16 +88,16 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h 05501612cc655504c5dce8ba765ab621d50fc478490089beaa0d75e00b23e520 -F ext/fts5/fts5Int.h 78a63cc0795186cde5384816a9403a68c65774b35d952e05b81a1b4b158e07c8 +F ext/fts5/fts5Int.h 66a38b285e2b860baa29745d8eff27f5b0809268e7820498494d9acfaccf8a5c F ext/fts5/fts5_aux.c 572d5ec92ba7301df2fea3258576332f2f4d2dfd66d8263afd157d9deceac480 F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 -F ext/fts5/fts5_hash.c 65e7707bc8774706574346d18c20218facf87de3599b995963c3e6d6809f203d -F ext/fts5/fts5_index.c a86bcd5637625ce1037649d55974ab8da1fa8d1375cb334aae47ef376642e93b +F ext/fts5/fts5_hash.c 76765856397eff56f526b0640b23a1677d737d35e07bc00e4b4b2e0fc5fda60d +F ext/fts5/fts5_index.c 16d775ecbccf7d3698a03bcae3c3fbee0749df748b93b29d0e82a37e02eaaa94 F ext/fts5/fts5_main.c 799ec88d2309055f6406bddb0bd6ed80148c5da5eb14594c3c5309a6e944d489 F ext/fts5/fts5_storage.c 3c9b41fce41b6410f2e8f82eb035c6a29b2560483f773e6dc98cf3cb2e4ddbb5 -F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae +F ext/fts5/fts5_tcl.c 0d2bb0ff7bf6ee136015be118167f0bd956ddd05a8f02c68bd34299b50648f9f F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_tok.c a2bed8edb25f6432e8cdb62aad5916935c19dba8dac2b8324950cfff397e25ff F ext/fts5/fts5_tokenize.c 5e251efb0f1af99a25ed50010ba6b1ad1250aca5921af1988fdcabe5ebc3cb43 @@ -187,6 +187,7 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca +F ext/fts5/test/fts5origintext.test 9a6edc85ccc4afb10e71d54d98d8170f850272e55b120520f367afbb12526674 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2122,8 +2123,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5e387275f69ab2d3159b4b67b8cbfc6270410b61e5ac1f988616e8d051f6572e -R 02618be495064fd0c511f49fba8a92b2 -U stephan -Z fb900a6927398da79962f35041fce8dc +P c04022b7407f77eaf0175e831ebcd6bbdc0af1cef0d42c5c11102aa8484f24ca +R ee3b13ddf778c77c1640cd7c7844c1f5 +T *branch * fts5-token-data +T *sym-fts5-token-data * +T -sym-trunk * +U dan +Z 7d5bd217a552215de3d888e155abaef5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 92d537682b..9db9eb3c64 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c04022b7407f77eaf0175e831ebcd6bbdc0af1cef0d42c5c11102aa8484f24ca \ No newline at end of file +c027c092c4af53bd6ae3cc6e2b4439167d9eeb0f9de549b6a2c2a72a67ee886c \ No newline at end of file From 9b176f863b41dc34dd7e5abcbb593500751848e6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 1 Oct 2023 18:59:34 +0000 Subject: [PATCH 027/347] Fix problems following OOM in JSONB parsing. FossilOrigin-Name: 0d8cd6b5fb592f88f593ceaad9cdfa95dcfdf14169c3a56a5becbd66d62fd02b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 12 +++++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index c46034ef3b..892d400ef4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Restore\sthe\strunk\sversion\sof\ssqlite3_user_data().\s\sFix\sthe\sxColumn\sfor\svirtual\ntables\sso\sthat\sthe\ssqlite3_context\scontains\sa\svalid\sbut\sNULL\suser\sdata\spointer. -D 2023-09-30T16:50:17.069 +C Fix\sproblems\sfollowing\sOOM\sin\sJSONB\sparsing. +D 2023-10-01T18:59:34.631 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d4de26b1c8a18949b80a89d457c4714a00cc65c8b32b965a5f921556daeb48f8 +F src/json.c 253ca59ae7f4be5fa7e0d630f9073f6d2f8b6dd89b47d80192f3b037271fe45e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 6daa7b69695e1b68dba317abbcad4d0205c91963d4a9eb2d595a3ec10fa0fdf4 -R 156c1068608d73b6c04a73abb658869e +P 15ffd932fecc82a5791b2024f55d2ec80887e8eb7345de68d6f5cac4912cfbe8 +R 32ce5e0c8d0e91f62fdd054af5b27453 U drh -Z 48c7e5fa14a791868247c634dc1c52ad +Z 6f251c6f66875dddb0d77e3789c48938 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 121b0eb585..ffd32b4c8c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -15ffd932fecc82a5791b2024f55d2ec80887e8eb7345de68d6f5cac4912cfbe8 \ No newline at end of file +0d8cd6b5fb592f88f593ceaad9cdfa95dcfdf14169c3a56a5becbd66d62fd02b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0089584e62..9f41ca4343 100644 --- a/src/json.c +++ b/src/json.c @@ -1612,7 +1612,9 @@ json_parse_restart: pParse->iErr = j; return -1; } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + if( !pParse->oom ){ + pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + } pParse->iDepth--; return j+1; } @@ -3493,7 +3495,9 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ if( r<=0 ) return -1; j = (u32)r; } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + if( !pParse->oom ){ + pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + } break; } case JSONB_OBJECT: { @@ -3507,7 +3511,9 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ } j = (u32)r; } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + if( !pParse->oom ){ + pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + } break; } } From 5624b0b4cd997f749511fdb62cca63602f0cb079 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 12:40:04 +0000 Subject: [PATCH 028/347] Gather forward references into a single place for the JSON code. Allow JSONB arguments to json_array() and json_object() and similar. FossilOrigin-Name: c352201b8c299c330d9abbff6dbcbcbcf00ada53183d3cd91020ec772e066357 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 41 ++++++++++++++++++----------------------- test/json101.test | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/manifest b/manifest index 892d400ef4..ac267d347c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sproblems\sfollowing\sOOM\sin\sJSONB\sparsing. -D 2023-10-01T18:59:34.631 +C Gather\sforward\sreferences\sinto\sa\ssingle\splace\sfor\sthe\sJSON\scode.\s\sAllow\nJSONB\sarguments\sto\sjson_array()\sand\sjson_object()\sand\ssimilar. +D 2023-10-02T12:40:04.670 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 253ca59ae7f4be5fa7e0d630f9073f6d2f8b6dd89b47d80192f3b037271fe45e +F src/json.c e815a5e2f5bac5747e8290216eeb7de209aadc537a3f6a37b2880473bb2a8ed3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1303,7 +1303,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test dc9d5a2a5b1fd1b54dbd71c538b17933cc98d84b4c1f821ead754933663dca55 +F test/json101.test 728d90ecd6881a507e4081ed25ce0ac33ec501bbe897568ac47a71d9034561cc F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2123,8 +2123,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 15ffd932fecc82a5791b2024f55d2ec80887e8eb7345de68d6f5cac4912cfbe8 -R 32ce5e0c8d0e91f62fdd054af5b27453 +P 0d8cd6b5fb592f88f593ceaad9cdfa95dcfdf14169c3a56a5becbd66d62fd02b +R cb29cf34fe33404e23405b0f8bd1299f U drh -Z 6f251c6f66875dddb0d77e3789c48938 +Z e34235bdd2a6e0a4ec57afd79666bb6d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ffd32b4c8c..cd8719aa96 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0d8cd6b5fb592f88f593ceaad9cdfa95dcfdf14169c3a56a5becbd66d62fd02b \ No newline at end of file +c352201b8c299c330d9abbff6dbcbcbcf00ada53183d3cd91020ec772e066357 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9f41ca4343..8070c7bdf7 100644 --- a/src/json.c +++ b/src/json.c @@ -347,6 +347,17 @@ struct JsonParse { */ #define JSON_MAX_DEPTH 1000 +/************************************************************************** +** Forward references +**************************************************************************/ +static void jsonReturnStringAsBlob(JsonString*); +static void jsonRenderNodeAsBlob(JsonParse*,JsonNode*,JsonParse*); +static int jsonParseAddNode(JsonParse*,u32,u32,const char*); +static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); +static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); +static u32 jsonRenderBlob(JsonParse*,u32,JsonString*); + /************************************************************************** ** Utility routines for dealing with JsonString objects **************************************************************************/ @@ -714,7 +725,13 @@ static void jsonAppendSqlValue( break; } default: { - if( p->bErr==0 ){ + if( jsonFuncArgMightBeBinary(pValue) ){ + JsonParse px; + memset(&px, 0, sizeof(px)); + px.aBlob = (u8*)sqlite3_value_blob(pValue); + px.nBlob = sqlite3_value_bytes(pValue); + jsonRenderBlob(&px, 0, p); + }else if( p->bErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); p->bErr = 2; jsonStringReset(p); @@ -724,8 +741,6 @@ static void jsonAppendSqlValue( } } -/* Forward reference */ -static void jsonReturnStringAsBlob(JsonString*); /* Make the text in p (which is probably a generated JSON text string) ** the result of the SQL function. @@ -968,13 +983,6 @@ static void jsonRenderNodeAsText( } } -/* Forward reference */ -static void jsonRenderNodeAsBlob( - JsonParse *pParse, /* the complete parse of the JSON */ - JsonNode *pNode, /* The node to render */ - JsonParse *pOut /* Write the BLOB rendering of JSON here */ -); - /* ** Make the return value of an SQL function be the JSON encoded by pNode. ** @@ -1220,9 +1228,6 @@ static void jsonReturnNodeAsSql( } } -/* Forward reference */ -static int jsonParseAddNode(JsonParse*,u32,u32,const char*); - /* ** A macro to hint to the compiler that a function should not be ** inlined. @@ -1973,9 +1978,6 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ } } -/* Forward reference */ -static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); - /* ** Parse JSON (either pure RFC-8259 JSON text, or JSON-5 text, or a JSONB ** blob) into the JsonNode representation. @@ -2047,10 +2049,6 @@ static int jsonParseFindParents(JsonParse *pParse){ #define JSON_CACHE_ID (-429938) /* First cache entry */ #define JSON_CACHE_SZ 4 /* Max number of cache entries */ -/* Forward reference */ -static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); - - /* ** Obtain a complete parse of the JSON found in the pJson argument ** @@ -2202,9 +2200,6 @@ static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ } } -/* forward declaration */ -static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); - /* ** Search along zPath to find the node specified. Return a pointer ** to that node, or NULL if zPath is malformed or if there is no such diff --git a/test/json101.test b/test/json101.test index 4da1d132cc..5a7d770dde 100644 --- a/test/json101.test +++ b/test/json101.test @@ -36,6 +36,9 @@ do_execsql_test json101-1.2 { do_catchsql_test json101-1.3 { SELECT json_array(1,printf('%.1000c','x'),x'abcd',3); } {1 {JSON cannot hold BLOB values}} +do_catchsql_test json101-1.3b { + SELECT jsonb_array(1,printf('%.1000c','x'),x'abcd',3); +} {1 {JSON cannot hold BLOB values}} do_execsql_test json101-1.4 { SELECT json_array(-9223372036854775808,9223372036854775807,0,1,-1, 0.0, 1.0, -1.0, -1e99, +2e100, @@ -47,13 +50,43 @@ do_execsql_test json101-1.4 { 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', 99); } {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]} +do_execsql_test json101-1.4b { + SELECT json(jsonb_array(-9223372036854775808,9223372036854775807,0,1,-1, + 0.0, 1.0, -1.0, -1e99, +2e100, + 'one','two','three', + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, NULL, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 99)); +} {[-9223372036854775808,9223372036854775807,0,1,-1,0.0,1.0,-1.0,-1.0e+99,2.0e+100,"one","two","three",4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,21,22,23,24,25,26,27,28,29,30,31,"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ",99]} do_execsql_test json101-2.1 { SELECT json_object('a',1,'b',2.5,'c',null,'d','String Test'); } {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}} +do_execsql_test json101-2.1b { + SELECT json(jsonb_object('a',1,'b',2.5,'c',null,'d','String Test')); +} {{{"a":1,"b":2.5,"c":null,"d":"String Test"}}} do_catchsql_test json101-2.2 { SELECT json_object('a',printf('%.1000c','x'),2,2.5); } {1 {json_object() labels must be TEXT}} +do_catchsql_test json101-2.2b { + SELECT jsonb_object('a',printf('%.1000c','x'),2,2.5); +} {1 {json_object() labels must be TEXT}} +do_execsql_test json101-2.2.2 { + SELECT json_object('a',json_array('xyx',77,4.5),'x',2.5); +} {{{"a":["xyx",77,4.5],"x":2.5}}} +do_execsql_test json101-2.2.2b { + SELECT json(jsonb_object('a',json_array('xyx',77,4.5),'x',2.5)); +} {{{"a":["xyx",77,4.5],"x":2.5}}} +do_execsql_test json101-2.2.3 { + SELECT json_object('a',jsonb_array('xyx',77,4.5),'x',2.5); +} {{{"a":["xyx",77,4.5],"x":2.5}}} +do_execsql_test json101-2.2.3b { + SELECT json(jsonb_object('a',jsonb_array('xyx',77,4.5),'x',2.5)); +} {{{"a":["xyx",77,4.5],"x":2.5}}} +exit do_catchsql_test json101-2.3 { SELECT json_object('a',1,'b'); } {1 {json_object() requires an even number of arguments}} From 8eeafb754459486a530ec3264cde5587793522d7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 13:20:43 +0000 Subject: [PATCH 029/347] Improvements to error handling for BLOB inputs on JSON. FossilOrigin-Name: 14f20ecbfab44934e86f1ac7a3f745b989aa8190c6df119ff5aa8100fa248d93 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 19 +++++++------------ test/json101.test | 6 ++++-- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index ac267d347c..b0b1b78488 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Gather\sforward\sreferences\sinto\sa\ssingle\splace\sfor\sthe\sJSON\scode.\s\sAllow\nJSONB\sarguments\sto\sjson_array()\sand\sjson_object()\sand\ssimilar. -D 2023-10-02T12:40:04.670 +C Improvements\sto\serror\shandling\sfor\sBLOB\sinputs\son\sJSON. +D 2023-10-02T13:20:43.907 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e815a5e2f5bac5747e8290216eeb7de209aadc537a3f6a37b2880473bb2a8ed3 +F src/json.c 057ce1f7bcf30abb8e0418e8b84892e6cda66e0fcac45465574ea3f2e37d6f85 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1303,7 +1303,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 728d90ecd6881a507e4081ed25ce0ac33ec501bbe897568ac47a71d9034561cc +F test/json101.test da33cc6d114e82228fc56b6b2700b2fc2c352636aeca59cb157fc6237df688af F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2123,8 +2123,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 0d8cd6b5fb592f88f593ceaad9cdfa95dcfdf14169c3a56a5becbd66d62fd02b -R cb29cf34fe33404e23405b0f8bd1299f +P c352201b8c299c330d9abbff6dbcbcbcf00ada53183d3cd91020ec772e066357 +R f0da6e230789d1a4973c52d698202614 U drh -Z e34235bdd2a6e0a4ec57afd79666bb6d +Z 0e4cae102419e8824f19fd85f33d7b9c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cd8719aa96..348760d2b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c352201b8c299c330d9abbff6dbcbcbcf00ada53183d3cd91020ec772e066357 \ No newline at end of file +14f20ecbfab44934e86f1ac7a3f745b989aa8190c6df119ff5aa8100fa248d93 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8070c7bdf7..1c7693df7a 100644 --- a/src/json.c +++ b/src/json.c @@ -3207,7 +3207,8 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ n = 5; } if( i+sz+n>pParse->nBlob ){ - sz = pParse->nBlob - (i+n); + sz = 0; + n = 0; } *pSz = sz; return n; @@ -3226,9 +3227,10 @@ static u32 jsonRenderBlob( ){ u32 sz, n, j, iEnd; - if( i>=pParse->nBlob ){ + n = jsonbPayloadSize(pParse, i, &sz); + if( n==0 ){ pOut->bErr = 1; - return 0; + return pParse->nBlob+1; } switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { @@ -3245,14 +3247,12 @@ static u32 jsonRenderBlob( } case JSONB_INT: case JSONB_FLOAT: { - n = jsonbPayloadSize(pParse, i, &sz); jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ int k; sqlite3_uint64 u = 0; - n = jsonbPayloadSize(pParse, i, &sz); const char *zIn = (const char*)&pParse->aBlob[i+n]; if( zIn[0]=='-' ){ zIn++; @@ -3267,7 +3267,6 @@ static u32 jsonRenderBlob( } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ int k; - n = jsonbPayloadSize(pParse, i, &sz); const char *zIn = (const char*)&pParse->aBlob[i+n]; if( zIn[0]=='-' ){ zIn++; @@ -3294,7 +3293,6 @@ static u32 jsonRenderBlob( } case JSONB_TEXT: case JSONB_TEXTJ: { - n = jsonbPayloadSize(pParse, i, &sz); jsonAppendChar(pOut, '"'); jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); jsonAppendChar(pOut, '"'); @@ -3303,7 +3301,6 @@ static u32 jsonRenderBlob( case JSONB_TEXT5: { const char *zIn; u32 k; - n = jsonbPayloadSize(pParse, i, &sz); zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz>0 ){ @@ -3358,7 +3355,6 @@ static u32 jsonRenderBlob( } case JSONB_ARRAY: { jsonAppendChar(pOut, '['); - n = jsonbPayloadSize(pParse, i, &sz); j = i+n; iEnd = j+sz; while( j0 && iaBlob[i] & 0x0f ){ case JSONB_NULL: { sqlite3_result_null(pCtx); diff --git a/test/json101.test b/test/json101.test index 5a7d770dde..5bf6635601 100644 --- a/test/json101.test +++ b/test/json101.test @@ -86,13 +86,15 @@ do_execsql_test json101-2.2.3 { do_execsql_test json101-2.2.3b { SELECT json(jsonb_object('a',jsonb_array('xyx',77,4.5),'x',2.5)); } {{{"a":["xyx",77,4.5],"x":2.5}}} -exit do_catchsql_test json101-2.3 { SELECT json_object('a',1,'b'); } {1 {json_object() requires an even number of arguments}} do_catchsql_test json101-2.4 { SELECT json_object('a',printf('%.1000c','x'),'b',x'abcd'); } {1 {JSON cannot hold BLOB values}} +do_execsql_test json101-2.5 { + SELECT json_object('a',printf('%.10c','x'),'b',jsonb_array(1,2,3)); +} {{{"a":"xxxxxxxxxx","b":[1,2,3]}}} do_execsql_test json101-3.1 { SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]'); @@ -431,7 +433,7 @@ do_execsql_test json101-9.4 { SELECT json_quote(null); } {"null"} do_catchsql_test json101-9.5 { - SELECT json_quote(x'30313233'); + SELECT json_quote(x'3031323334'); } {1 {JSON cannot hold BLOB values}} do_catchsql_test json101-9.6 { SELECT json_quote(123,456) From c97407fdbad35baaefcea20738db81a02c853c6d Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 13:35:06 +0000 Subject: [PATCH 030/347] Allow json_replace() to accept JSONB arguments as the new value. FossilOrigin-Name: 95eb7b37fab29931924311f541d52173ef77a448efc8771b1a1783ccd786d23d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 15 +++++++++++---- test/json101.test | 6 ++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index b0b1b78488..d67b7d412c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\serror\shandling\sfor\sBLOB\sinputs\son\sJSON. -D 2023-10-02T13:20:43.907 +C Allow\sjson_replace()\sto\saccept\sJSONB\sarguments\sas\sthe\snew\svalue. +D 2023-10-02T13:35:06.348 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 057ce1f7bcf30abb8e0418e8b84892e6cda66e0fcac45465574ea3f2e37d6f85 +F src/json.c 643f1553f9a704ea558937b16831d26f9db90dd97e05d77cfef984f84af37564 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1303,7 +1303,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test da33cc6d114e82228fc56b6b2700b2fc2c352636aeca59cb157fc6237df688af +F test/json101.test 54b27bd681ce3fe33629b58fecdba2502d0ce8d445f69a155dbd9861a868ea27 F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2123,8 +2123,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 c352201b8c299c330d9abbff6dbcbcbcf00ada53183d3cd91020ec772e066357 -R f0da6e230789d1a4973c52d698202614 +P 14f20ecbfab44934e86f1ac7a3f745b989aa8190c6df119ff5aa8100fa248d93 +R cc5fa69b041d057bb643a7cdf2281f1c U drh -Z 0e4cae102419e8824f19fd85f33d7b9c +Z 2b0bdbc85e19cadd323c6d81b0285ad4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 348760d2b2..7c8b3403e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14f20ecbfab44934e86f1ac7a3f745b989aa8190c6df119ff5aa8100fa248d93 \ No newline at end of file +95eb7b37fab29931924311f541d52173ef77a448efc8771b1a1783ccd786d23d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1c7693df7a..c7bf260c9c 100644 --- a/src/json.c +++ b/src/json.c @@ -4620,7 +4620,10 @@ static void jsonReplaceNode( k = jsonParseAddNode(p, JSON_STRING, n, zCopy); assert( k>0 || p->oom ); if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW; - }else{ + break; + } + replace_with_json: + { JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1); if( pPatch==0 ){ p->oom = 1; @@ -4637,9 +4640,13 @@ static void jsonReplaceNode( break; } default: { - jsonParseAddNode(p, JSON_NULL, 0, 0); - sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1); - p->nErr++; + if( jsonFuncArgMightBeBinary(pValue) ){ + goto replace_with_json; + }else{ + jsonParseAddNode(p, JSON_NULL, 0, 0); + sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1); + p->nErr++; + } break; } } diff --git a/test/json101.test b/test/json101.test index 5bf6635601..a6fec19507 100644 --- a/test/json101.test +++ b/test/json101.test @@ -99,9 +99,15 @@ do_execsql_test json101-2.5 { do_execsql_test json101-3.1 { SELECT json_replace('{"a":1,"b":2}','$.a','[3,4,5]'); } {{{"a":"[3,4,5]","b":2}}} +do_execsql_test json101-3.1b { + SELECT json(jsonb_replace('{"a":1,"b":2}','$.a','[3,4,5]')); +} {{{"a":"[3,4,5]","b":2}}} do_execsql_test json101-3.2 { SELECT json_replace('{"a":1,"b":2}','$.a',json('[3,4,5]')); } {{{"a":[3,4,5],"b":2}}} +do_execsql_test json101-3.2b { + SELECT json_replace('{"a":1,"b":2}','$.a',jsonb('[3,4,5]')); +} {{{"a":[3,4,5],"b":2}}} do_execsql_test json101-3.3 { SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); } {text} From dee29e8c7acbe30112ed875496e86f77cc3bdcae Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 14:51:28 +0000 Subject: [PATCH 031/347] New test cases for JSONB. FossilOrigin-Name: d624c31e5c49e1ce63b4b72caa42a61c5167866f47d842fbcfe4e826fd079d7c --- manifest | 14 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- test/json101.test | 56 ++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index d67b7d412c..a167aca684 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sjson_replace()\sto\saccept\sJSONB\sarguments\sas\sthe\snew\svalue. -D 2023-10-02T13:35:06.348 +C New\stest\scases\sfor\sJSONB. +D 2023-10-02T14:51:28.119 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 643f1553f9a704ea558937b16831d26f9db90dd97e05d77cfef984f84af37564 +F src/json.c a75a4bfff4d4f435c4e22d69e0b54c0bccfa978725e4a89c052764f31c7f3bc9 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1303,7 +1303,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 54b27bd681ce3fe33629b58fecdba2502d0ce8d445f69a155dbd9861a868ea27 +F test/json101.test e8ccd09f965c594f38ef486ddf7913f0fcac97be20a785a41c3d7cd4289e82de F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2123,8 +2123,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 14f20ecbfab44934e86f1ac7a3f745b989aa8190c6df119ff5aa8100fa248d93 -R cc5fa69b041d057bb643a7cdf2281f1c +P 95eb7b37fab29931924311f541d52173ef77a448efc8771b1a1783ccd786d23d +R 2841b59f4f7afcfcc5b77dd83ccca9c0 U drh -Z 2b0bdbc85e19cadd323c6d81b0285ad4 +Z d9c4ddddb83fc3b47a718cf56777e023 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7c8b3403e2..3e4d4770c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -95eb7b37fab29931924311f541d52173ef77a448efc8771b1a1783ccd786d23d \ No newline at end of file +d624c31e5c49e1ce63b4b72caa42a61c5167866f47d842fbcfe4e826fd079d7c \ No newline at end of file diff --git a/src/json.c b/src/json.c index c7bf260c9c..46c65befd7 100644 --- a/src/json.c +++ b/src/json.c @@ -3890,7 +3890,7 @@ static void jsonReturnFromBlob( c = z[++iIn]; if( c=='u' ){ u32 v = jsonHexToInt4(z+iIn+1); - i += 4; + iIn += 4; if( v==0 ) break; if( v<=0x7f ){ zOut[iOut++] = (char)v; diff --git a/test/json101.test b/test/json101.test index a6fec19507..d5ec36d8b5 100644 --- a/test/json101.test +++ b/test/json101.test @@ -111,13 +111,22 @@ do_execsql_test json101-3.2b { do_execsql_test json101-3.3 { SELECT json_type(json_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); } {text} +do_execsql_test json101-3.3b { + SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b','{"x":3,"y":4}'),'$.b'); +} {text} do_execsql_test json101-3.4 { SELECT json_type(json_set('{"a":1,"b":2}','$.b',json('{"x":3,"y":4}')),'$.b'); } {object} +do_execsql_test json101-3.4b { + SELECT json_type(jsonb_set('{"a":1,"b":2}','$.b',jsonb('{"x":3,"y":4}')),'$.b'); +} {object} ifcapable vtab { -do_execsql_test json101-3.5 { - SELECT fullkey, atom, '|' FROM json_tree(json_set('{}','$.x',123,'$.x',456)); -} {{$} {} | {$.x} 456 |} + do_execsql_test json101-3.5 { + SELECT fullkey, atom, '|' FROM json_tree(json_set('{}','$.x',123,'$.x',456)); + } {{$} {} | {$.x} 456 |} + do_execsql_test json101-3.5b { + SELECT fullkey, atom, '|' FROM json_tree(jsonb_set('{}','$.x',123,'$.x',456)); + } {{$} {} | {$.x} 456 |} } # Per rfc7159, any JSON value is allowed at the top level, and whitespace @@ -169,6 +178,13 @@ do_execsql_test json101-4.10 { WHERE json_extract(x,'$')<>x AND json_type(x) IN ('object','array'); } {4} +do_execsql_test json101-4.10b { + CREATE TABLE j1b AS SELECT jsonb(x) AS "x" FROM j1; + SELECT count(*) FROM j1b WHERE json_type(x) IN ('object','array'); + SELECT json(x) FROM j1b + WHERE json_extract(x,'$')<>json(x) + AND json_type(x) IN ('object','array'); +} {4} do_execsql_test json101-5.1 { CREATE TABLE j2(id INTEGER PRIMARY KEY, json, src); @@ -296,11 +312,17 @@ do_execsql_test json101-5.1 { } ]','https://adobe.github.io/Spry/samples/data_region/JSONDataSetSample.html'); SELECT count(*) FROM j2; -} {3} + CREATE TABLE j2b(id INTEGER PRIMARY KEY, json, src); + INSERT INTO J2b(id,json,src) SELECT id, jsonb(json), src FROM j2; + SELECT count(*) FROM j2b; +} {3 3} do_execsql_test json101-5.2 { SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id; } {1 1 object | 2 1 object | 3 1 array |} +do_execsql_test json101-5.2b { + SELECT id, json_valid(json), json_type(json), '|' FROM j2b ORDER BY id; +} {1 1 object | 2 1 object | 3 1 array |} ifcapable !vtab { finish_test @@ -315,6 +337,12 @@ do_execsql_test json101-5.3 { WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']' ELSE '.'||key END); } {} +do_execsql_test json101-5.3b { + SELECT j2b.rowid, jx.rowid, fullkey, path, key + FROM j2b, json_tree(j2b.json) AS jx + WHERE fullkey!=(path || CASE WHEN typeof(key)=='integer' THEN '['||key||']' + ELSE '.'||key END); +} {} do_execsql_test json101-5.4 { SELECT j2.rowid, jx.rowid, fullkey, path, key FROM j2, json_each(j2.json) AS jx @@ -409,6 +437,13 @@ do_execsql_test json101-8.1 { UPDATE t8 SET b=json_array(a); SELECT b FROM t8; } {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}} +do_execsql_test json101-8.1b { + DROP TABLE IF EXISTS t8; + CREATE TABLE t8(a,b); + INSERT INTO t8(a) VALUES('abc' || char(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35) || 'xyz'); + UPDATE t8 SET b=jsonb_array(a); + SELECT json(b) FROM t8; +} {{["abc\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#xyz"]}} do_execsql_test json101-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} @@ -807,14 +842,23 @@ do_execsql_test json101-12.100 { } }'); } {} + do_execsql_test json101-12.110 { SELECT json_remove(x, '$.settings.layer2."dis.legomenon".forceDisplay') FROM t12; } {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}} +do_execsql_test json101-12.110b { + SELECT json_remove(jsonb(x), '$.settings.layer2."dis.legomenon".forceDisplay') + FROM t12; +} {{{"settings":{"layer2":{"hapax.legomenon":{"forceDisplay":true,"transliterate":true,"add.footnote":true,"summary.report":true},"dis.legomenon":{"transliterate":false,"add.footnote":false,"summary.report":true},"tris.legomenon":{"forceDisplay":true,"transliterate":false,"add.footnote":false,"summary.report":false}}}}}} do_execsql_test json101-12.120 { SELECT json_extract(x, '$.settings.layer2."tris.legomenon"."summary.report"') FROM t12; } {0} +do_execsql_test json101-12.120b { + SELECT json_extract(jsonb(x), '$.settings.layer2."tris.legomenon"."summary.report"') + FROM t12; +} {0} # 2018-01-26 # ticket https://www.sqlite.org/src/tktview/80177f0c226ff54f6ddd41 @@ -1054,8 +1098,4 @@ do_execsql_test json101-21.27 { SELECT json_group_object(x,y) FROM c; } {{{"a":1,"b":2.0,"c":null,:"three","e":"four"}}} - - - - finish_test From e5c384e9a52816ca989953154617fa66ee4a75a2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 20:16:06 +0000 Subject: [PATCH 032/347] Fix jsonb_insert() so that it does not behave like jsonb_set(). New test cases added. FossilOrigin-Name: 54197149b811d30b6c4487eedf5692b164ed0f90cfcc541aa3157094f5f17f6a --- manifest | 14 ++--- manifest.uuid | 2 +- src/json.c | 3 +- test/json102.test | 133 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 141 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index a167aca684..02e0ac823a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sfor\sJSONB. -D 2023-10-02T14:51:28.119 +C Fix\sjsonb_insert()\sso\sthat\sit\sdoes\snot\sbehave\slike\sjsonb_set().\nNew\stest\scases\sadded. +D 2023-10-02T20:16:06.301 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a75a4bfff4d4f435c4e22d69e0b54c0bccfa978725e4a89c052764f31c7f3bc9 +F src/json.c a2e70610cc14d0f97feb17bf0000c0e034f1cb5ac1b318bcf3ccf7a014f01a80 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1304,7 +1304,7 @@ F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b7244 F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x F test/json101.test e8ccd09f965c594f38ef486ddf7913f0fcac97be20a785a41c3d7cd4289e82de -F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b +F test/json102.test d89476e2fb515c8c2660b1dcdd9772ac8f17bb538d12e7b7f44dfa46fc899aa8 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d @@ -2123,8 +2123,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 95eb7b37fab29931924311f541d52173ef77a448efc8771b1a1783ccd786d23d -R 2841b59f4f7afcfcc5b77dd83ccca9c0 +P d624c31e5c49e1ce63b4b72caa42a61c5167866f47d842fbcfe4e826fd079d7c +R f84ac9b23e77db6a3aeb1eb8d5ee202d U drh -Z d9c4ddddb83fc3b47a718cf56777e023 +Z 7bfe4ac791f85f5c6a70ea3c48417630 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3e4d4770c2..bb4d24251b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d624c31e5c49e1ce63b4b72caa42a61c5167866f47d842fbcfe4e826fd079d7c \ No newline at end of file +54197149b811d30b6c4487eedf5692b164ed0f90cfcc541aa3157094f5f17f6a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 46c65befd7..1e40668f9f 100644 --- a/src/json.c +++ b/src/json.c @@ -4712,7 +4712,8 @@ static void jsonSetFunc( const char *zPath; u32 i; int bApnd; - int bIsSet = sqlite3_user_data(ctx)!=0; + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + int bIsSet = (flags&JSON_ISSET)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { diff --git a/test/json102.test b/test/json102.test index ce901efeb1..8ada49d7ae 100644 --- a/test/json102.test +++ b/test/json102.test @@ -21,75 +21,204 @@ source $testdir/tester.tcl do_execsql_test json102-100 { SELECT json_object('ex','[52,3.14159]'); } {{{"ex":"[52,3.14159]"}}} +do_execsql_test json102-100b { + SELECT json(jsonb_object('ex','[52,3.14159]')); +} {{{"ex":"[52,3.14159]"}}} do_execsql_test json102-110 { SELECT json_object('ex',json('[52,3.14159]')); } {{{"ex":[52,3.14159]}}} +do_execsql_test json102-110-2 { + SELECT json(jsonb_object('ex',json('[52,3.14159]'))); +} {{{"ex":[52,3.14159]}}} +do_execsql_test json102-110-3 { + SELECT json_object('ex',jsonb('[52,3.14159]')); +} {{{"ex":[52,3.14159]}}} +do_execsql_test json102-110-3 { + SELECT json(jsonb_object('ex',jsonb('[52,3.14159]'))); +} {{{"ex":[52,3.14159]}}} do_execsql_test json102-120 { SELECT json_object('ex',json_array(52,3.14159)); } {{{"ex":[52,3.14159]}}} +do_execsql_test json102-120-2 { + SELECT json(jsonb_object('ex',json_array(52,3.14159))); +} {{{"ex":[52,3.14159]}}} +do_execsql_test json102-120-3 { + SELECT json_object('ex',jsonb_array(52,3.14159)); +} {{{"ex":[52,3.14159]}}} +do_execsql_test json102-120-4 { + SELECT json(jsonb_object('ex',jsonb_array(52,3.14159))); +} {{{"ex":[52,3.14159]}}} do_execsql_test json102-130 { SELECT json(' { "this" : "is", "a": [ "test" ] } '); } {{{"this":"is","a":["test"]}}} +do_execsql_test json102-130b { + SELECT json(jsonb(' { "this" : "is", "a": [ "test" ] } ')); +} {{{"this":"is","a":["test"]}}} do_execsql_test json102-140 { SELECT json_array(1,2,'3',4); } {{[1,2,"3",4]}} +do_execsql_test json102-140b { + SELECT json(jsonb_array(1,2,'3',4)); +} {{[1,2,"3",4]}} do_execsql_test json102-150 { SELECT json_array('[1,2]'); } {{["[1,2]"]}} +do_execsql_test json102-150b { + SELECT json(jsonb_array('[1,2]')); +} {{["[1,2]"]}} do_execsql_test json102-160 { SELECT json_array(json_array(1,2)); } {{[[1,2]]}} +do_execsql_test json102-160-2 { + SELECT json_array(jsonb_array(1,2)); +} {{[[1,2]]}} +do_execsql_test json102-160-3 { + SELECT json(jsonb_array(json_array(1,2))); +} {{[[1,2]]}} +do_execsql_test json102-160-4 { + SELECT json(jsonb_array(jsonb_array(1,2))); +} {{[[1,2]]}} do_execsql_test json102-170 { SELECT json_array(1,null,'3','[4,5]','{"six":7.7}'); } {{[1,null,"3","[4,5]","{\"six\":7.7}"]}} +do_execsql_test json102-170b { + SELECT json(jsonb_array(1,null,'3','[4,5]','{"six":7.7}')); +} {{[1,null,"3","[4,5]","{\"six\":7.7}"]}} do_execsql_test json102-180 { SELECT json_array(1,null,'3',json('[4,5]'),json('{"six":7.7}')); } {{[1,null,"3",[4,5],{"six":7.7}]}} +do_execsql_test json102-180-2 { + SELECT json_array(1,null,'3',jsonb('[4,5]'),json('{"six":7.7}')); +} {{[1,null,"3",[4,5],{"six":7.7}]}} +do_execsql_test json102-180-3 { + SELECT json(jsonb_array(1,null,'3',json('[4,5]'),json('{"six":7.7}'))); +} {{[1,null,"3",[4,5],{"six":7.7}]}} +do_execsql_test json102-180-4 { + SELECT json(jsonb_array(1,null,'3',jsonb('[4,5]'),jsonb('{"six":7.7}'))); +} {{[1,null,"3",[4,5],{"six":7.7}]}} do_execsql_test json102-190 { SELECT json_array_length('[1,2,3,4]'); } {{4}} +do_execsql_test json102-190b { + SELECT json_array_length(jsonb('[1,2,3,4]')); +} {{4}} do_execsql_test json102-191 { SELECT json_array_length( json_remove('[1,2,3,4]','$[2]') ); } {{3}} +do_execsql_test json102-191b { + SELECT json_array_length( jsonb_remove('[1,2,3,4]','$[2]') ); +} {{3}} do_execsql_test json102-200 { SELECT json_array_length('[1,2,3,4]', '$'); } {{4}} +do_execsql_test json102-200b { + SELECT json_array_length(jsonb('[1,2,3,4]'), '$'); +} {{4}} do_execsql_test json102-210 { SELECT json_array_length('[1,2,3,4]', '$[2]'); } {{0}} +do_execsql_test json102-210b { + SELECT json_array_length(jsonb('[1,2,3,4]'), '$[2]'); +} {{0}} do_execsql_test json102-220 { SELECT json_array_length('{"one":[1,2,3]}'); } {{0}} -do_execsql_test json102-230 { - SELECT json_array_length('{"one":[1,2,3]}', '$.one'); +do_execsql_test json102-220 { + SELECT json_array_length('{"one":[1,2,3]}'); +} {{0}} +do_execsql_test json102-230b { + SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.one'); } {{3}} do_execsql_test json102-240 { SELECT json_array_length('{"one":[1,2,3]}', '$.two'); } {{}} +do_execsql_test json102-240b { + SELECT json_array_length(jsonb('{"one":[1,2,3]}'), '$.two'); +} {{}} do_execsql_test json102-250 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$'); } {{{"a":2,"c":[4,5,{"f":7}]}}} +do_execsql_test json102-250-2 { + SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$'); +} {{{"a":2,"c":[4,5,{"f":7}]}}} +do_execsql_test json102-250-3 { + SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$')); +} {{{"a":2,"c":[4,5,{"f":7}]}}} +do_execsql_test json102-250-4 { + SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$')); +} {{{"a":2,"c":[4,5,{"f":7}]}}} do_execsql_test json102-260 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c'); } {{[4,5,{"f":7}]}} +do_execsql_test json102-260-2 { + SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c'); +} {{[4,5,{"f":7}]}} +do_execsql_test json102-260-3 { + SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c')); +} {{[4,5,{"f":7}]}} +do_execsql_test json102-260-4 { + SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c')); +} {{[4,5,{"f":7}]}} do_execsql_test json102-270 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]'); } {{{"f":7}}} +do_execsql_test json102-270-2 { + SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]'); +} {{{"f":7}}} +do_execsql_test json102-270-3 { + SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.c[2]')); +} {{{"f":7}}} +do_execsql_test json102-270-4 { + SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2]')); +} {{{"f":7}}} do_execsql_test json102-280 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f'); } {{7}} +do_execsql_test json102-280b { + SELECT jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.c[2].f'); +} {{7}} do_execsql_test json102-290 { SELECT json_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a'); } {{[[4,5],2]}} +do_execsql_test json102-290-2 { + SELECT json_extract(jsonb('{"a":2,"c":[4,5],"f":7}'),'$.c','$.a'); +} {{[[4,5],2]}} +do_execsql_test json102-290-3 { + SELECT json(jsonb_extract('{"a":2,"c":[4,5],"f":7}','$.c','$.a')); +} {{[[4,5],2]}} +do_execsql_test json102-290-4 { + SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5],"f":7}'),'$.c','$.a')); +} {{[[4,5],2]}} do_execsql_test json102-300 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x'); } {{}} +do_execsql_test json102-300b { + SELECT jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x'); +} {{}} do_execsql_test json102-310 { SELECT json_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a'); } {{[null,2]}} +do_execsql_test json102-310-2 { + SELECT json_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a'); +} {{[null,2]}} +do_execsql_test json102-310-3 { + SELECT json(jsonb_extract(jsonb('{"a":2,"c":[4,5,{"f":7}]}'), '$.x', '$.a')); +} {{[null,2]}} +do_execsql_test json102-310-43 { + SELECT json(jsonb_extract('{"a":2,"c":[4,5,{"f":7}]}', '$.x', '$.a')); +} {{[null,2]}} do_execsql_test json102-320 { SELECT json_insert('{"a":2,"c":4}', '$.a', 99); } {{{"a":2,"c":4}}} +do_execsql_test json102-320-2 { + SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.a', 99); +} {{{"a":2,"c":4}}} +do_execsql_test json102-320-3 { + SELECT json(jsonb_insert('{"a":2,"c":4}', '$.a', 99)); +} {{{"a":2,"c":4}}} +do_execsql_test json102-320-4 { + SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.a', 99)); +} {{{"a":2,"c":4}}} do_execsql_test json102-330 { SELECT json_insert('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4,"e":99}}} From 313336f90bc3f6e180529260096610a0aff8cc23 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 2 Oct 2023 23:56:46 +0000 Subject: [PATCH 033/347] New test cases for JSONB. FossilOrigin-Name: 6d4aeff5751722c83ebb0e1f21072b72be418c64dcf28ee032d3a548e1b3a951 --- manifest | 12 +-- manifest.uuid | 2 +- test/json102.test | 219 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 225 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 02e0ac823a..c3140c8eee 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sjsonb_insert()\sso\sthat\sit\sdoes\snot\sbehave\slike\sjsonb_set().\nNew\stest\scases\sadded. -D 2023-10-02T20:16:06.301 +C New\stest\scases\sfor\sJSONB. +D 2023-10-02T23:56:46.985 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1304,7 +1304,7 @@ F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b7244 F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x F test/json101.test e8ccd09f965c594f38ef486ddf7913f0fcac97be20a785a41c3d7cd4289e82de -F test/json102.test d89476e2fb515c8c2660b1dcdd9772ac8f17bb538d12e7b7f44dfa46fc899aa8 +F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d @@ -2123,8 +2123,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 d624c31e5c49e1ce63b4b72caa42a61c5167866f47d842fbcfe4e826fd079d7c -R f84ac9b23e77db6a3aeb1eb8d5ee202d +P 54197149b811d30b6c4487eedf5692b164ed0f90cfcc541aa3157094f5f17f6a +R f7716cc560ac345b0556f46a68783d4e U drh -Z 7bfe4ac791f85f5c6a70ea3c48417630 +Z aa79deaa2b4777d46e48ea5272670f4a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bb4d24251b..cc209c225a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -54197149b811d30b6c4487eedf5692b164ed0f90cfcc541aa3157094f5f17f6a \ No newline at end of file +6d4aeff5751722c83ebb0e1f21072b72be418c64dcf28ee032d3a548e1b3a951 \ No newline at end of file diff --git a/test/json102.test b/test/json102.test index 8ada49d7ae..15a54b47c4 100644 --- a/test/json102.test +++ b/test/json102.test @@ -222,87 +222,291 @@ do_execsql_test json102-320-4 { do_execsql_test json102-330 { SELECT json_insert('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-330-2 { + SELECT json_insert(jsonb('{"a":2,"c":4}'), '$.e', 99); +} {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-330-3 { + SELECT json(jsonb_insert('{"a":2,"c":4}', '$.e', 99)); +} {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-330-4 { + SELECT json(jsonb_insert(jsonb('{"a":2,"c":4}'), '$.e', 99)); +} {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-340 { SELECT json_replace('{"a":2,"c":4}', '$.a', 99); } {{{"a":99,"c":4}}} +do_execsql_test json102-340-2 { + SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.a', 99); +} {{{"a":99,"c":4}}} +do_execsql_test json102-340-3 { + SELECT json(jsonb_replace('{"a":2,"c":4}', '$.a', 99)); +} {{{"a":99,"c":4}}} +do_execsql_test json102-340-4 { + SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.a', 99)); +} {{{"a":99,"c":4}}} do_execsql_test json102-350 { SELECT json_replace('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4}}} +do_execsql_test json102-350-2 { + SELECT json_replace(jsonb('{"a":2,"c":4}'), '$.e', 99); +} {{{"a":2,"c":4}}} +do_execsql_test json102-350-3 { + SELECT json(jsonb_replace('{"a":2,"c":4}', '$.e', 99)); +} {{{"a":2,"c":4}}} +do_execsql_test json102-350-4 { + SELECT json(jsonb_replace(jsonb('{"a":2,"c":4}'), '$.e', 99)); +} {{{"a":2,"c":4}}} do_execsql_test json102-360 { SELECT json_set('{"a":2,"c":4}', '$.a', 99); } {{{"a":99,"c":4}}} +do_execsql_test json102-360-2 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.a', 99); +} {{{"a":99,"c":4}}} +do_execsql_test json102-360-3 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.a', 99)); +} {{{"a":99,"c":4}}} +do_execsql_test json102-360-4 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.a', 99)); +} {{{"a":99,"c":4}}} do_execsql_test json102-370 { SELECT json_set('{"a":2,"c":4}', '$.e', 99); } {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-370-2 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.e', 99); +} {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-370-3 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.e', 99)); +} {{{"a":2,"c":4,"e":99}}} +do_execsql_test json102-370-4 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.e', 99)); +} {{{"a":2,"c":4,"e":99}}} do_execsql_test json102-380 { SELECT json_set('{"a":2,"c":4}', '$.c', '[97,96]'); } {{{"a":2,"c":"[97,96]"}}} +do_execsql_test json102-380-2 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]'); +} {{{"a":2,"c":"[97,96]"}}} +do_execsql_test json102-380-3 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', '[97,96]')); +} {{{"a":2,"c":"[97,96]"}}} +do_execsql_test json102-380-4 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', '[97,96]')); +} {{{"a":2,"c":"[97,96]"}}} do_execsql_test json102-390 { SELECT json_set('{"a":2,"c":4}', '$.c', json('[97,96]')); } {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-2 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]')); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-3 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json('[97,96]'))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-4 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json('[97,96]'))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-5 { + SELECT json_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]')); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-6 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]')); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-7 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb('[97,96]'))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-390-8 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb('[97,96]'))); +} {{{"a":2,"c":[97,96]}}} do_execsql_test json102-400 { SELECT json_set('{"a":2,"c":4}', '$.c', json_array(97,96)); } {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-2 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96)); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-3 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', json_array(97,96))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-4 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', json_array(97,96))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-5 { + SELECT json_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96)); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-6 { + SELECT json_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96)); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-7 { + SELECT json(jsonb_set('{"a":2,"c":4}', '$.c', jsonb_array(97,96))); +} {{{"a":2,"c":[97,96]}}} +do_execsql_test json102-400-8 { + SELECT json(jsonb_set(jsonb('{"a":2,"c":4}'), '$.c', jsonb_array(97,96))); +} {{{"a":2,"c":[97,96]}}} do_execsql_test json102-410 { SELECT json_object('a',2,'c',4); } {{{"a":2,"c":4}}} +do_execsql_test json102-410b { + SELECT json(jsonb_object('a',2,'c',4)); +} {{{"a":2,"c":4}}} do_execsql_test json102-420 { SELECT json_object('a',2,'c','{e:5}'); } {{{"a":2,"c":"{e:5}"}}} +do_execsql_test json102-420b { + SELECT json(jsonb_object('a',2,'c','{e:5}')); +} {{{"a":2,"c":"{e:5}"}}} do_execsql_test json102-430 { SELECT json_object('a',2,'c',json_object('e',5)); } {{{"a":2,"c":{"e":5}}}} +do_execsql_test json102-430-2 { + SELECT json(jsonb_object('a',2,'c',json_object('e',5))); +} {{{"a":2,"c":{"e":5}}}} +do_execsql_test json102-430-3 { + SELECT json_object('a',2,'c',jsonb_object('e',5)); +} {{{"a":2,"c":{"e":5}}}} +do_execsql_test json102-430-4 { + SELECT json(jsonb_object('a',2,'c',jsonb_object('e',5))); +} {{{"a":2,"c":{"e":5}}}} do_execsql_test json102-440 { SELECT json_remove('[0,1,2,3,4]','$[2]'); } {{[0,1,3,4]}} +do_execsql_test json102-440-2 { + SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]'); +} {{[0,1,3,4]}} +do_execsql_test json102-440-3 { + SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]')); +} {{[0,1,3,4]}} +do_execsql_test json102-440-4 { + SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]')); +} {{[0,1,3,4]}} do_execsql_test json102-450 { SELECT json_remove('[0,1,2,3,4]','$[2]','$[0]'); } {{[1,3,4]}} +do_execsql_test json102-450-2 { + SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]'); +} {{[1,3,4]}} +do_execsql_test json102-450-3 { + SELECT json(jsonb_remove('[0,1,2,3,4]','$[2]','$[0]')); +} {{[1,3,4]}} +do_execsql_test json102-450-4 { + SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[2]','$[0]')); +} {{[1,3,4]}} do_execsql_test json102-460 { SELECT json_remove('[0,1,2,3,4]','$[0]','$[2]'); } {{[1,2,4]}} +do_execsql_test json102-460-2 { + SELECT json_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]'); +} {{[1,2,4]}} +do_execsql_test json102-460-3 { + SELECT json(jsonb_remove('[0,1,2,3,4]','$[0]','$[2]')); +} {{[1,2,4]}} +do_execsql_test json102-460-4 { + SELECT json(jsonb_remove(jsonb('[0,1,2,3,4]'),'$[0]','$[2]')); +} {{[1,2,4]}} do_execsql_test json102-470 { SELECT json_remove('{"x":25,"y":42}'); } {{{"x":25,"y":42}}} +do_execsql_test json102-470-2 { + SELECT json_remove(jsonb('{"x":25,"y":42}')); +} {{{"x":25,"y":42}}} +do_execsql_test json102-470-3 { + SELECT json(jsonb_remove('{"x":25,"y":42}')); +} {{{"x":25,"y":42}}} +do_execsql_test json102-470-4 { + SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'))); +} {{{"x":25,"y":42}}} do_execsql_test json102-480 { SELECT json_remove('{"x":25,"y":42}','$.z'); } {{{"x":25,"y":42}}} +do_execsql_test json102-480-2 { + SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.z'); +} {{{"x":25,"y":42}}} +do_execsql_test json102-480-3 { + SELECT json(jsonb_remove('{"x":25,"y":42}','$.z')); +} {{{"x":25,"y":42}}} +do_execsql_test json102-480-4 { + SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.z')); +} {{{"x":25,"y":42}}} do_execsql_test json102-490 { SELECT json_remove('{"x":25,"y":42}','$.y'); } {{{"x":25}}} +do_execsql_test json102-490-2 { + SELECT json_remove(jsonb('{"x":25,"y":42}'),'$.y'); +} {{{"x":25}}} +do_execsql_test json102-490-3 { + SELECT json(jsonb_remove('{"x":25,"y":42}','$.y')); +} {{{"x":25}}} +do_execsql_test json102-490-4 { + SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$.y')); +} {{{"x":25}}} do_execsql_test json102-500 { SELECT json_remove('{"x":25,"y":42}','$'); } {{}} +do_execsql_test json102-500-2 { + SELECT json_remove(jsonb('{"x":25,"y":42}'),'$'); +} {{}} +do_execsql_test json102-500-3 { + SELECT json(jsonb_remove('{"x":25,"y":42}','$')); +} {{}} +do_execsql_test json102-500-4 { + SELECT json(jsonb_remove(jsonb('{"x":25,"y":42}'),'$')); +} {{}} do_execsql_test json102-510 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}'); } {{object}} +do_execsql_test json102-510b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778'); +} {{object}} do_execsql_test json102-520 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$'); } {{object}} +do_execsql_test json102-520b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$'); +} {{object}} do_execsql_test json102-530 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a'); } {{array}} +do_execsql_test json102-530b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a'); +} {{array}} do_execsql_test json102-540 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[0]'); } {{integer}} +do_execsql_test json102-540b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[0]'); +} {{integer}} do_execsql_test json102-550 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[1]'); } {{real}} +do_execsql_test json102-550b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[1]'); +} {{real}} do_execsql_test json102-560 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[2]'); } {{true}} +do_execsql_test json102-560b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[2]'); +} {{true}} do_execsql_test json102-570 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[3]'); } {{false}} +do_execsql_test json102-570b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[3]'); +} {{false}} do_execsql_test json102-580 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[4]'); } {{null}} +do_execsql_test json102-580b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[4]'); +} {{null}} do_execsql_test json102-590 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[5]'); } {{text}} +do_execsql_test json102-590b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[5]'); +} {{text}} do_execsql_test json102-600 { SELECT json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[6]'); } {{}} +do_execsql_test json102-600b { + SELECT json_type(x'cc0f1761cb0b133235332e350102001778','$.a[6]'); +} {{}} do_execsql_test json102-610 { SELECT json_valid(char(123)||'"x":35'||char(125)); } {{1}} @@ -312,17 +516,24 @@ do_execsql_test json102-620 { ifcapable vtab { do_execsql_test json102-1000 { - CREATE TABLE user(name,phone); + CREATE TABLE user(name,phone,phoneb); INSERT INTO user(name,phone) VALUES ('Alice','["919-555-2345","804-555-3621"]'), ('Bob','["201-555-8872"]'), ('Cindy','["704-555-9983"]'), ('Dave','["336-555-8421","704-555-4321","803-911-4421"]'); + UPDATE user SET phoneb=jsonb(phone); SELECT DISTINCT user.name FROM user, json_each(user.phone) WHERE json_each.value LIKE '704-%' ORDER BY 1; } {Cindy Dave} +do_execsql_test json102-1000b { + SELECT DISTINCT user.name + FROM user, json_each(user.phoneb) + WHERE json_each.value LIKE '704-%' + ORDER BY 1; +} {Cindy Dave} do_execsql_test json102-1010 { UPDATE user @@ -382,6 +593,12 @@ do_execsql_test json102-1110 { WHERE json_tree.type NOT IN ('object','array') ORDER BY +big.rowid, +json_tree.id } $correct_answer +do_execsql_test json102-1110b { + SELECT big.rowid, fullkey, value + FROM big, json_tree(jsonb(big.json)) + WHERE json_tree.type NOT IN ('object','array') + ORDER BY +big.rowid, +json_tree.id +} $correct_answer do_execsql_test json102-1120 { SELECT big.rowid, fullkey, atom FROM big, json_tree(big.json) From 8405e1140a6eefd2ab7e2a02a36e527caba07ca9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 10:43:30 +0000 Subject: [PATCH 034/347] Fix an issue with the use of jsonb_group_array() and jsonb_group_object() when used by window functions. FossilOrigin-Name: 808bd349ba587fbcdc4aea3f9c08e0df01ba08dec181c5af5ea157e89d86ff7b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index c3140c8eee..ea14e67936 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sfor\sJSONB. -D 2023-10-02T23:56:46.985 +C Fix\san\sissue\swith\sthe\suse\sof\sjsonb_group_array()\sand\sjsonb_group_object()\nwhen\sused\sby\swindow\sfunctions. +D 2023-10-03T10:43:30.961 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a2e70610cc14d0f97feb17bf0000c0e034f1cb5ac1b318bcf3ccf7a014f01a80 +F src/json.c 87218d0698545f668879e2a0f51dc5e3982de7d8cccfc0d0a27ede23f26f68d7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 54197149b811d30b6c4487eedf5692b164ed0f90cfcc541aa3157094f5f17f6a -R f7716cc560ac345b0556f46a68783d4e +P 6d4aeff5751722c83ebb0e1f21072b72be418c64dcf28ee032d3a548e1b3a951 +R d0d4e794647619bd04667ee02e2f09dc U drh -Z aa79deaa2b4777d46e48ea5272670f4a +Z 873b50f85fdb68283d7ad2c516b8f579 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cc209c225a..c2bdadfa3b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6d4aeff5751722c83ebb0e1f21072b72be418c64dcf28ee032d3a548e1b3a951 \ No newline at end of file +808bd349ba587fbcdc4aea3f9c08e0df01ba08dec181c5af5ea157e89d86ff7b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1e40668f9f..50da24b897 100644 --- a/src/json.c +++ b/src/json.c @@ -4897,6 +4897,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); + if( !isFinal ) pStr->nUsed--; return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, @@ -5012,6 +5013,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); + if( !isFinal ) pStr->nUsed--; return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, From ee0f6777f9796f82f4198f7805edf5fe1a24944e Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 10:59:01 +0000 Subject: [PATCH 035/347] Fix a problem in the JSONB parser that comes up following an OOM. FossilOrigin-Name: 355acfb18897254f6b6444a21d781b5e10e930b81952850dd2a40d88bbf2f3db --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ea14e67936..ad6777f075 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sissue\swith\sthe\suse\sof\sjsonb_group_array()\sand\sjsonb_group_object()\nwhen\sused\sby\swindow\sfunctions. -D 2023-10-03T10:43:30.961 +C Fix\sa\sproblem\sin\sthe\sJSONB\sparser\sthat\scomes\sup\sfollowing\san\sOOM. +D 2023-10-03T10:59:01.269 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 87218d0698545f668879e2a0f51dc5e3982de7d8cccfc0d0a27ede23f26f68d7 +F src/json.c 7ed190ae57c81674e978a420f4d1ba2293d09fc5dff8c26801e9e2303346981d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 6d4aeff5751722c83ebb0e1f21072b72be418c64dcf28ee032d3a548e1b3a951 -R d0d4e794647619bd04667ee02e2f09dc +P 808bd349ba587fbcdc4aea3f9c08e0df01ba08dec181c5af5ea157e89d86ff7b +R 807c8cfa37036711ec0caf325260119e U drh -Z 873b50f85fdb68283d7ad2c516b8f579 +Z 88d0db789da116a139a304208f9109e5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c2bdadfa3b..27183fc527 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -808bd349ba587fbcdc4aea3f9c08e0df01ba08dec181c5af5ea157e89d86ff7b \ No newline at end of file +355acfb18897254f6b6444a21d781b5e10e930b81952850dd2a40d88bbf2f3db \ No newline at end of file diff --git a/src/json.c b/src/json.c index 50da24b897..f25bed1bec 100644 --- a/src/json.c +++ b/src/json.c @@ -3495,7 +3495,7 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ while( joom ){ pParse->aNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; } j = (u32)r; From eb28787b5ff64a634a70ae7d649eb540f068b0e0 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 3 Oct 2023 17:07:54 +0000 Subject: [PATCH 036/347] Update fts5_decode() to allow for embedded 0x00 bytes in tokens. FossilOrigin-Name: e051120067fd87f57b498e505e3960cf4d14e8e33bad940618cc0823253254f7 --- ext/fts5/fts5_index.c | 28 ++++++++++++++++++++++------ manifest | 15 ++++++--------- manifest.uuid | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 9db5925b94..6cf30b5a00 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -7745,6 +7745,24 @@ static void fts5DecodeRowidList( } #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) +static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ + int ii; + fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1); + if( *pRc==SQLITE_OK ){ + for(ii=0; iin; ii++){ + if( pTerm->p[ii]==0x00 ){ + pBuf->p[pBuf->n++] = '\\'; + pBuf->p[pBuf->n++] = '0'; + }else{ + pBuf->p[pBuf->n++] = pTerm->p[ii]; + } + } + pBuf->p[pBuf->n] = 0x00; + } +} +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The implementation of user-defined scalar function fts5_decode(). @@ -7852,9 +7870,8 @@ static void fts5DecodeFunction( iOff += fts5GetVarint32(&a[iOff], nAppend); term.n = nKeep; fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]); - sqlite3Fts5BufferAppendPrintf( - &rc, &s, " term=%.*s", term.n, (const char*)term.p - ); + sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); + fts5BufferAppendTerm(&rc, &s, &term); iOff += nAppend; /* Figure out where the doclist for this term ends */ @@ -7962,9 +7979,8 @@ static void fts5DecodeFunction( fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; - sqlite3Fts5BufferAppendPrintf( - &rc, &s, " term=%.*s", term.n, (const char*)term.p - ); + sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); + fts5BufferAppendTerm(&rc, &s, &term); iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff); } diff --git a/manifest b/manifest index bf21569e58..431d902181 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Changes\sso\sthat\sfts5\scan\shandle\stokens\swith\sembedded\s'\\0'\sbytes. -D 2023-09-30T18:13:35.306 +C Update\sfts5_decode()\sto\sallow\sfor\sembedded\s0x00\sbytes\sin\stokens. +D 2023-10-03T17:07:54.562 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,7 +94,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 F ext/fts5/fts5_hash.c 76765856397eff56f526b0640b23a1677d737d35e07bc00e4b4b2e0fc5fda60d -F ext/fts5/fts5_index.c 16d775ecbccf7d3698a03bcae3c3fbee0749df748b93b29d0e82a37e02eaaa94 +F ext/fts5/fts5_index.c e472083d371f420d52ec80445b9d2a99b16b23548205cb4064ddcd41bd79f63e F ext/fts5/fts5_main.c 799ec88d2309055f6406bddb0bd6ed80148c5da5eb14594c3c5309a6e944d489 F ext/fts5/fts5_storage.c 3c9b41fce41b6410f2e8f82eb035c6a29b2560483f773e6dc98cf3cb2e4ddbb5 F ext/fts5/fts5_tcl.c 0d2bb0ff7bf6ee136015be118167f0bd956ddd05a8f02c68bd34299b50648f9f @@ -2123,11 +2123,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 c04022b7407f77eaf0175e831ebcd6bbdc0af1cef0d42c5c11102aa8484f24ca -R ee3b13ddf778c77c1640cd7c7844c1f5 -T *branch * fts5-token-data -T *sym-fts5-token-data * -T -sym-trunk * +P c027c092c4af53bd6ae3cc6e2b4439167d9eeb0f9de549b6a2c2a72a67ee886c +R 9e81ed5ff713a928831c6c73df8f7a54 U dan -Z 7d5bd217a552215de3d888e155abaef5 +Z 0a0daf2566a3400fafbefb55947df637 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9db9eb3c64..53a545a52f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c027c092c4af53bd6ae3cc6e2b4439167d9eeb0f9de549b6a2c2a72a67ee886c \ No newline at end of file +e051120067fd87f57b498e505e3960cf4d14e8e33bad940618cc0823253254f7 \ No newline at end of file From 1846de49a46bbe1487679d5fdc4283acd38ba307 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 3 Oct 2023 19:06:52 +0000 Subject: [PATCH 037/347] Fixes for fts5 expression parser module to allow embedded 0x00 bytes in tokens. FossilOrigin-Name: 342c8d0783f449817d3f565ff6b9f010a6c690beeea32f1861640810490a8b5f --- ext/fts5/fts5_expr.c | 43 +++++++++++++++++-------------- ext/fts5/test/fts5origintext.test | 4 +++ manifest | 14 +++++----- manifest.uuid | 2 +- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index f5101ba065..745a5d9fa6 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -100,7 +100,8 @@ struct Fts5ExprNode { struct Fts5ExprTerm { u8 bPrefix; /* True for a prefix term */ u8 bFirst; /* True if token must be first in column */ - char *zTerm; /* nul-terminated term */ + char *pTerm; /* Term data */ + int nTerm; /* Size of term in bytes */ Fts5IndexIter *pIter; /* Iterator for this term */ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ }; @@ -967,7 +968,7 @@ static int fts5ExprNearInitAll( p->pIter = 0; } rc = sqlite3Fts5IndexQuery( - pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm), + pExpr->pIndex, p->pTerm, p->nTerm, (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), pNear->pColset, @@ -1604,7 +1605,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ Fts5ExprTerm *pSyn; Fts5ExprTerm *pNext; Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; - sqlite3_free(pTerm->zTerm); + sqlite3_free(pTerm->pTerm); sqlite3Fts5IterClose(pTerm->pIter); for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ pNext = pSyn->pSynonym; @@ -1735,8 +1736,9 @@ static int fts5ParseTokenize( rc = SQLITE_NOMEM; }else{ memset(pSyn, 0, (size_t)nByte); - pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); - memcpy(pSyn->zTerm, pToken, nToken); + pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); + pSyn->nTerm = nToken; + memcpy(pSyn->pTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; } @@ -1761,7 +1763,8 @@ static int fts5ParseTokenize( if( rc==SQLITE_OK ){ pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; memset(pTerm, 0, sizeof(Fts5ExprTerm)); - pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); + pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); + pTerm->nTerm = nToken; } } @@ -1913,9 +1916,7 @@ int sqlite3Fts5ExprClonePhrase( int tflags = 0; Fts5ExprTerm *p; for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ - const char *zTerm = p->zTerm; - rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm), - 0, 0); + rc = fts5ParseTokenize((void*)&sCtx, tflags, p->pTerm, p->nTerm, 0, 0); tflags = FTS5_TOKEN_COLOCATED; } if( rc==SQLITE_OK ){ @@ -2296,11 +2297,13 @@ static Fts5ExprNode *fts5ParsePhraseToAnd( if( parseGrowPhraseArray(pParse) ){ fts5ExprPhraseFree(pPhrase); }else{ + Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; pParse->apPhrase[pParse->nPhrase++] = pPhrase; pPhrase->nTerm = 1; - pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup( - &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1 + pPhrase->aTerm[0].pTerm = sqlite3Fts5Strndup( + &pParse->rc, p->pTerm, p->nTerm ); + pPhrase->aTerm[0].nTerm = p->nTerm; pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) ); @@ -2485,16 +2488,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ /* Determine the maximum amount of space required. */ for(p=pTerm; p; p=p->pSynonym){ - nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; + nByte += pTerm->nTerm * 2 + 3 + 2; } zQuoted = sqlite3_malloc64(nByte); if( zQuoted ){ int i = 0; for(p=pTerm; p; p=p->pSynonym){ - char *zIn = p->zTerm; + char *zIn = p->pTerm; + char *zEnd = &zIn[p->nTerm]; zQuoted[i++] = '"'; - while( *zIn ){ + while( zInnTerm; iTerm++){ - char *zTerm = pPhrase->aTerm[iTerm].zTerm; - zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); + Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; + zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", + p->nTerm, p->pTerm + ); if( pPhrase->aTerm[iTerm].bPrefix ){ zRet = fts5PrintfAppend(zRet, "*"); } @@ -2994,9 +3000,8 @@ static int fts5ExprPopulatePoslistsCb( Fts5ExprTerm *pTerm; if( p->aPopulator[i].bOk==0 ) continue; for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ - int nTerm = (int)strlen(pTerm->zTerm); - if( (nTerm==nToken || (nTermbPrefix)) - && memcmp(pTerm->zTerm, pToken, nTerm)==0 + if( (pTerm->nTerm==nToken || (pTerm->nTermbPrefix)) + && memcmp(pTerm->pTerm, pToken, pTerm->nTerm)==0 ){ int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 791b850c76..155d74c025 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -45,6 +45,10 @@ do_execsql_test 1.3 { world } +do_execsql_test 1.4 { + SELECT rowid FROM ft('Hello'); +} {1} + #------------------------------------------------------------------------- reset_db diff --git a/manifest b/manifest index 431d902181..e0c37bf1e2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sfts5_decode()\sto\sallow\sfor\sembedded\s0x00\sbytes\sin\stokens. -D 2023-10-03T17:07:54.562 +C Fixes\sfor\sfts5\sexpression\sparser\smodule\sto\sallow\sembedded\s0x00\sbytes\sin\stokens. +D 2023-10-03T19:06:52.966 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -92,7 +92,7 @@ F ext/fts5/fts5Int.h 66a38b285e2b860baa29745d8eff27f5b0809268e7820498494d9acfacc F ext/fts5/fts5_aux.c 572d5ec92ba7301df2fea3258576332f2f4d2dfd66d8263afd157d9deceac480 F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 -F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 +F ext/fts5/fts5_expr.c cc215d39714b428523d2f2ef42b713c83095a28a67bc7f6f2dc4ac036a29f460 F ext/fts5/fts5_hash.c 76765856397eff56f526b0640b23a1677d737d35e07bc00e4b4b2e0fc5fda60d F ext/fts5/fts5_index.c e472083d371f420d52ec80445b9d2a99b16b23548205cb4064ddcd41bd79f63e F ext/fts5/fts5_main.c 799ec88d2309055f6406bddb0bd6ed80148c5da5eb14594c3c5309a6e944d489 @@ -187,7 +187,7 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 9a6edc85ccc4afb10e71d54d98d8170f850272e55b120520f367afbb12526674 +F ext/fts5/test/fts5origintext.test 3e1ac3230f65a0d644e9bf0738bebb09b4db9d9f123e1307d8630e42269b4afb F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2123,8 +2123,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 c027c092c4af53bd6ae3cc6e2b4439167d9eeb0f9de549b6a2c2a72a67ee886c -R 9e81ed5ff713a928831c6c73df8f7a54 +P e051120067fd87f57b498e505e3960cf4d14e8e33bad940618cc0823253254f7 +R 5cce41f02eae121cb66e72942ef56113 U dan -Z 0a0daf2566a3400fafbefb55947df637 +Z 80b8e2664e768ed2ac03913fcf0180ea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 53a545a52f..4798938837 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e051120067fd87f57b498e505e3960cf4d14e8e33bad940618cc0823253254f7 \ No newline at end of file +342c8d0783f449817d3f565ff6b9f010a6c690beeea32f1861640810490a8b5f \ No newline at end of file From 6e737b92521d8bb3e796ca87b13af36a55aa1531 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 19:37:19 +0000 Subject: [PATCH 038/347] Improved handling of OOM while translating the JsonNode representing into the BLOB representation. FossilOrigin-Name: ef5956710bb542d6045c82937d02218a7ed45af94cf3959b0c180268e04d14e1 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/json.c | 14 +++++++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 68220a4e0d..40a49f1c48 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-10-03T11:36:47.346 +C Improved\shandling\sof\sOOM\swhile\stranslating\sthe\sJsonNode\srepresenting\sinto\nthe\sBLOB\srepresentation. +D 2023-10-03T19:37:19.514 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -259,12 +259,12 @@ F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7b9ff2218129ece98ba60c F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java d12352c0e22840de484ffa9b11ed5058bb0daca2e9f218055d3c54c947a273c4 F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c -F ext/jni/src/org/sqlite/jni/SQLTester.java da42be06a2d644e0b915b40508934c1f32391e5308ab8767c1e2e65a281a198f w ext/jni/src/org/sqlite/jni/tester/SQLTester.java +F ext/jni/src/org/sqlite/jni/SQLTester.java da42be06a2d644e0b915b40508934c1f32391e5308ab8767c1e2e65a281a198f F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 9860c1cebd8a38041306f2ee7563f2898fcbdf77e4bfa393fba25b4924edcb5d F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab F ext/jni/src/org/sqlite/jni/Tester1.java ced62ed417c3326f93d2e90b3bb64ac2db58ac42a7ad7a5965b24545434e3200 -F ext/jni/src/org/sqlite/jni/TesterFts5.java 854c737bb5c9463ee92a8ee230013e924236dd4b74d4688dd62c17f38d5837db w ext/jni/src/org/sqlite/jni/fts5/TesterFts5.java +F ext/jni/src/org/sqlite/jni/TesterFts5.java 854c737bb5c9463ee92a8ee230013e924236dd4b74d4688dd62c17f38d5837db F ext/jni/src/org/sqlite/jni/TraceV2Callback.java beb0b064c1a5f8bfe585a324ed39a4e33edbe379a3fc60f1401661620d3ca7c0 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 8376f4a931f2d5612b295c003c9515ba933ee76d8f95610e89c339727376e36c F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246 @@ -289,7 +289,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3_blob.java fc631ad52feea6e3d1d62b0d0e769ac10 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java cf7f076d8b0f2a23faebbd64e12e8b3dd1977378ca828245d186f1b98458127d F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e w ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md +F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7ed190ae57c81674e978a420f4d1ba2293d09fc5dff8c26801e9e2303346981d +F src/json.c 6a6437fce0eb558b05fc30c1d806ee342e99ccb2dcae1f53b038c60572f84383 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 355acfb18897254f6b6444a21d781b5e10e930b81952850dd2a40d88bbf2f3db e4f9d3946fa570bccea2de17c5536901bdabd46cfe25678fdd37ba0e2bfd99b1 -R 98820669d6cd8b3d30c4719644b02708 +P e6406a9865b75dea2f26d3ee4f4c206958400059c7f92ced88edc8507dd3c82f +R a18f64bf40d3960df89ef1dde4bf90eb U drh -Z e2ba343971c07e25e0b89cfd2106272c +Z d1b86d452a43e36a5aedf44b0fbb363f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 034aa299ab..bea2bfc71f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e6406a9865b75dea2f26d3ee4f4c206958400059c7f92ced88edc8507dd3c82f \ No newline at end of file +ef5956710bb542d6045c82937d02218a7ed45af94cf3959b0c180268e04d14e1 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f25bed1bec..c4d874309f 100644 --- a/src/json.c +++ b/src/json.c @@ -1011,7 +1011,12 @@ static void jsonReturnNodeAsJson( JsonParse x; memset(&x, 0, sizeof(x)); jsonRenderNodeAsBlob(pParse, pNode, &x); - sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); + if( x.oom ){ + sqlite3_result_error_nomem(pCtx); + sqlite3_free(x.aBlob); + }else{ + sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); + } }else{ jsonStringInit(&s, pCtx); jsonRenderNodeAsText(pParse, pNode, &s); @@ -2598,8 +2603,11 @@ static void jsonBlobChangePayloadSize( u32 i, u32 szPayload ){ - u8 *a = &pParse->aBlob[i]; - u8 szType = a[0]>>4; + u8 *a; + u8 szType; + if( pParse->oom ) return; + a = &pParse->aBlob[i]; + szType = a[0]>>4; if( szType<=11 ){ assert( szPayload<=11 ); a[0] = (a[0] & 0x0f) | (szPayload<<4); From 51cc3041fe03a7ede42c5286d92bbdf7adbfb276 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 20:58:39 +0000 Subject: [PATCH 039/347] Fix problems in the "json" output column of the json_tree() virtual table for the case when the input is JSONB. FossilOrigin-Name: fefa4475c496aab32fe7b9cc203747ce4faf7448da4f29b807e5486c7686238d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 9 ++++++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 40a49f1c48..fd92509288 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\shandling\sof\sOOM\swhile\stranslating\sthe\sJsonNode\srepresenting\sinto\nthe\sBLOB\srepresentation. -D 2023-10-03T19:37:19.514 +C Fix\sproblems\sin\sthe\s"json"\soutput\scolumn\sof\sthe\sjson_tree()\svirtual\stable\nfor\sthe\scase\swhen\sthe\sinput\sis\sJSONB. +D 2023-10-03T20:58:39.820 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6a6437fce0eb558b05fc30c1d806ee342e99ccb2dcae1f53b038c60572f84383 +F src/json.c 6b11a18b09250869464c7b0e025e76c8697366a2a1e88459ee6ce1e189064332 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 e6406a9865b75dea2f26d3ee4f4c206958400059c7f92ced88edc8507dd3c82f -R a18f64bf40d3960df89ef1dde4bf90eb +P ef5956710bb542d6045c82937d02218a7ed45af94cf3959b0c180268e04d14e1 +R dc84d245e8ee185581c160d484d587e1 U drh -Z d1b86d452a43e36a5aedf44b0fbb363f +Z 9c1148fb20ac73a79d87122df2949595 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bea2bfc71f..e9a8f2071a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ef5956710bb542d6045c82937d02218a7ed45af94cf3959b0c180268e04d14e1 \ No newline at end of file +fefa4475c496aab32fe7b9cc203747ce4faf7448da4f29b807e5486c7686238d \ No newline at end of file diff --git a/src/json.c b/src/json.c index c4d874309f..967a3af006 100644 --- a/src/json.c +++ b/src/json.c @@ -3514,7 +3514,6 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ break; } } - pParse->aBlob[i] = 0; return i+x+sz; } @@ -5356,8 +5355,12 @@ static int jsonEachColumn( break; } case JEACH_JSON: { - assert( i==JEACH_JSON ); - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + if( p->sParse.isBinary ){ + sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, + SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + } break; } } From f362731c1cba8f46cba7b978705573d9e0a1eb51 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 21:54:09 +0000 Subject: [PATCH 040/347] Fix a bug in the jsonReturnFromBlob() function that causes a positive result for a negative value for when a JSON integer is too large and needs to be converted into double. FossilOrigin-Name: dca684da0c29ec78460362f972ea7747be42c13c4d1325da9d62c1ea58022e39 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index fd92509288..5334835936 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sproblems\sin\sthe\s"json"\soutput\scolumn\sof\sthe\sjson_tree()\svirtual\stable\nfor\sthe\scase\swhen\sthe\sinput\sis\sJSONB. -D 2023-10-03T20:58:39.820 +C Fix\sa\sbug\sin\sthe\sjsonReturnFromBlob()\sfunction\sthat\scauses\sa\spositive\sresult\nfor\sa\snegative\svalue\sfor\swhen\sa\sJSON\sinteger\sis\stoo\slarge\sand\sneeds\sto\sbe\nconverted\sinto\sdouble. +D 2023-10-03T21:54:09.421 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6b11a18b09250869464c7b0e025e76c8697366a2a1e88459ee6ce1e189064332 +F src/json.c 62de483f16a54d30fb98b1ff85a344cb945cfa65a4056dc8950f2b101f0413ec F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 ef5956710bb542d6045c82937d02218a7ed45af94cf3959b0c180268e04d14e1 -R dc84d245e8ee185581c160d484d587e1 +P fefa4475c496aab32fe7b9cc203747ce4faf7448da4f29b807e5486c7686238d +R f94f7ac8eca40bde91714a188c3f89b5 U drh -Z 9c1148fb20ac73a79d87122df2949595 +Z 6b6f54adde231acb6a7cd797b978cce1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e9a8f2071a..2980b28f6b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fefa4475c496aab32fe7b9cc203747ce4faf7448da4f29b807e5486c7686238d \ No newline at end of file +dca684da0c29ec78460362f972ea7747be42c13c4d1325da9d62c1ea58022e39 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 967a3af006..e29940c720 100644 --- a/src/json.c +++ b/src/json.c @@ -3856,6 +3856,7 @@ static void jsonReturnFromBlob( }else if( rc==3 && bNeg ){ sqlite3_result_int64(pCtx, SMALLEST_INT64); }else{ + if( bNeg ){ n--; sz++; } goto to_double; } break; From 4500d87e763914432dd65d181e2bbe928a36500b Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 3 Oct 2023 22:40:22 +0000 Subject: [PATCH 041/347] Fix a memory leak in JSON group-aggregates when the output is JSONB. FossilOrigin-Name: 08e7db138b636890cb29a76d65c2069c6e2ff44470fa4bca14f4526fe5f195ae --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 12 ++++++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 5334835936..3991c75517 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbug\sin\sthe\sjsonReturnFromBlob()\sfunction\sthat\scauses\sa\spositive\sresult\nfor\sa\snegative\svalue\sfor\swhen\sa\sJSON\sinteger\sis\stoo\slarge\sand\sneeds\sto\sbe\nconverted\sinto\sdouble. -D 2023-10-03T21:54:09.421 +C Fix\sa\smemory\sleak\sin\sJSON\sgroup-aggregates\swhen\sthe\soutput\sis\sJSONB. +D 2023-10-03T22:40:22.299 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 62de483f16a54d30fb98b1ff85a344cb945cfa65a4056dc8950f2b101f0413ec +F src/json.c 52e4fa529ca0a727ece05607fc3641a7ba6734e39ecc1f3e9ba8c1a7caa1f0cc F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2123,8 +2123,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 fefa4475c496aab32fe7b9cc203747ce4faf7448da4f29b807e5486c7686238d -R f94f7ac8eca40bde91714a188c3f89b5 +P dca684da0c29ec78460362f972ea7747be42c13c4d1325da9d62c1ea58022e39 +R 29d5fdb97c89ceaf3767b5ff5eb276bd U drh -Z 6b6f54adde231acb6a7cd797b978cce1 +Z 573dbd4dc43601643579851c7ec5201b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2980b28f6b..bdc5d99e2d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dca684da0c29ec78460362f972ea7747be42c13c4d1325da9d62c1ea58022e39 \ No newline at end of file +08e7db138b636890cb29a76d65c2069c6e2ff44470fa4bca14f4526fe5f195ae \ No newline at end of file diff --git a/src/json.c b/src/json.c index e29940c720..955c35377b 100644 --- a/src/json.c +++ b/src/json.c @@ -4905,7 +4905,11 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); - if( !isFinal ) pStr->nUsed--; + if( isFinal ){ + sqlite3RCStrUnref(pStr->zBuf); + }else{ + pStr->nUsed--; + } return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, @@ -5021,7 +5025,11 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ assert( pStr->bStatic ); }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); - if( !isFinal ) pStr->nUsed--; + if( isFinal ){ + sqlite3RCStrUnref(pStr->zBuf); + }else{ + pStr->nUsed--; + } return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, From 9061e22a05f7b0037a9121a8f58f26e37ae8c2f0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 15:02:00 +0000 Subject: [PATCH 042/347] Allow the PG-style syntax for the PATH operand on the right-hand side of the ->> and -> operators. FossilOrigin-Name: bae5071b0834aa3b7fd4bd871f863e1b148c6558989c8f6cdd02dc4da4770953 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 6d4d77ef56..0303e4cd7f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-10-05T11:22:16.707 +C Allow\sthe\sPG-style\ssyntax\sfor\sthe\sPATH\soperand\son\sthe\sright-hand\sside\sof\nthe\s->>\sand\s->\soperators. +D 2023-10-05T15:02:00.537 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 52e4fa529ca0a727ece05607fc3641a7ba6734e39ecc1f3e9ba8c1a7caa1f0cc +F src/json.c 7f765a31a4b0c7df2505a43e74f5059eaff8ed851d60dfc7b6eedfdf9f5b4089 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 08e7db138b636890cb29a76d65c2069c6e2ff44470fa4bca14f4526fe5f195ae a2464bbb825b5976ef974a2e6c17ea150f5e6fcd0dd0f144b9f9c1c22a9c9c82 -R 84b74e4fd51ca93386cc67654677da5e +P be5907b648386e05530d1e321e2a3e563e07e99f08373aaf9a3452adc9228dc3 +R a953453e7f2d067ffe8d15205e74a86b U drh -Z 4d31c96be6e146821c899c0f24a5a9d8 +Z 1c88c01c099cd40d1caaee0961805818 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 973ac14362..12eff4a58c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -be5907b648386e05530d1e321e2a3e563e07e99f08373aaf9a3452adc9228dc3 \ No newline at end of file +bae5071b0834aa3b7fd4bd871f863e1b148c6558989c8f6cdd02dc4da4770953 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 955c35377b..3307db0bab 100644 --- a/src/json.c +++ b/src/json.c @@ -3990,12 +3990,38 @@ static void jsonExtractFromBlob( u32 i; JsonParse px; if( zPath==0 ) return; - if( zPath[0]=='$' ) zPath++; memset(&px, 0, sizeof(px)); px.nBlob = sqlite3_value_bytes(pJson); px.aBlob = (u8*)sqlite3_value_blob(pJson); if( px.aBlob==0 ) return; - i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + if( zPath[0]=='$' ){ + zPath++; + i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + }else if( (flags & JSON_ABPATH) ){ + /* The -> and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for + ** convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + */ + JsonString jx; + jsonStringInit(&jx, ctx); + if( sqlite3Isdigit(zPath[0]) ){ + jsonAppendRawNZ(&jx, "[", 1); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRawNZ(&jx, "]", 2); + zPath = jx.zBuf; + }else if( zPath[0]!='[' ){ + jsonAppendRawNZ(&jx, ".", 1); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + zPath = jx.zBuf; + } + i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + jsonStringReset(&jx); + } if( i Date: Thu, 5 Oct 2023 15:05:33 +0000 Subject: [PATCH 043/347] Fix a memory leak following a syntax error in jsonb(). FossilOrigin-Name: bf4b36eda8e200b67041ebdf60fc31d0325bd84185272f8d390c56a91d6ac1ee --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 0303e4cd7f..51517eaffb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\sPG-style\ssyntax\sfor\sthe\sPATH\soperand\son\sthe\sright-hand\sside\sof\nthe\s->>\sand\s->\soperators. -D 2023-10-05T15:02:00.537 +C Fix\sa\smemory\sleak\sfollowing\sa\ssyntax\serror\sin\sjsonb(). +D 2023-10-05T15:05:33.202 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7f765a31a4b0c7df2505a43e74f5059eaff8ed851d60dfc7b6eedfdf9f5b4089 +F src/json.c 85d3615672a130f2fd22ea6005984048953b745deb48886be72f227ba6f21f9c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 be5907b648386e05530d1e321e2a3e563e07e99f08373aaf9a3452adc9228dc3 -R a953453e7f2d067ffe8d15205e74a86b +P bae5071b0834aa3b7fd4bd871f863e1b148c6558989c8f6cdd02dc4da4770953 +R 4bb974d3e61cd02ec317b43011061d2f U drh -Z 1c88c01c099cd40d1caaee0961805818 +Z be9524533dfa255e55d6b8c43429a3ea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 12eff4a58c..a27cb035f1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bae5071b0834aa3b7fd4bd871f863e1b148c6558989c8f6cdd02dc4da4770953 \ No newline at end of file +bf4b36eda8e200b67041ebdf60fc31d0325bd84185272f8d390c56a91d6ac1ee \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3307db0bab..24487f430c 100644 --- a/src/json.c +++ b/src/json.c @@ -4203,12 +4203,13 @@ static void jsonbFunc( x.nJson = nJson; if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); + sqlite3_free(pParse->aBlob); }else{ sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); - pParse->aBlob = 0; - pParse->nBlob = 0; - pParse->nBlobAlloc = 0; } + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; jsonParseReset(pParse); } } From 0585d9f08f0b7786739621b3e41362079b2cb1e9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 16:33:00 +0000 Subject: [PATCH 044/347] Slightly stricter testing for when a BLOB is valid JSONB: If the element is a null, true, or false, its payload size must be zero. FossilOrigin-Name: 487781be8a93d9b8d870efb9f25822bb4bbcf9d39d571d1017e9c29043ea515d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 51517eaffb..dea192ba1b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\smemory\sleak\sfollowing\sa\ssyntax\serror\sin\sjsonb(). -D 2023-10-05T15:05:33.202 +C Slightly\sstricter\stesting\sfor\swhen\sa\sBLOB\sis\svalid\sJSONB:\s\sIf\sthe\selement\nis\sa\snull,\strue,\sor\sfalse,\sits\spayload\ssize\smust\sbe\szero. +D 2023-10-05T16:33:00.108 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 85d3615672a130f2fd22ea6005984048953b745deb48886be72f227ba6f21f9c +F src/json.c 5fce177a1fa0e0853024141e4f603cf04324833c35f1ee2484c20296236d1fa8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 bae5071b0834aa3b7fd4bd871f863e1b148c6558989c8f6cdd02dc4da4770953 -R 4bb974d3e61cd02ec317b43011061d2f +P bf4b36eda8e200b67041ebdf60fc31d0325bd84185272f8d390c56a91d6ac1ee +R 82f6c0430b8291dd07f3a6ba0e331be4 U drh -Z be9524533dfa255e55d6b8c43429a3ea +Z fe5195d3e7cce0d43ee394c132952294 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a27cb035f1..943883628e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bf4b36eda8e200b67041ebdf60fc31d0325bd84185272f8d390c56a91d6ac1ee \ No newline at end of file +487781be8a93d9b8d870efb9f25822bb4bbcf9d39d571d1017e9c29043ea515d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 24487f430c..c405c5943a 100644 --- a/src/json.c +++ b/src/json.c @@ -3415,6 +3415,8 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ s.nBlob = nBlob; n = jsonbPayloadSize(&s, 0, &sz); if( n==0 ) return 0; + if( sz+n!=(u32)nBlob ) return 0; + if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0; return sz+n==(u32)nBlob; } From 14fce24a9bacf6e872154a2a679ff95acb10a7a8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 17:52:39 +0000 Subject: [PATCH 045/347] Fix the text-to-JSONB parser so that it handles some JSON5 floating point literals correctly. FossilOrigin-Name: 564edb3b6dd752e09e445e3a25c2f5394ceede2a2cdff251d6433dadc0b3644f --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index dea192ba1b..d1a8859d10 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Slightly\sstricter\stesting\sfor\swhen\sa\sBLOB\sis\svalid\sJSONB:\s\sIf\sthe\selement\nis\sa\snull,\strue,\sor\sfalse,\sits\spayload\ssize\smust\sbe\szero. -D 2023-10-05T16:33:00.108 +C Fix\sthe\stext-to-JSONB\sparser\sso\sthat\sit\shandles\ssome\sJSON5\sfloating\spoint\nliterals\scorrectly. +D 2023-10-05T17:52:39.456 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 5fce177a1fa0e0853024141e4f603cf04324833c35f1ee2484c20296236d1fa8 +F src/json.c 5b00523c237b93a0c0f644bfa2adc5cf2bf381e3a2a02791a03a1765d6ddac67 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 bf4b36eda8e200b67041ebdf60fc31d0325bd84185272f8d390c56a91d6ac1ee -R 82f6c0430b8291dd07f3a6ba0e331be4 +P 487781be8a93d9b8d870efb9f25822bb4bbcf9d39d571d1017e9c29043ea515d +R 6dd39d4bbce9b9abcc4c4c2e13a0b712 U drh -Z fe5195d3e7cce0d43ee394c132952294 +Z 37a9884f8448701e772294f223618265 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 943883628e..233800a07f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -487781be8a93d9b8d870efb9f25822bb4bbcf9d39d571d1017e9c29043ea515d \ No newline at end of file +564edb3b6dd752e09e445e3a25c2f5394ceede2a2cdff251d6433dadc0b3644f \ No newline at end of file diff --git a/src/json.c b/src/json.c index c405c5943a..ba701ba68e 100644 --- a/src/json.c +++ b/src/json.c @@ -2955,7 +2955,7 @@ json_parse_restart: } if( z[i+1]=='.' ){ pParse->hasNonstd = 1; - t |= 0x03; + t |= 0x01; goto parse_number_2; } pParse->iErr = i; From 5b6cfb79068f20f4df34322139ef7c0b9d58ea2c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 18:09:12 +0000 Subject: [PATCH 046/347] Change the json_valid(X) routine to return true whenever X is a blob that could plausibly be a valid JSONB. FossilOrigin-Name: 425f0b85a6f8ad0604c4a5faa18efb90436fedb1fe2612a559147c62cff8b7e7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index d1a8859d10..3b52cf09de 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\stext-to-JSONB\sparser\sso\sthat\sit\shandles\ssome\sJSON5\sfloating\spoint\nliterals\scorrectly. -D 2023-10-05T17:52:39.456 +C Change\sthe\sjson_valid(X)\sroutine\sto\sreturn\strue\swhenever\sX\sis\sa\sblob\sthat\ncould\splausibly\sbe\sa\svalid\sJSONB. +D 2023-10-05T18:09:12.028 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 5b00523c237b93a0c0f644bfa2adc5cf2bf381e3a2a02791a03a1765d6ddac67 +F src/json.c 436cc9b8a4cfef2ac5c8547fcf8acff8a91b9fb9b9d070ebf240e8ab871f23a7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 487781be8a93d9b8d870efb9f25822bb4bbcf9d39d571d1017e9c29043ea515d -R 6dd39d4bbce9b9abcc4c4c2e13a0b712 +P 564edb3b6dd752e09e445e3a25c2f5394ceede2a2cdff251d6433dadc0b3644f +R 99ed34df38b98d618529ef152948a4c8 U drh -Z 37a9884f8448701e772294f223618265 +Z 05060c3a4460d98ce1ee0304660e3eea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 233800a07f..8c16d8482a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -564edb3b6dd752e09e445e3a25c2f5394ceede2a2cdff251d6433dadc0b3644f \ No newline at end of file +425f0b85a6f8ad0604c4a5faa18efb90436fedb1fe2612a559147c62cff8b7e7 \ No newline at end of file diff --git a/src/json.c b/src/json.c index ba701ba68e..3c179ac16a 100644 --- a/src/json.c +++ b/src/json.c @@ -4812,8 +4812,14 @@ static void jsonTypeFunc( /* ** json_valid(JSON) ** -** Return 1 if JSON is a well-formed canonical JSON string according -** to RFC-7159. Return 0 otherwise. +** Return 1 if the argument is one of: +** +** * A well-formed canonical JSON string according to RFC-8259 +** (without JSON5 enhancements), or +** +** * A BLOB that plausibly could be a JSONB value. +** +** Return 0 otherwise. */ static void jsonValidFunc( sqlite3_context *ctx, @@ -4829,6 +4835,10 @@ static void jsonValidFunc( #endif return; } + if( jsonFuncArgMightBeBinary(argv[0]) ){ + sqlite3_result_int(ctx, 1); + return; + } p = jsonParseCached(ctx, argv[0], 0, 0); if( p==0 || p->oom ){ sqlite3_result_error_nomem(ctx); From 51d507d432c60e241ad3684a50e0d9a74283303b Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 18:33:19 +0000 Subject: [PATCH 047/347] Fix the parsing of non-standard "Infinity" and "NaN" values from text into JSONB. FossilOrigin-Name: df1fbbeb83a2b4a496c9de0d86c7c8c677f8fe3bc770da163dcc1d338a17b58b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 3b52cf09de..2f457f4e43 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sjson_valid(X)\sroutine\sto\sreturn\strue\swhenever\sX\sis\sa\sblob\sthat\ncould\splausibly\sbe\sa\svalid\sJSONB. -D 2023-10-05T18:09:12.028 +C Fix\sthe\sparsing\sof\snon-standard\s"Infinity"\sand\s"NaN"\svalues\sfrom\stext\sinto\nJSONB. +D 2023-10-05T18:33:19.785 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 436cc9b8a4cfef2ac5c8547fcf8acff8a91b9fb9b9d070ebf240e8ab871f23a7 +F src/json.c 91ba143709783aef3aa7d0e04cc963284a70d1b904f0da14494ede24819b356e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 564edb3b6dd752e09e445e3a25c2f5394ceede2a2cdff251d6433dadc0b3644f -R 99ed34df38b98d618529ef152948a4c8 +P 425f0b85a6f8ad0604c4a5faa18efb90436fedb1fe2612a559147c62cff8b7e7 +R 8b87e1b5ecb5e7b583f0e4691e8e2ff5 U drh -Z 05060c3a4460d98ce1ee0304660e3eea +Z 222bd3b1cad134cfad3fe073645453f8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8c16d8482a..37628e34a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -425f0b85a6f8ad0604c4a5faa18efb90436fedb1fe2612a559147c62cff8b7e7 \ No newline at end of file +df1fbbeb83a2b4a496c9de0d86c7c8c677f8fe3bc770da163dcc1d338a17b58b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3c179ac16a..8b14aa3aa9 100644 --- a/src/json.c +++ b/src/json.c @@ -3097,8 +3097,7 @@ json_parse_restart: } if( sqlite3Isalnum(z[i+nn]) ) continue; if( aNanInfName[k].eType==JSON_REAL ){ - jsonBlobAppendOneByte(pParse, JSONB_FLOAT); - jsonBlobAppendOneByte(pParse, 5); + jsonBlobAppendOneByte(pParse, JSONB_FLOAT | 0x50); jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); }else{ jsonBlobAppendOneByte(pParse, JSONB_NULL); From dac27071526c6e0b54827713f8b8c2cee413a9c0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 20:17:01 +0000 Subject: [PATCH 048/347] Turn an unreachable branch into an assert(). FossilOrigin-Name: 0f75199160e48fa8c44f986f1af777adf19e40fbd114a6f58e24d5e6dede779d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 2f457f4e43..dc737fc064 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sparsing\sof\snon-standard\s"Infinity"\sand\s"NaN"\svalues\sfrom\stext\sinto\nJSONB. -D 2023-10-05T18:33:19.785 +C Turn\san\sunreachable\sbranch\sinto\san\sassert(). +D 2023-10-05T20:17:01.522 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 91ba143709783aef3aa7d0e04cc963284a70d1b904f0da14494ede24819b356e +F src/json.c e8befacf282a191e830e68f64cf09b1d725c7b4fac0a5617456b218832ecd760 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 425f0b85a6f8ad0604c4a5faa18efb90436fedb1fe2612a559147c62cff8b7e7 -R 8b87e1b5ecb5e7b583f0e4691e8e2ff5 +P df1fbbeb83a2b4a496c9de0d86c7c8c677f8fe3bc770da163dcc1d338a17b58b +R 44eaad4f19392397a894a77de5ede2c8 U drh -Z 222bd3b1cad134cfad3fe073645453f8 +Z 855bfdf3eb65ffc730a52733ff32ac54 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 37628e34a1..ed65424673 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -df1fbbeb83a2b4a496c9de0d86c7c8c677f8fe3bc770da163dcc1d338a17b58b \ No newline at end of file +0f75199160e48fa8c44f986f1af777adf19e40fbd114a6f58e24d5e6dede779d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8b14aa3aa9..30612aff91 100644 --- a/src/json.c +++ b/src/json.c @@ -2517,7 +2517,7 @@ static void jsonRemoveAllNulls(JsonNode *pNode){ static int jsonBlobExpand(JsonParse *pParse, u32 N){ u8 *aNew; u32 t; - if( N<=pParse->nBlobAlloc ) return 0; + assert( N>pParse->nBlobAlloc ); if( pParse->nBlobAlloc==0 ){ t = 100; }else{ From f7af8f32704b5675b1acab942d3b8e6931c8edd0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 22:52:43 +0000 Subject: [PATCH 049/347] Improvements to comments. No changes to code. FossilOrigin-Name: ac74d7877685006e43684edd6a2d22be8c9857f9f9eb52fc5b3c182d5e2fdb8d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index dc737fc064..3423bef24f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Turn\san\sunreachable\sbranch\sinto\san\sassert(). -D 2023-10-05T20:17:01.522 +C Improvements\sto\scomments.\s\sNo\schanges\sto\scode. +D 2023-10-05T22:52:43.004 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e8befacf282a191e830e68f64cf09b1d725c7b4fac0a5617456b218832ecd760 +F src/json.c a51a9dd785d3d65f7b4ffc3827610571efae82c5f7469ff8bc9b973de4b43245 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 df1fbbeb83a2b4a496c9de0d86c7c8c677f8fe3bc770da163dcc1d338a17b58b -R 44eaad4f19392397a894a77de5ede2c8 +P 0f75199160e48fa8c44f986f1af777adf19e40fbd114a6f58e24d5e6dede779d +R 61adb43472fe06712b001bc8a8daf5aa U drh -Z 855bfdf3eb65ffc730a52733ff32ac54 +Z b88584b3539f893217397c49059fc0f7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ed65424673..b2ac92830c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f75199160e48fa8c44f986f1af777adf19e40fbd114a6f58e24d5e6dede779d \ No newline at end of file +ac74d7877685006e43684edd6a2d22be8c9857f9f9eb52fc5b3c182d5e2fdb8d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 30612aff91..57309e3674 100644 --- a/src/json.c +++ b/src/json.c @@ -1515,10 +1515,10 @@ static const struct NanInfName { ** ** 0 End of input ** -1 Syntax error -** -2 '}' seen -** -3 ']' seen -** -4 ',' seen -** -5 ':' seen +** -2 '}' seen \ +** -3 ']' seen \___ For these returns, pParse->iErr is set to +** -4 ',' seen / the index in zJson[] of the seen character +** -5 ':' seen / */ static int jsonParseValueFromText(JsonParse *pParse, u32 i){ char c; @@ -2656,10 +2656,10 @@ static int jsonIs4HexB(const char *z, int *pOp){ ** ** 0 End of input ** -1 Syntax error -** -2 '}' seen -** -3 ']' seen -** -4 ',' seen -** -5 ':' seen +** -2 '}' seen \ +** -3 ']' seen \___ For these returns, pParse->iErr is set to +** -4 ',' seen / the index in zJson[] of the seen character +** -5 ':' seen / */ static int jsonTranslateTextValueToBlob(JsonParse *pParse, u32 i){ char c; From 27401d38d8a50ff2f03ac4ce34453418249e41d8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 5 Oct 2023 23:05:35 +0000 Subject: [PATCH 050/347] Better error detection when doing a lookup on a JSONB. FossilOrigin-Name: 6e8e0eedbf06e175af6ef230f1ce9a5ad84698b1c6dcd2794d878c746e7b6be4 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 3423bef24f..218f2d0904 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\scomments.\s\sNo\schanges\sto\scode. -D 2023-10-05T22:52:43.004 +C Better\serror\sdetection\swhen\sdoing\sa\slookup\son\sa\sJSONB. +D 2023-10-05T23:05:35.100 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a51a9dd785d3d65f7b4ffc3827610571efae82c5f7469ff8bc9b973de4b43245 +F src/json.c 1b3349c49b363c654b76f5f9b45c4172d13955679fd4a242b3d92c879f95294c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 0f75199160e48fa8c44f986f1af777adf19e40fbd114a6f58e24d5e6dede779d -R 61adb43472fe06712b001bc8a8daf5aa +P ac74d7877685006e43684edd6a2d22be8c9857f9f9eb52fc5b3c182d5e2fdb8d +R 01e239b11851146ca21382a5317a867b U drh -Z b88584b3539f893217397c49059fc0f7 +Z 51245adc64c2e53d6bd1f03542705249 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b2ac92830c..cbe2a3c96d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac74d7877685006e43684edd6a2d22be8c9857f9f9eb52fc5b3c182d5e2fdb8d \ No newline at end of file +6e8e0eedbf06e175af6ef230f1ce9a5ad84698b1c6dcd2794d878c746e7b6be4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 57309e3674..738354f009 100644 --- a/src/json.c +++ b/src/json.c @@ -3720,6 +3720,7 @@ static u32 jsonLookupBlobStep( x = pParse->aBlob[j] & 0x0f; if( xJSONB_TEXTRAW ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_BLOB_ERROR; k = j+n; if( k+sz>=iEnd ) return JSON_BLOB_ERROR; if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ @@ -3732,6 +3733,7 @@ static u32 jsonLookupBlobStep( j = k+sz; if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_BLOB_ERROR; j += n+sz; } if( j>iEnd ) return JSON_BLOB_ERROR; @@ -3776,6 +3778,7 @@ static u32 jsonLookupBlobStep( } k--; n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_BLOB_ERROR; j += n+sz; } if( j>iEnd ) return JSON_BLOB_ERROR; From dd7f09e6c00315eff7930a8c13d4e8d659c541a9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 00:06:26 +0000 Subject: [PATCH 051/347] Improvements to coping with malformed JSONB. FossilOrigin-Name: 563cde404cec4c6559ae0c5fc5edbc878fee874b36562ce2fac5049cc8349343 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 9 +++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 218f2d0904..bc4af507a0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Better\serror\sdetection\swhen\sdoing\sa\slookup\son\sa\sJSONB. -D 2023-10-05T23:05:35.100 +C Improvements\sto\scoping\swith\smalformed\sJSONB. +D 2023-10-06T00:06:26.513 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 1b3349c49b363c654b76f5f9b45c4172d13955679fd4a242b3d92c879f95294c +F src/json.c 6758202d3fa30306ae6379c6f995158b0768e1f75e304ff604c1bb498499fcd1 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 ac74d7877685006e43684edd6a2d22be8c9857f9f9eb52fc5b3c182d5e2fdb8d -R 01e239b11851146ca21382a5317a867b +P 6e8e0eedbf06e175af6ef230f1ce9a5ad84698b1c6dcd2794d878c746e7b6be4 +R 0e32d4d167fb477dce553004066d14c6 U drh -Z 51245adc64c2e53d6bd1f03542705249 +Z 0df001ca326ecb2506a99289e47391a0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cbe2a3c96d..62b4fee4ff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6e8e0eedbf06e175af6ef230f1ce9a5ad84698b1c6dcd2794d878c746e7b6be4 \ No newline at end of file +563cde404cec4c6559ae0c5fc5edbc878fee874b36562ce2fac5049cc8349343 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 738354f009..7522c9571a 100644 --- a/src/json.c +++ b/src/json.c @@ -3512,6 +3512,7 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ if( !pParse->oom ){ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; } + if( k&1 ) return -1; break; } } @@ -3724,10 +3725,10 @@ static u32 jsonLookupBlobStep( k = j+n; if( k+sz>=iEnd ) return JSON_BLOB_ERROR; if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ - j = k+sz; + j = k+sz; if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); - if( j+n+sz>iEnd ) return JSON_BLOB_ERROR; + if( n==0 || j+n+sz>iEnd ) return JSON_BLOB_ERROR; return jsonLookupBlobStep(pParse, j, &zPath[i], pzErr); } j = k+sz; @@ -4031,7 +4032,7 @@ static void jsonExtractFromBlob( }else if( i==JSON_BLOB_NOTFOUND ){ return; /* Return NULL if not found */ }else if( i==JSON_BLOB_ERROR ){ - sqlite3_result_error(ctx, "malformed JSONB", -1); + sqlite3_result_error(ctx, "malformed JSON", -1); }else{ sqlite3_result_error(ctx, zErr, -1); } @@ -4340,7 +4341,7 @@ static void jsonExtractFunc( JsonString jx; if( argc<2 ) return; - if( sqlite3_value_type(argv[0])==SQLITE_BLOB && argc==2 ){ + if( jsonFuncArgMightBeBinary(argv[0]) && argc==2 ){ jsonExtractFromBlob(ctx, argv[0], argv[1], flags); return; } From e1e2f2dcc5b76ef2bb2ce7c0e59f67b8913b74a2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 14:52:49 +0000 Subject: [PATCH 052/347] Improvements to error handling. FossilOrigin-Name: b41dd237fb6c0dd7daedeaaf81dbc4fdb31cf511fd32cd3d4ee69db34e389915 --- manifest | 12 +++---- manifest.uuid | 2 +- src/json.c | 89 +++++++++++++++++++++++++++------------------------ 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/manifest b/manifest index b6d3e12ce0..555f873fb7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\scompiler\swarning\sfixes\sfrom\strunk\sinto\sthe\sjsonb\sbranch. -D 2023-10-06T13:05:42.465 +C Improvements\sto\serror\shandling. +D 2023-10-06T14:52:49.860 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e89b89a6f36b2281524aea01521690e1d3bc69db2754856e4a03ac271f261346 +F src/json.c fe8cd7b38e947a89fbe8b8099a88ee363d02a6747009ba5246a517de1e15159f F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 563cde404cec4c6559ae0c5fc5edbc878fee874b36562ce2fac5049cc8349343 9bf4bfd68080367b58594e0d44b110b3ee9766420f648537fd7bc638dacefb72 -R fa6a8cc346d8604b4e2021455b16ba57 +P 6409d307915f8969f12df2d5ffa961645bd4db7ccfbd6f52237a217b6a867252 +R bb54780cc2adfb5e91d01e5a38070ac9 U drh -Z 77ed8049621ac72b8ae81ce5801b8fae +Z 4eb2d45211dd6d4bee3b0c79a0072e1a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dea74400a2..950949b618 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6409d307915f8969f12df2d5ffa961645bd4db7ccfbd6f52237a217b6a867252 \ No newline at end of file +b41dd237fb6c0dd7daedeaaf81dbc4fdb31cf511fd32cd3d4ee69db34e389915 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3419572759..69c9e67b6b 100644 --- a/src/json.c +++ b/src/json.c @@ -207,10 +207,15 @@ struct JsonString { u64 nAlloc; /* Bytes of storage available in zBuf[] */ u64 nUsed; /* Bytes of zBuf[] currently used */ u8 bStatic; /* True if zBuf is static space */ - u8 bErr; /* True if an error has been encountered */ + u8 eErr; /* True if an error has been encountered */ char zSpace[100]; /* Initial static space */ }; +/* Allowed values for JsonString.eErr */ +#define JSTRING_OOM 0x01 /* Out of memory */ +#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ +#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ + /* A deferred cleanup task. A list of JsonCleanup objects might be ** run when the JsonParse object is destroyed. */ @@ -376,7 +381,7 @@ static void jsonStringZero(JsonString *p){ */ static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ p->pCtx = pCtx; - p->bErr = 0; + p->eErr = 0; jsonStringZero(p); } @@ -391,7 +396,7 @@ static void jsonStringReset(JsonString *p){ /* Report an out-of-memory (OOM) condition */ static void jsonStringOom(JsonString *p){ - p->bErr = 1; + p->eErr |= JSTRING_OOM; sqlite3_result_error_nomem(p->pCtx); jsonStringReset(p); } @@ -403,7 +408,7 @@ static int jsonStringGrow(JsonString *p, u32 N){ u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; char *zNew; if( p->bStatic ){ - if( p->bErr ) return 1; + if( p->eErr ) return 1; zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ jsonStringOom(p); @@ -415,7 +420,7 @@ static int jsonStringGrow(JsonString *p, u32 N){ }else{ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); if( p->zBuf==0 ){ - p->bErr = 1; + p->eErr |= JSTRING_OOM; jsonStringZero(p); return SQLITE_NOMEM; } @@ -488,7 +493,7 @@ static void jsonAppendChar(JsonString *p, char c){ */ static int jsonForceRCStr(JsonString *p){ jsonAppendChar(p, 0); - if( p->bErr ) return 0; + if( p->eErr ) return 0; p->nUsed--; if( p->bStatic==0 ) return 1; p->nAlloc = 0; @@ -626,7 +631,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ char *zBuf = sqlite3_malloc64( N+1 ); if( zBuf==0 ){ - p->bErr = 1; + p->eErr |= JSTRING_OOM; return; } memcpy(zBuf, zIn, N); @@ -731,9 +736,9 @@ static void jsonAppendSqlValue( px.aBlob = (u8*)sqlite3_value_blob(pValue); px.nBlob = sqlite3_value_bytes(pValue); jsonRenderBlob(&px, 0, p); - }else if( p->bErr==0 ){ + }else if( p->eErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); - p->bErr = 2; + p->eErr = JSTRING_ERR; jsonStringReset(p); } break; @@ -748,7 +753,7 @@ static void jsonAppendSqlValue( ** The JsonString is reset. */ static void jsonReturnString(JsonString *p){ - if( p->bErr==0 ){ + if( p->eErr==0 ){ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(p); @@ -760,10 +765,13 @@ static void jsonReturnString(JsonString *p){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, (void(*)(void*))sqlite3RCStrUnref, SQLITE_UTF8); + }else{ + sqlite3_result_error_nomem(p->pCtx); } - } - if( p->bErr==1 ){ + }else if( p->eErr & JSTRING_OOM ){ sqlite3_result_error_nomem(p->pCtx); + }else if( p->eErr & JSTRING_MALFORMED ){ + sqlite3_result_error(p->pCtx, "malformed JSON", -1); } jsonStringReset(p); } @@ -2974,6 +2982,7 @@ json_parse_restart: } } } + parse_number_2: for(j=i+1;; j++){ c = z[j]; @@ -3236,7 +3245,7 @@ static u32 jsonRenderBlob( n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ - pOut->bErr = 1; + pOut->eErr |= JSTRING_MALFORMED; return pParse->nBlob+1; } switch( pParse->aBlob[i] & 0x0f ){ @@ -3258,44 +3267,35 @@ static u32 jsonRenderBlob( break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ - int k; + int k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; - if( zIn[0]=='-' ){ - zIn++; - sz--; - jsonAppendChar(pOut, '-'); + if( zIn[0]=='+' || zIn[0]=='-' ){ + if( zIn[0]=='-' ) jsonAppendChar(pOut, '-'); + k++; } - for(k=2; kaBlob[i+n]; - if( zIn[0]=='-' ){ - zIn++; - sz--; - jsonAppendChar(pOut, '-'); + if( zIn[0]=='+' || zIn[0]=='-' ){ + if( zIn[0]=='-' ) jsonAppendChar(pOut, '-'); + k++; } - if( zIn[0]=='.' ){ + if( zIn[k]=='.' ){ jsonAppendChar(pOut, '0'); } - for(k=0; k0 ){ - jsonAppendRawNZ(pOut, zIn, sz); - } break; } case JSONB_TEXT: @@ -3808,7 +3808,12 @@ static void jsonReturnTextJsonFromBlob( x.nBlob = nBlob; jsonStringInit(&s, ctx); jsonRenderBlob(&x, 0, &s); - jsonReturnString(&s); + if( x.nErr ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + jsonStringReset(&s); + }else{ + jsonReturnString(&s); + } } @@ -4371,7 +4376,7 @@ static void jsonExtractFunc( jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); jsonAppendChar(&jx, 0); } - pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); + pNode = jx.eErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); jsonStringReset(&jx); }else{ pNode = jsonLookup(p, zPath, 0, ctx); @@ -4942,9 +4947,9 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ pStr->pCtx = ctx; jsonAppendChar(pStr, ']'); flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - if( pStr->bErr ){ - if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); - assert( pStr->bStatic ); + if( pStr->eErr ){ + jsonReturnString(pStr); + return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ @@ -5062,9 +5067,9 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ jsonAppendChar(pStr, '}'); pStr->pCtx = ctx; flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - if( pStr->bErr ){ - if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); - assert( pStr->bStatic ); + if( pStr->eErr ){ + jsonReturnString(pStr); + return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ From e690d5ad80ae90573fce913b0c01d05aaac0c769 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 14:59:40 +0000 Subject: [PATCH 053/347] Fix compiler warnings. FossilOrigin-Name: 5227add3c8d509de2e081249163fafdf30ac3173a6d710957f3c3b6f03e7017e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 17 ++++++++++------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 555f873fb7..53be387052 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\serror\shandling. -D 2023-10-06T14:52:49.860 +C Fix\scompiler\swarnings. +D 2023-10-06T14:59:40.414 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fe8cd7b38e947a89fbe8b8099a88ee363d02a6747009ba5246a517de1e15159f +F src/json.c e1a4c5ffadbeaab6dfd9dfb687f8c7a0b9e012ad2ec8b74223e75f86295a5a5e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 6409d307915f8969f12df2d5ffa961645bd4db7ccfbd6f52237a217b6a867252 -R bb54780cc2adfb5e91d01e5a38070ac9 +P b41dd237fb6c0dd7daedeaaf81dbc4fdb31cf511fd32cd3d4ee69db34e389915 +R d43877b0997e4344224eb4366c47821e U drh -Z 4eb2d45211dd6d4bee3b0c79a0072e1a +Z 0dbbe0cfb7ed03f93b950daed7da83cd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 950949b618..cd9e86ced9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b41dd237fb6c0dd7daedeaaf81dbc4fdb31cf511fd32cd3d4ee69db34e389915 \ No newline at end of file +5227add3c8d509de2e081249163fafdf30ac3173a6d710957f3c3b6f03e7017e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 69c9e67b6b..b2053af9ec 100644 --- a/src/json.c +++ b/src/json.c @@ -3267,7 +3267,7 @@ static u32 jsonRenderBlob( break; } case JSONB_INT5: { /* Integer literal in hexadecimal notation */ - int k = 2; + u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; if( zIn[0]=='+' || zIn[0]=='-' ){ @@ -3281,7 +3281,7 @@ static u32 jsonRenderBlob( break; } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ - int k = 0; + u32 k = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; if( zIn[0]=='+' || zIn[0]=='-' ){ if( zIn[0]=='-' ) jsonAppendChar(pOut, '-'); @@ -3753,14 +3753,14 @@ static u32 jsonLookupBlobStep( k = jsonbArrayCount(pParse, iRoot); i = 2; if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ - unsigned int x = 0; + unsigned int nn = 0; i = 3; do{ - x = x*10 + zPath[i] - '0'; + nn = nn*10 + zPath[i] - '0'; i++; }while( sqlite3Isdigit(zPath[i]) ); - if( x>k ) return 0; - k -= x; + if( nn>k ) return 0; + k -= nn; } if( zPath[i]!=']' ){ *pzErr = zPath; @@ -3997,7 +3997,7 @@ static void jsonExtractFromBlob( ){ const char *zPath = (const char*)sqlite3_value_text(pPath); const char *zErr = 0; - u32 i; + u32 i = 0; JsonParse px; if( zPath==0 ) return; memset(&px, 0, sizeof(px)); @@ -4031,6 +4031,9 @@ static void jsonExtractFromBlob( } i = jsonLookupBlobStep(&px, 0, zPath, &zErr); jsonStringReset(&jx); + }else{ + sqlite3_result_error(ctx, "bad path", -1); + return; } if( i Date: Fri, 6 Oct 2023 15:35:42 +0000 Subject: [PATCH 054/347] Fixes to rendering JSON5 extensions encoded as JSONB. FossilOrigin-Name: 5a17e4479aad2d8313170e5de83a1c52f30b55d9d4fb776024fa6622e175c63b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 20 +++++++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 53be387052..ab452be4c4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\scompiler\swarnings. -D 2023-10-06T14:59:40.414 +C Fixes\sto\srendering\sJSON5\sextensions\sencoded\sas\sJSONB. +D 2023-10-06T15:35:42.574 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e1a4c5ffadbeaab6dfd9dfb687f8c7a0b9e012ad2ec8b74223e75f86295a5a5e +F src/json.c 07cbae93a65485082e3a2b7900b0c5e3912a5dd37ea0d948af54fec0fe39855c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 b41dd237fb6c0dd7daedeaaf81dbc4fdb31cf511fd32cd3d4ee69db34e389915 -R d43877b0997e4344224eb4366c47821e +P 5227add3c8d509de2e081249163fafdf30ac3173a6d710957f3c3b6f03e7017e +R 4cd522c574d0ec5aee66d5dc3b63f485 U drh -Z 0dbbe0cfb7ed03f93b950daed7da83cd +Z 9624fe80ac16daaa792bb7c1f316d34b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cd9e86ced9..630195eeab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5227add3c8d509de2e081249163fafdf30ac3173a6d710957f3c3b6f03e7017e \ No newline at end of file +5a17e4479aad2d8313170e5de83a1c52f30b55d9d4fb776024fa6622e175c63b \ No newline at end of file diff --git a/src/json.c b/src/json.c index b2053af9ec..25ef372d81 100644 --- a/src/json.c +++ b/src/json.c @@ -3308,15 +3308,16 @@ static u32 jsonRenderBlob( case JSONB_TEXT5: { const char *zIn; u32 k; + u32 sz2 = sz; zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); - while( sz>0 ){ - for(k=0; k0 ){ + for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); zIn += k; - sz -= k; - if( sz==0 ) break; + sz2 -= k; + if( sz2==0 ) break; } assert( zIn[0]=='\\' ); switch( (u8)zIn[1] ){ @@ -3330,7 +3331,7 @@ static u32 jsonRenderBlob( jsonAppendRawNZ(pOut, "\\u00", 4); jsonAppendRawNZ(pOut, &zIn[2], 2); zIn += 2; - sz -= 2; + sz2 -= 2; break; case '0': jsonAppendRawNZ(pOut, "\\u0000", 6); @@ -3338,24 +3339,24 @@ static u32 jsonRenderBlob( case '\r': if( zIn[2]=='\n' ){ zIn++; - sz--; + sz2--; } break; case '\n': break; case 0xe2: - assert( sz>=4 ); + assert( sz2>=4 ); assert( 0x80==(u8)zIn[2] ); assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); zIn += 2; - sz -= 2; + sz2 -= 2; break; default: jsonAppendRawNZ(pOut, zIn, 2); break; } zIn += 2; - sz -= 2; + sz2 -= 2; } jsonAppendChar(pOut, '"'); break; @@ -3387,6 +3388,7 @@ static u32 jsonRenderBlob( } default: { + pOut->eErr |= JSTRING_MALFORMED; break; } } From 7b4349735f339a4272060b196d7ede864825f3d4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 18:21:47 +0000 Subject: [PATCH 055/347] Incremental improvements to the JSONB logic. FossilOrigin-Name: fe326829c27715e249f727ba797c7df6491e874ec205a0a82ee09c78d61c6e1f --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 32 +++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index ab452be4c4..7a0a01ea06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sto\srendering\sJSON5\sextensions\sencoded\sas\sJSONB. -D 2023-10-06T15:35:42.574 +C Incremental\simprovements\sto\sthe\sJSONB\slogic. +D 2023-10-06T18:21:47.497 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 07cbae93a65485082e3a2b7900b0c5e3912a5dd37ea0d948af54fec0fe39855c +F src/json.c 7a37b75ae7a31399af464627f923232f88bdee093af5148445f0df42ee85bd77 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 5227add3c8d509de2e081249163fafdf30ac3173a6d710957f3c3b6f03e7017e -R 4cd522c574d0ec5aee66d5dc3b63f485 +P 5a17e4479aad2d8313170e5de83a1c52f30b55d9d4fb776024fa6622e175c63b +R 83990e8b89665f4fb2543aa74cc93235 U drh -Z 9624fe80ac16daaa792bb7c1f316d34b +Z e604af5f00b43366ef6886bc95452ede # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 630195eeab..7926def158 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5a17e4479aad2d8313170e5de83a1c52f30b55d9d4fb776024fa6622e175c63b \ No newline at end of file +fe326829c27715e249f727ba797c7df6491e874ec205a0a82ee09c78d61c6e1f \ No newline at end of file diff --git a/src/json.c b/src/json.c index 25ef372d81..62dc44b830 100644 --- a/src/json.c +++ b/src/json.c @@ -3150,7 +3150,7 @@ static int jsonConvertTextToBlob( } } if( i<=0 ){ - if( pCtx!=0 ){ + if( ALWAYS(pCtx!=0) ){ if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); }else{ @@ -3191,7 +3191,7 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; - if( i>pParse->nBlob ){ + if( NEVER(i>pParse->nBlob) ){ *pSz = 0; return 0; } @@ -3200,21 +3200,21 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ sz = x; n = 1; }else if( x==12 ){ - if( i+1>pParse->nBlob ){ + if( i+1>=pParse->nBlob ){ *pSz = 0; return 0; } sz = pParse->aBlob[i+1]; n = 2; }else if( x==13 ){ - if( i+2>pParse->nBlob ){ + if( i+2>=pParse->nBlob ){ *pSz = 0; return 0; } sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; n = 3; }else{ - if( i+4>pParse->nBlob ){ + if( i+4>=pParse->nBlob ){ *pSz = 0; return 0; } @@ -3233,8 +3233,15 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ /* ** Convert the binary BLOB representation of JSON beginning at -** aBlob[0] (and extending for no more than nBlob bytes) into +** aBlob[0] and extending for no more than nBlob bytes into ** a pure JSON string. The string is appended to pOut. +** +** If an error is detected in the BLOB input, the pOut->eErr flag +** might get set to JSTRING_MALFORMED. But not all BLOB input errors +** are detected. So a malformed JSONB input might either result +** in an error, or in incorrect JSON. +** +** The pOut->eErr JSTRING_OOM flag is set on a OOM. */ static u32 jsonRenderBlob( JsonParse *pParse, /* the complete parse of the JSON */ @@ -3270,11 +3277,14 @@ static u32 jsonRenderBlob( u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; - if( zIn[0]=='+' || zIn[0]=='-' ){ - if( zIn[0]=='-' ) jsonAppendChar(pOut, '-'); + if( zIn[0]=='-' ){ + jsonAppendChar(pOut, '-'); k++; } for(; keErr |= JSTRING_MALFORMED; + } u = u*16 + sqlite3HexToInt(zIn[k]); } jsonPrintf(100,pOut,"%llu",u); @@ -3283,8 +3293,8 @@ static u32 jsonRenderBlob( case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ u32 k = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; - if( zIn[0]=='+' || zIn[0]=='-' ){ - if( zIn[0]=='-' ) jsonAppendChar(pOut, '-'); + if( zIn[0]=='-' ){ + jsonAppendChar(pOut, '-'); k++; } if( zIn[k]=='.' ){ @@ -3761,7 +3771,7 @@ static u32 jsonLookupBlobStep( nn = nn*10 + zPath[i] - '0'; i++; }while( sqlite3Isdigit(zPath[i]) ); - if( nn>k ) return 0; + if( nn>k ) return JSON_BLOB_NOTFOUND; k -= nn; } if( zPath[i]!=']' ){ From 6b8aa95c3c3f79cedb4fcaea84687c671642bcf9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 22:16:09 +0000 Subject: [PATCH 056/347] Improved error detection for JSONB inputs. FossilOrigin-Name: 6945e11aa441ace52b93cdbb81ac45b6f809987f3dbc522251e63f8cb948fddd --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 +++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 7a0a01ea06..5fe562b491 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Incremental\simprovements\sto\sthe\sJSONB\slogic. -D 2023-10-06T18:21:47.497 +C Improved\serror\sdetection\sfor\sJSONB\sinputs. +D 2023-10-06T22:16:09.559 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7a37b75ae7a31399af464627f923232f88bdee093af5148445f0df42ee85bd77 +F src/json.c b806a942b8abe97c95eef416fe6b467687e3b6452db35bf53746b19bd6800d5f F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 5a17e4479aad2d8313170e5de83a1c52f30b55d9d4fb776024fa6622e175c63b -R 83990e8b89665f4fb2543aa74cc93235 +P fe326829c27715e249f727ba797c7df6491e874ec205a0a82ee09c78d61c6e1f +R 37a35a123339c5edc35b6af8c811707d U drh -Z e604af5f00b43366ef6886bc95452ede +Z 5c69847c848dbc8186c5994996d772c2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7926def158..8c40873330 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fe326829c27715e249f727ba797c7df6491e874ec205a0a82ee09c78d61c6e1f \ No newline at end of file +6945e11aa441ace52b93cdbb81ac45b6f809987f3dbc522251e63f8cb948fddd \ No newline at end of file diff --git a/src/json.c b/src/json.c index 62dc44b830..25aa186e9a 100644 --- a/src/json.c +++ b/src/json.c @@ -3284,8 +3284,10 @@ static u32 jsonRenderBlob( for(; keErr |= JSTRING_MALFORMED; + break; + }else{ + u = u*16 + sqlite3HexToInt(zIn[k]); } - u = u*16 + sqlite3HexToInt(zIn[k]); } jsonPrintf(100,pOut,"%llu",u); break; From 11aa2adca34aed5b723e4772b39176aafc1da84a Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 6 Oct 2023 23:02:06 +0000 Subject: [PATCH 057/347] Correct handling of "raw" strings in JSON. This requires three test-case changes in TH3 to add double-quotes to the path outputs from json_tree(). The new behavior is correct, I believe. FossilOrigin-Name: ab2bf3e3596dfa7566f4a130adeccbef4056c7321181112a875477193583f30c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 28 +++++++++++++++------------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 5fe562b491..7ea0301feb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\serror\sdetection\sfor\sJSONB\sinputs. -D 2023-10-06T22:16:09.559 +C Correct\shandling\sof\s"raw"\sstrings\sin\sJSON.\s\sThis\srequires\sthree\stest-case\nchanges\sin\sTH3\sto\sadd\sdouble-quotes\sto\sthe\spath\soutputs\sfrom\sjson_tree().\nThe\snew\sbehavior\sis\scorrect,\sI\sbelieve. +D 2023-10-06T23:02:06.178 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c b806a942b8abe97c95eef416fe6b467687e3b6452db35bf53746b19bd6800d5f +F src/json.c 4d2527a8f01797bc4a83ca282f4ea82a5e4a1fdbb05260df241a4a76d2e610b9 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 fe326829c27715e249f727ba797c7df6491e874ec205a0a82ee09c78d61c6e1f -R 37a35a123339c5edc35b6af8c811707d +P 6945e11aa441ace52b93cdbb81ac45b6f809987f3dbc522251e63f8cb948fddd +R 9a8ad0594c72d0104c485b0093cb4da0 U drh -Z 5c69847c848dbc8186c5994996d772c2 +Z 53c0ada212c66efecbd15c380158c623 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8c40873330..c079911246 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6945e11aa441ace52b93cdbb81ac45b6f809987f3dbc522251e63f8cb948fddd \ No newline at end of file +ab2bf3e3596dfa7566f4a130adeccbef4056c7321181112a875477193583f30c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 25aa186e9a..850f2bbf48 100644 --- a/src/json.c +++ b/src/json.c @@ -910,13 +910,7 @@ static void jsonRenderNodeAsText( case JSON_STRING: { assert( pNode->eU==1 ); if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->jnFlags & JNODE_LABEL ){ - jsonAppendChar(pOut, '"'); - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); - jsonAppendChar(pOut, '"'); - }else{ - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - } + jsonAppendString(pOut, pNode->u.zJContent, pNode->n); }else if( pNode->jnFlags & JNODE_JSON5 ){ jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); }else{ @@ -1564,7 +1558,7 @@ json_parse_restart: ){ k++; } - jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); + jsonParseAddNode(pParse, JSON_STRING, k-j, &z[j]); pParse->hasNonstd = 1; x = k; }else{ @@ -3373,6 +3367,10 @@ static u32 jsonRenderBlob( jsonAppendChar(pOut, '"'); break; } + case JSONB_TEXTRAW: { + jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); + break; + } case JSONB_ARRAY: { jsonAppendChar(pOut, '['); j = i+n; @@ -3481,10 +3479,14 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ jsonParseAddNode(pParse, JSON_REAL | (JNODE_JSON5<<8), sz, zPayload); break; } - case JSONB_TEXT: { + case JSONB_TEXTRAW: { jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); break; } + case JSONB_TEXT: { + jsonParseAddNode(pParse, JSON_STRING, sz, zPayload); + break; + } case JSONB_TEXTJ: { jsonParseAddNode(pParse, JSON_STRING | (JNODE_ESCAPE<<8), sz, zPayload); break; @@ -3495,10 +3497,6 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ sz, zPayload); break; } - case JSONB_TEXTRAW: { - jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); - break; - } case JSONB_ARRAY: { int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); u32 j = i+x; @@ -3529,6 +3527,10 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ if( k&1 ) return -1; break; } + default: { + pParse->nErr++; + break; + } } return i+x+sz; } From 522e880bc263a2b7afa3234fe473532cc054348c Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 09:13:59 +0000 Subject: [PATCH 058/347] Report unknown JSONB element type when parsing JSONB into JsonNode. FossilOrigin-Name: 1f4d3268257bda234c66c3767451b3d39d7b4c7c73a779ad396d9dfc5315905c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 7ea0301feb..25c5daf405 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\shandling\sof\s"raw"\sstrings\sin\sJSON.\s\sThis\srequires\sthree\stest-case\nchanges\sin\sTH3\sto\sadd\sdouble-quotes\sto\sthe\spath\soutputs\sfrom\sjson_tree().\nThe\snew\sbehavior\sis\scorrect,\sI\sbelieve. -D 2023-10-06T23:02:06.178 +C Report\sunknown\sJSONB\selement\stype\swhen\sparsing\sJSONB\sinto\sJsonNode. +D 2023-10-07T09:13:59.445 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4d2527a8f01797bc4a83ca282f4ea82a5e4a1fdbb05260df241a4a76d2e610b9 +F src/json.c e1a1da1393d89d9845dccb57cb1e37fa9b753294946c2779d2c94dddbdee799b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 6945e11aa441ace52b93cdbb81ac45b6f809987f3dbc522251e63f8cb948fddd -R 9a8ad0594c72d0104c485b0093cb4da0 +P ab2bf3e3596dfa7566f4a130adeccbef4056c7321181112a875477193583f30c +R 6dab506825da64397c11d8b8419cab92 U drh -Z 53c0ada212c66efecbd15c380158c623 +Z be12a743f89d2812c7cba83b03ca7a82 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c079911246..68c204be9d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ab2bf3e3596dfa7566f4a130adeccbef4056c7321181112a875477193583f30c \ No newline at end of file +1f4d3268257bda234c66c3767451b3d39d7b4c7c73a779ad396d9dfc5315905c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 850f2bbf48..3c2af6e845 100644 --- a/src/json.c +++ b/src/json.c @@ -3528,8 +3528,7 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ break; } default: { - pParse->nErr++; - break; + return -1; } } return i+x+sz; From bae197c175d8e58bfccd873e29d3c2d722f6b2ae Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 11:36:16 +0000 Subject: [PATCH 059/347] Improved error messages from search on JSONB. FossilOrigin-Name: 96cfdc31e305406123f6ef1b920423f71902dc4b34381c3efb720274343d37cc --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 15 ++++++++++----- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 25c5daf405..5c7372384b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Report\sunknown\sJSONB\selement\stype\swhen\sparsing\sJSONB\sinto\sJsonNode. -D 2023-10-07T09:13:59.445 +C Improved\serror\smessages\sfrom\ssearch\son\sJSONB. +D 2023-10-07T11:36:16.795 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e1a1da1393d89d9845dccb57cb1e37fa9b753294946c2779d2c94dddbdee799b +F src/json.c 969f4897a681aa786f77ed7122db07475cf95f473d3a2de2572ffae600ec0ac1 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 ab2bf3e3596dfa7566f4a130adeccbef4056c7321181112a875477193583f30c -R 6dab506825da64397c11d8b8419cab92 +P 1f4d3268257bda234c66c3767451b3d39d7b4c7c73a779ad396d9dfc5315905c +R 473fbbc9196350cff1fe0e5f952691b5 U drh -Z be12a743f89d2812c7cba83b03ca7a82 +Z ebe282f031d63d9584408df2f4948927 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 68c204be9d..fc45fd3b7c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1f4d3268257bda234c66c3767451b3d39d7b4c7c73a779ad396d9dfc5315905c \ No newline at end of file +96cfdc31e305406123f6ef1b920423f71902dc4b34381c3efb720274343d37cc \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3c2af6e845..fdc7df41b2 100644 --- a/src/json.c +++ b/src/json.c @@ -3716,8 +3716,8 @@ static u32 jsonLookupBlobStep( if( zPath[i] ){ i++; }else{ - *pzErr = zPath; - return 0; + *pzErr = "unterminated \""; + return JSON_BLOB_PATHERROR; } testcase( nKey==0 ); }else{ @@ -3778,11 +3778,11 @@ static u32 jsonLookupBlobStep( k -= nn; } if( zPath[i]!=']' ){ - *pzErr = zPath; + *pzErr = "unterminated ["; return JSON_BLOB_PATHERROR; } }else{ - *pzErr = zPath; + *pzErr = "bad argument to []"; return JSON_BLOB_PATHERROR; } } @@ -3799,7 +3799,7 @@ static u32 jsonLookupBlobStep( } if( j>iEnd ) return JSON_BLOB_ERROR; }else{ - *pzErr = zPath; + *pzErr = "syntax error"; return JSON_BLOB_PATHERROR; } return JSON_BLOB_NOTFOUND; @@ -4056,6 +4056,11 @@ static void jsonExtractFromBlob( return; /* Return NULL if not found */ }else if( i==JSON_BLOB_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); + }else if( i==JSON_BLOB_PATHERROR ){ + char *zMsg = sqlite3_mprintf("in JSON path '%s': %s", + sqlite3_value_text(pPath), zErr); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); }else{ sqlite3_result_error(ctx, zErr, -1); } From 1244b6cbf663132e6f3d8c285eef0162c6fca397 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 17:50:06 +0000 Subject: [PATCH 060/347] Improvements to error messages returned when the ->> operator fails. FossilOrigin-Name: 2f3388f14c843f1c02926e8b929365c06c1f1f4ea6fe6316092c3799c14549d3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 29 ++++++++++------------------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index 1720248ca6..5ccd6fe084 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\smemory\sleak\sfix\sfrom\strunk. -D 2023-10-07T11:37:00.963 +C Improvements\sto\serror\smessages\sreturned\swhen\sthe\s->>\soperator\sfails. +D 2023-10-07T17:50:06.014 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 969f4897a681aa786f77ed7122db07475cf95f473d3a2de2572ffae600ec0ac1 +F src/json.c a5d273004e0b0d4b961efe59b403c5b118218b3db2c2d61b4512ea2815ada8b2 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 96cfdc31e305406123f6ef1b920423f71902dc4b34381c3efb720274343d37cc f99ff655d09763c4a22d065041644ece793d84c82c644931e89ccf50c4f4564a -R 9cb62826bab57bac8933243b48ca592e +P 358de1b09f3d5ec0fe459775b0a2a99dfa235817327016b472aaa1ed56d952e6 +R 65b1837fdcd2785b2dbe03dd6bcb09bb U drh -Z cdf0570fe999933e060b2c73f0eb1dc1 +Z f25f60a6b37a3fc4a80bb7878104ebfa # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 282aac4978..f4e3476f43 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -358de1b09f3d5ec0fe459775b0a2a99dfa235817327016b472aaa1ed56d952e6 \ No newline at end of file +2f3388f14c843f1c02926e8b929365c06c1f1f4ea6fe6316092c3799c14549d3 \ No newline at end of file diff --git a/src/json.c b/src/json.c index fdc7df41b2..fac05a0776 100644 --- a/src/json.c +++ b/src/json.c @@ -3696,8 +3696,7 @@ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ static u32 jsonLookupBlobStep( JsonParse *pParse, /* The JSON to search */ u32 iRoot, /* Begin the search at this element of aBlob[] */ - const char *zPath, /* The path to search */ - const char **pzErr /* Make *pzErr point to any syntax error in zPath */ + const char *zPath /* The path to search */ ){ u32 i, j, k, nKey, sz, n, iEnd; const char *zKey; @@ -3707,7 +3706,6 @@ static u32 jsonLookupBlobStep( if( zPath[0]==0 ) return iRoot; if( zPath[0]=='.' ){ x = pParse->aBlob[iRoot]; - if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND; zPath++; if( zPath[0]=='"' ){ zKey = zPath + 1; @@ -3716,7 +3714,6 @@ static u32 jsonLookupBlobStep( if( zPath[i] ){ i++; }else{ - *pzErr = "unterminated \""; return JSON_BLOB_PATHERROR; } testcase( nKey==0 ); @@ -3725,10 +3722,10 @@ static u32 jsonLookupBlobStep( for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; if( nKey==0 ){ - *pzErr = zPath; - return 0; + return JSON_BLOB_PATHERROR; } } + if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); j = iRoot + n; iEnd = j+sz; @@ -3744,7 +3741,7 @@ static u32 jsonLookupBlobStep( if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 || j+n+sz>iEnd ) return JSON_BLOB_ERROR; - return jsonLookupBlobStep(pParse, j, &zPath[i], pzErr); + return jsonLookupBlobStep(pParse, j, &zPath[i]); } j = k+sz; if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; @@ -3778,11 +3775,9 @@ static u32 jsonLookupBlobStep( k -= nn; } if( zPath[i]!=']' ){ - *pzErr = "unterminated ["; return JSON_BLOB_PATHERROR; } }else{ - *pzErr = "bad argument to []"; return JSON_BLOB_PATHERROR; } } @@ -3790,7 +3785,7 @@ static u32 jsonLookupBlobStep( iEnd = j+sz; while( jiEnd ) return JSON_BLOB_ERROR; }else{ - *pzErr = "syntax error"; return JSON_BLOB_PATHERROR; } return JSON_BLOB_NOTFOUND; @@ -4011,7 +4005,6 @@ static void jsonExtractFromBlob( int flags ){ const char *zPath = (const char*)sqlite3_value_text(pPath); - const char *zErr = 0; u32 i = 0; JsonParse px; if( zPath==0 ) return; @@ -4021,7 +4014,7 @@ static void jsonExtractFromBlob( if( px.aBlob==0 ) return; if( zPath[0]=='$' ){ zPath++; - i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + i = jsonLookupBlobStep(&px, 0, zPath); }else if( (flags & JSON_ABPATH) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for @@ -4044,7 +4037,7 @@ static void jsonExtractFromBlob( jsonAppendChar(&jx, 0); zPath = jx.zBuf; } - i = jsonLookupBlobStep(&px, 0, zPath, &zErr); + i = jsonLookupBlobStep(&px, 0, zPath); jsonStringReset(&jx); }else{ sqlite3_result_error(ctx, "bad path", -1); @@ -4056,13 +4049,11 @@ static void jsonExtractFromBlob( return; /* Return NULL if not found */ }else if( i==JSON_BLOB_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); - }else if( i==JSON_BLOB_PATHERROR ){ - char *zMsg = sqlite3_mprintf("in JSON path '%s': %s", - sqlite3_value_text(pPath), zErr); + }else{ + char *zMsg = sqlite3_mprintf("bad path syntax: %s", + sqlite3_value_text(pPath)); sqlite3_result_error(ctx, zMsg, -1); sqlite3_free(zMsg); - }else{ - sqlite3_result_error(ctx, zErr, -1); } } From f26833d7837932c7a3ecc6522e439947e34074e0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 19:05:10 +0000 Subject: [PATCH 061/347] Fix the jsonbChangePayloadSize() routine so that it shifts the payload in order to always render the most compact encoding of the payload size. This is necessary as sometimes (as discovered by dbsqlfuzz) the payload size can grow significantly due to json_insert() or json_replace(). FossilOrigin-Name: 8d6d04ca975ec55c419d40d8463c433b0db698c9fb4812ab9f16d4ee5bee460e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 5ccd6fe084..588048d8e9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\serror\smessages\sreturned\swhen\sthe\s->>\soperator\sfails. -D 2023-10-07T17:50:06.014 +C Fix\sthe\sjsonbChangePayloadSize()\sroutine\sso\sthat\sit\sshifts\sthe\spayload\sin\sorder\nto\salways\srender\sthe\smost\scompact\sencoding\sof\sthe\spayload\ssize.\s\sThis\sis\nnecessary\sas\ssometimes\s(as\sdiscovered\sby\sdbsqlfuzz)\sthe\spayload\ssize\scan\ngrow\ssignificantly\sdue\sto\sjson_insert()\sor\sjson_replace(). +D 2023-10-07T19:05:10.497 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a5d273004e0b0d4b961efe59b403c5b118218b3db2c2d61b4512ea2815ada8b2 +F src/json.c f526f060002c245769018dc5610bb64c981fb41b239b4d21781c47c6f6e83f1c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 358de1b09f3d5ec0fe459775b0a2a99dfa235817327016b472aaa1ed56d952e6 -R 65b1837fdcd2785b2dbe03dd6bcb09bb +P 2f3388f14c843f1c02926e8b929365c06c1f1f4ea6fe6316092c3799c14549d3 +R 9d37d6a70144b585d8728dd14898ad5e U drh -Z f25f60a6b37a3fc4a80bb7878104ebfa +Z 1ec73fd9ef6d1fd16b94f82c874a9e54 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f4e3476f43..76641696e4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2f3388f14c843f1c02926e8b929365c06c1f1f4ea6fe6316092c3799c14549d3 \ No newline at end of file +8d6d04ca975ec55c419d40d8463c433b0db698c9fb4812ab9f16d4ee5bee460e \ No newline at end of file diff --git a/src/json.c b/src/json.c index fac05a0776..6f31214f55 100644 --- a/src/json.c +++ b/src/json.c @@ -2607,24 +2607,55 @@ static void jsonBlobChangePayloadSize( ){ u8 *a; u8 szType; + u8 nExtra; + u8 nNeeded; + i8 delta; if( pParse->oom ) return; a = &pParse->aBlob[i]; szType = a[0]>>4; if( szType<=11 ){ - assert( szPayload<=11 ); + nExtra = 0; + }else if( szType==12 ){ + nExtra = 1; + }else if( szType==13 ){ + nExtra = 2; + }else{ + nExtra = 4; + } + if( szPayload<=11 ){ + nNeeded = 0; + }else if( szPayload<=0xff ){ + nNeeded = 1; + }else if( szPayload<=0xffff ){ + nNeeded = 2; + }else{ + nNeeded = 4; + } + delta = nNeeded - nExtra; + if( delta ){ + u32 newSize = pParse->nBlob + delta; + if( delta>0 ){ + if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ + return; /* OOM error. Error state recorded in pParse->oom. */ + } + a = &pParse->aBlob[i]; + memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); + }else{ + memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta)); + } + pParse->nBlob = newSize; + } + if( nNeeded==0 ){ a[0] = (a[0] & 0x0f) | (szPayload<<4); - }else if( szType==0xc ){ - assert( szPayload<=0xff ); - assert( i+1nBlob ); + }else if( nNeeded==1 ){ + a[0] = (a[0] & 0x0f) | 0xc0; a[1] = szPayload & 0xff; - }else if( szType==0xd ){ - assert( szPayload<=0xffff ); - assert( i+2nBlob ); + }else if( nNeeded==2 ){ + a[0] = (a[0] & 0x0f) | 0xd0; a[1] = (szPayload >> 8) & 0xff; a[2] = szPayload & 0xff; }else{ - assert( szType==0xe ); - assert( i+4nBlob ); + a[0] = (a[0] & 0x0f) | 0xe0; a[1] = (szPayload >> 24) & 0xff; a[2] = (szPayload >> 16) & 0xff; a[3] = (szPayload >> 8) & 0xff; From 3efb2c47918e47c391d04b49cbebe24b31c3464b Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 19:40:20 +0000 Subject: [PATCH 062/347] Improved detection of malformed JSONB when parsing it into a JsonNode array. FossilOrigin-Name: ed99a788415e1f8375bd5ec004dd18b1cd0fae4aa94558170882ca487f6dff93 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 7 +++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 588048d8e9..3e86195bd3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sjsonbChangePayloadSize()\sroutine\sso\sthat\sit\sshifts\sthe\spayload\sin\sorder\nto\salways\srender\sthe\smost\scompact\sencoding\sof\sthe\spayload\ssize.\s\sThis\sis\nnecessary\sas\ssometimes\s(as\sdiscovered\sby\sdbsqlfuzz)\sthe\spayload\ssize\scan\ngrow\ssignificantly\sdue\sto\sjson_insert()\sor\sjson_replace(). -D 2023-10-07T19:05:10.497 +C Improved\sdetection\sof\smalformed\sJSONB\swhen\sparsing\sit\sinto\sa\sJsonNode\sarray. +D 2023-10-07T19:40:20.950 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c f526f060002c245769018dc5610bb64c981fb41b239b4d21781c47c6f6e83f1c +F src/json.c 98ef9894e38f07a5565160b297754937ff3ac2b800241f16a60c6168251d3aa3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 2f3388f14c843f1c02926e8b929365c06c1f1f4ea6fe6316092c3799c14549d3 -R 9d37d6a70144b585d8728dd14898ad5e +P 8d6d04ca975ec55c419d40d8463c433b0db698c9fb4812ab9f16d4ee5bee460e +R 6b6b7362eeaf8bd29175dff0f94e9ac1 U drh -Z 1ec73fd9ef6d1fd16b94f82c874a9e54 +Z 3aae7be717232d09cf2cbfe5dd8cc288 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 76641696e4..46067f0f9c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8d6d04ca975ec55c419d40d8463c433b0db698c9fb4812ab9f16d4ee5bee460e \ No newline at end of file +ed99a788415e1f8375bd5ec004dd18b1cd0fae4aa94558170882ca487f6dff93 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6f31214f55..1cfaaaf58a 100644 --- a/src/json.c +++ b/src/json.c @@ -3481,31 +3481,38 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ zPayload = &pParse->zJson[i+x]; switch( t ){ case JSONB_NULL: { + if( sz>0 ) return -1; jsonParseAddNode(pParse, JSON_NULL, 0, 0); break; } case JSONB_TRUE: { + if( sz>0 ) return -1; jsonParseAddNode(pParse, JSON_TRUE, 0, 0); break; } case JSONB_FALSE: { + if( sz>0 ) return -1; jsonParseAddNode(pParse, JSON_FALSE, 0, 0); break; } case JSONB_INT: { + if( sz==0 ) return -1; jsonParseAddNode(pParse, JSON_INT, sz, zPayload); break; } case JSONB_INT5: { + if( sz==0 ) return -1; pParse->hasNonstd = 1; jsonParseAddNode(pParse, JSON_INT | (JNODE_JSON5<<8), sz, zPayload); break; } case JSONB_FLOAT: { + if( sz==0 ) return -1; jsonParseAddNode(pParse, JSON_REAL, sz, zPayload); break; } case JSONB_FLOAT5: { + if( sz==0 ) return -1; pParse->hasNonstd = 1; jsonParseAddNode(pParse, JSON_REAL | (JNODE_JSON5<<8), sz, zPayload); break; From de8ccf00fbe12a0c4f353224e94412ef0be13090 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 19:46:22 +0000 Subject: [PATCH 063/347] The return from sqlite3_value_blob() in jsonFuncArgMightBeBinary() might be a NULL pointer. Check for that case. FossilOrigin-Name: 7b52b266b066f1385144c1103a3a411306db5f44568366ae1e93cd8cce799bbc --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 3e86195bd3..76e3968de4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sdetection\sof\smalformed\sJSONB\swhen\sparsing\sit\sinto\sa\sJsonNode\sarray. -D 2023-10-07T19:40:20.950 +C The\sreturn\sfrom\ssqlite3_value_blob()\sin\sjsonFuncArgMightBeBinary()\smight\nbe\sa\sNULL\spointer.\s\sCheck\sfor\sthat\scase. +D 2023-10-07T19:46:22.897 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 98ef9894e38f07a5565160b297754937ff3ac2b800241f16a60c6168251d3aa3 +F src/json.c 6b0c502122ad2e306888e2c32eebd8c5b46c36e6b6f3ae1662c81e4a26d48410 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 8d6d04ca975ec55c419d40d8463c433b0db698c9fb4812ab9f16d4ee5bee460e -R 6b6b7362eeaf8bd29175dff0f94e9ac1 +P ed99a788415e1f8375bd5ec004dd18b1cd0fae4aa94558170882ca487f6dff93 +R d7e7cc74e75e3ba90c541936a8c94fd0 U drh -Z 3aae7be717232d09cf2cbfe5dd8cc288 +Z 327a5a9c0b0e46cc41c5ed6d2b8be3ed # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 46067f0f9c..2445478af5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed99a788415e1f8375bd5ec004dd18b1cd0fae4aa94558170882ca487f6dff93 \ No newline at end of file +7b52b266b066f1385144c1103a3a411306db5f44568366ae1e93cd8cce799bbc \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1cfaaaf58a..f2cb796889 100644 --- a/src/json.c +++ b/src/json.c @@ -3451,7 +3451,7 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ nBlob = sqlite3_value_bytes(pJson); if( nBlob<1 ) return 0; aBlob = sqlite3_value_blob(pJson); - if( (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; + if( aBlob==0 || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; memset(&s, 0, sizeof(s)); s.aBlob = (u8*)aBlob; s.nBlob = nBlob; From a7e9386e889151ca062688190468e361a7e7e21d Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 7 Oct 2023 23:35:07 +0000 Subject: [PATCH 064/347] Remove some unnecessary code. Report errors for invalid JSONB input on an extract. FossilOrigin-Name: cbea16c29eb0507f39b5a1cf744a3bb9bb7c71ac156e84a19d03a37cb1816891 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 20 +++++++------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 76e3968de4..1df3b331aa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sreturn\sfrom\ssqlite3_value_blob()\sin\sjsonFuncArgMightBeBinary()\smight\nbe\sa\sNULL\spointer.\s\sCheck\sfor\sthat\scase. -D 2023-10-07T19:46:22.897 +C Remove\ssome\sunnecessary\scode.\s\sReport\serrors\sfor\sinvalid\sJSONB\sinput\son\nan\sextract. +D 2023-10-07T23:35:07.967 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6b0c502122ad2e306888e2c32eebd8c5b46c36e6b6f3ae1662c81e4a26d48410 +F src/json.c e97d03f1c19e403bfe0f0a1deaf50b3e3d657bb8addd5dfe7f9dcf72bcfa3109 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2124,8 +2124,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 ed99a788415e1f8375bd5ec004dd18b1cd0fae4aa94558170882ca487f6dff93 -R d7e7cc74e75e3ba90c541936a8c94fd0 +P 7b52b266b066f1385144c1103a3a411306db5f44568366ae1e93cd8cce799bbc +R 878d86e1e87a30f2fb868ea5f585d29f U drh -Z 327a5a9c0b0e46cc41c5ed6d2b8be3ed +Z 509d9208ad89eff1e0f2ca5c213ed021 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2445478af5..db876805c8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7b52b266b066f1385144c1103a3a411306db5f44568366ae1e93cd8cce799bbc \ No newline at end of file +cbea16c29eb0507f39b5a1cf744a3bb9bb7c71ac156e84a19d03a37cb1816891 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f2cb796889..139ffb4598 100644 --- a/src/json.c +++ b/src/json.c @@ -2800,9 +2800,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - if( pParse->oom==0 ){ - jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); - } + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } @@ -2854,9 +2852,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - if( pParse->oom==0 ){ - jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); - } + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } @@ -3855,12 +3851,7 @@ static void jsonReturnTextJsonFromBlob( x.nBlob = nBlob; jsonStringInit(&s, ctx); jsonRenderBlob(&x, 0, &s); - if( x.nErr ){ - sqlite3_result_error(ctx, "malformed JSON", -1); - jsonStringReset(&s); - }else{ - jsonReturnString(&s); - } + jsonReturnString(&s); } @@ -3903,7 +3894,6 @@ static void jsonReturnFromBlob( int bNeg = 0; char x = (char)pParse->aBlob[i+n]; if( x=='-' && ALWAYS(sz>0) ){ n++; sz--; bNeg = 1; } - else if( x=='+' && ALWAYS(sz>0) ){ n++; sz--; } z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); if( z==0 ) return; rc = sqlite3DecOrHexToI64(z, &iRes); @@ -4031,6 +4021,10 @@ static void jsonReturnFromBlob( } break; } + default: { + sqlite3_result_error(pCtx, "malformed JSON", -1); + break; + } } } From f32aa3434648a03a308d7251d21a3334664e9fcc Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 9 Oct 2023 18:33:01 +0000 Subject: [PATCH 065/347] Systematize the names of some of the translation function in the JSON implementation. FossilOrigin-Name: db44bd1d62084ef69c808f7d07e0a25d5a089dcb8b98b1b5d026777591bbdefc --- manifest | 14 +++--- manifest.uuid | 2 +- src/json.c | 137 ++++++++++++++++++++++++++------------------------ 3 files changed, 78 insertions(+), 75 deletions(-) diff --git a/manifest b/manifest index d523129a6f..3959f0c228 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\sfixes\sand\senhancements\sinto\sthe\sjsonb\sbranch,\sand\nespecially\sthe\sJSON\scache\sspill\sUAF\sfix. -D 2023-10-09T12:57:03.290 +C Systematize\sthe\snames\sof\ssome\sof\sthe\stranslation\sfunction\sin\sthe\sJSON\nimplementation. +D 2023-10-09T18:33:01.568 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -245,7 +245,7 @@ F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java fde5f758ad170ca45ae00b12194c8ba8d8f3090bd64cc3e002dd9c5e7dff8568 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java c0fbfd3779fc92982c7935325a7484dee43eeb80d716989ed31218f453addb94 F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 4cb7fc70efd55583fed6033c34a8719da42975ca97ef4781dda0b9f6cc8ec2e8 -F ext/jni/src/org/sqlite/jni/CApi.java c1dde485a3a3f43c46c8d9c527f9ba5bf303fe0409b2c0de253fb7b6e1055f7e w ext/jni/src/org/sqlite/jni/SQLite3Jni.java +F ext/jni/src/org/sqlite/jni/CApi.java c1dde485a3a3f43c46c8d9c527f9ba5bf303fe0409b2c0de253fb7b6e1055f7e F ext/jni/src/org/sqlite/jni/CallbackProxy.java 064a8a00e4c63cc501c30504f93ca996d422c5f010067f969b2d0a10f0868153 F ext/jni/src/org/sqlite/jni/CollationCallback.java 8cf57cb014a645ecc12609eed17308852a597bc5e83d82a4fdb90f7fadc25f9d F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java 0c62245e000d5db52576c728cac20f6a31f31f5cf40ca4cbcd64b22964e82ae5 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 8717fe7a6461f24ba7b92ccd323c8e2417f44f2a959704c5a05a7aac1ca0df12 +F src/json.c bbf52181d04e09852e273f11cf31544a8030ffc911359cb7f77e39f252cce7a6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2128,8 +2128,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 cbea16c29eb0507f39b5a1cf744a3bb9bb7c71ac156e84a19d03a37cb1816891 a163fecca90cab9d1b7bf8ebac78d498775eed7b6d81e7920e3401633c3a4b60 -R 1dd322f61ae3b783d06705b01094df38 +P 9422c24f4a8b290dcae61e50ec81be5b314b22c61a2bca1e194e47da1316b6e6 +R 3ec6096d3289810ffc9770092e31c712 U drh -Z 41e410a665a938a0454df231f95bd4b4 +Z 411045c6779d7c6439ce2a3295f7e71a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e2032820b2..533952e38f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9422c24f4a8b290dcae61e50ec81be5b314b22c61a2bca1e194e47da1316b6e6 \ No newline at end of file +db44bd1d62084ef69c808f7d07e0a25d5a089dcb8b98b1b5d026777591bbdefc \ No newline at end of file diff --git a/src/json.c b/src/json.c index d4367324d8..c2b7e39225 100644 --- a/src/json.c +++ b/src/json.c @@ -337,7 +337,7 @@ struct JsonParse { u32 iErr; /* Error location in zJson[] */ u32 iSubst; /* Last JSON_SUBST entry in aNode[] */ u32 iHold; /* Age of this entry in the cache for LRU replacement */ - /* Binary format */ + /* Storage for the binary JSONB format */ u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[] */ u8 *aBlob; /* BLOB representation of zJson */ @@ -356,12 +356,12 @@ struct JsonParse { ** Forward references **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); -static void jsonRenderNodeAsBlob(JsonParse*,JsonNode*,JsonParse*); +static void jsonXlateNodeToBlob(JsonParse*,JsonNode*,JsonParse*); static int jsonParseAddNode(JsonParse*,u32,u32,const char*); -static int jsonParseValueFromBlob(JsonParse *pParse, u32 i); +static int jsonXlateBlobToNode(JsonParse *pParse, u32 i); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); -static u32 jsonRenderBlob(JsonParse*,u32,JsonString*); +static u32 jsonXlateBlobToText(JsonParse*,u32,JsonString*); /************************************************************************** ** Utility routines for dealing with JsonString objects @@ -735,7 +735,7 @@ static void jsonAppendSqlValue( memset(&px, 0, sizeof(px)); px.aBlob = (u8*)sqlite3_value_blob(pValue); px.nBlob = sqlite3_value_bytes(pValue); - jsonRenderBlob(&px, 0, p); + jsonXlateBlobToText(&px, 0, p); }else if( p->eErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); p->eErr = JSTRING_ERR; @@ -868,11 +868,10 @@ static int jsonParseAddCleanup( } /* -** Convert the JsonNode pNode into a pure JSON string and -** append to pOut. Subsubstructure is also included. Return -** the number of JsonNode objects that are encoded. +** Translate the JsonNode pNode into a pure JSON string and +** append that string on pOut. Subsubstructure is also included. */ -static void jsonRenderNodeAsText( +static void jsonXlateNodeToText( JsonParse *pParse, /* the complete parse of the JSON */ JsonNode *pNode, /* The node to render */ JsonString *pOut /* Write JSON here */ @@ -947,7 +946,7 @@ static void jsonRenderNodeAsText( while( j<=pNode->n ){ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNodeAsText(pParse, &pNode[j], pOut); + jsonXlateNodeToText(pParse, &pNode[j], pOut); } j += jsonNodeSize(&pNode[j]); } @@ -967,9 +966,9 @@ static void jsonRenderNodeAsText( while( j<=pNode->n ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); - jsonRenderNodeAsText(pParse, &pNode[j], pOut); + jsonXlateNodeToText(pParse, &pNode[j], pOut); jsonAppendChar(pOut, ':'); - jsonRenderNodeAsText(pParse, &pNode[j+1], pOut); + jsonXlateNodeToText(pParse, &pNode[j+1], pOut); } j += 1 + jsonNodeSize(&pNode[j+1]); } @@ -1012,7 +1011,7 @@ static void jsonReturnNodeAsJson( if( flags & JSON_BLOB ){ JsonParse x; memset(&x, 0, sizeof(x)); - jsonRenderNodeAsBlob(pParse, pNode, &x); + jsonXlateNodeToBlob(pParse, pNode, &x); if( x.oom ){ sqlite3_result_error_nomem(pCtx); sqlite3_free(x.aBlob); @@ -1021,7 +1020,7 @@ static void jsonReturnNodeAsJson( } }else{ jsonStringInit(&s, pCtx); - jsonRenderNodeAsText(pParse, pNode, &s); + jsonXlateNodeToText(pParse, pNode, &s); if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ pParse->zAlt = sqlite3RCStrRef(s.zBuf); pParse->nAlt = s.nUsed; @@ -1072,7 +1071,7 @@ static u32 jsonHexToInt4(const char *z){ ** value returned is either RFC-8259 JSON text or a BLOB in the JSONB ** format, depending on the JSON_BLOB flag of the function user-data. */ -static void jsonReturnNodeAsSql( +static void jsonReturnFromNode( JsonParse *pParse, /* Complete JSON parse tree */ JsonNode *pNode, /* Node to return */ sqlite3_context *pCtx /* Return value for this function */ @@ -1510,8 +1509,11 @@ static const struct NanInfName { }; /* -** Parse a single JSON value which begins at pParse->zJson[i]. Return the -** index of the first character past the end of the value parsed. +** Translate a single element of JSON text beginning at pParse->zJson[i] into +** its JsonNode representation. Append the translation onto the +** pParse->aNode[] array, which is increased in size as necessary. +** Return the pJson->zJson[] index of the first character past the end of +** the element that was parsed. ** ** Special return values: ** @@ -1522,7 +1524,7 @@ static const struct NanInfName { ** -4 ',' seen / the index in zJson[] of the seen character ** -5 ':' seen / */ -static int jsonParseValueFromText(JsonParse *pParse, u32 i){ +static int jsonXlateTextToNode(JsonParse *pParse, u32 i){ char c; u32 j; int iThis; @@ -1541,7 +1543,7 @@ json_parse_restart: } for(j=i+1;;j++){ u32 nNode = pParse->nNode; - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x<=0 ){ if( x==(-2) ){ j = pParse->iErr; @@ -1584,7 +1586,7 @@ json_parse_restart: goto parse_object_value; } } - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -1592,7 +1594,7 @@ json_parse_restart: j = pParse->iErr+1; } parse_object_value: - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -1611,7 +1613,7 @@ json_parse_restart: break; } } - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -1640,7 +1642,7 @@ json_parse_restart: } memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; @@ -1664,7 +1666,7 @@ json_parse_restart: break; } } - x = jsonParseValueFromText(pParse, j); + x = jsonXlateTextToNode(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -2004,9 +2006,9 @@ static int jsonParse( if( pParse->isBinary ){ pParse->aBlob = (u8*)pParse->zJson; pParse->nBlob = pParse->nJson; - i = jsonParseValueFromBlob(pParse, 0); + i = jsonXlateBlobToNode(pParse, 0); }else{ - i = jsonParseValueFromText(pParse, 0); + i = jsonXlateTextToNode(pParse, 0); } if( pParse->oom ) i = -1; if( !pParse->isBinary && i>0 ){ @@ -2679,12 +2681,12 @@ static int jsonIs4HexB(const char *z, int *pOp){ } /* -** Parse a single JSON text value which begins at pParse->zJson[i] into -** its equivalent BLOB representation in pParse->aBlob[]. The parse is -** appended to pParse->aBlob[] beginning at pParse->nBlob. The size of +** Translate a single element of JSON text at pParse->zJson[i] into +** its equivalent binary JSONB representation. Append the translation into +** pParse->aBlob[] beginning at pParse->nBlob. The size of ** pParse->aBlob[] is increased as necessary. ** -** Return the index of the first character past the end of the value parsed, +** Return the index of the first character past the end of the element parsed, ** or one of the following special result codes: ** ** 0 End of input @@ -2694,7 +2696,7 @@ static int jsonIs4HexB(const char *z, int *pOp){ ** -4 ',' seen / the index in zJson[] of the seen character ** -5 ':' seen / */ -static int jsonTranslateTextValueToBlob(JsonParse *pParse, u32 i){ +static int jsonXlateTextToBlob(JsonParse *pParse, u32 i){ char c; u32 j; u32 iThis, iStart; @@ -2714,7 +2716,7 @@ json_parse_restart: iStart = pParse->nBlob; for(j=i+1;;j++){ u32 iBlob = pParse->nBlob; - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x<=0 ){ int op; if( x==(-2) ){ @@ -2760,7 +2762,7 @@ json_parse_restart: goto parse_object_value; } } - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -2768,7 +2770,7 @@ json_parse_restart: j = pParse->iErr+1; } parse_object_value: - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -2787,7 +2789,7 @@ json_parse_restart: break; } } - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -2815,7 +2817,7 @@ json_parse_restart: return -1; } for(j=i+1;;j++){ - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; @@ -2839,7 +2841,7 @@ json_parse_restart: break; } } - x = jsonTranslateTextValueToBlob(pParse, j); + x = jsonXlateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -3156,7 +3158,7 @@ static int jsonConvertTextToBlob( ){ int i; const char *zJson = pParse->zJson; - i = jsonTranslateTextValueToBlob(pParse, 0); + i = jsonXlateTextToBlob(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); @@ -3194,7 +3196,7 @@ static void jsonReturnStringAsBlob(JsonString *pStr){ memset(&px, 0, sizeof(px)); px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; - (void)jsonTranslateTextValueToBlob(&px, 0); + (void)jsonXlateTextToBlob(&px, 0); if( px.oom ){ sqlite3_free(px.aBlob); sqlite3_result_error_nomem(pStr->pCtx); @@ -3253,9 +3255,10 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ /* -** Convert the binary BLOB representation of JSON beginning at -** aBlob[0] and extending for no more than nBlob bytes into -** a pure JSON string. The string is appended to pOut. +** Translate the binary JSONB representation of JSON beginning at +** pParse->aBlob[i] into a JSON text string. Append the JSON +** text onto the end of pOut. Return the index in pParse->aBlob[] +** of the first byte past the end of the element that is translated. ** ** If an error is detected in the BLOB input, the pOut->eErr flag ** might get set to JSTRING_MALFORMED. But not all BLOB input errors @@ -3264,7 +3267,7 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ ** ** The pOut->eErr JSTRING_OOM flag is set on a OOM. */ -static u32 jsonRenderBlob( +static u32 jsonXlateBlobToText( JsonParse *pParse, /* the complete parse of the JSON */ u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ @@ -3403,7 +3406,7 @@ static u32 jsonRenderBlob( j = i+n; iEnd = j+sz; while( j0 ) pOut->nUsed--; @@ -3416,7 +3419,7 @@ static u32 jsonRenderBlob( j = i+n; iEnd = j+sz; while( j0 ) pOut->nUsed--; @@ -3458,14 +3461,13 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ return sz+n==(u32)nBlob; } -/* Parse a single element of binary JSON into the legacy Node structure. -** Return the index of the first byte past the end of the binary JSON element. -** -** This routine is a temporary translator while the legacy Node encoding -** is still in use. It will be removed after all processing translates -** to the new BLOB encoding. +/* Translate a single element of JSONB into the JsonNode format. The +** first byte of the element to be translated is at pParse->aBlob[i]. +** Return the index in pParse->aBlob[] of the first byte past the end +** of the JSONB element. Append the JsonNode translation in +** pParse->aNode[], which is increased in size as necessary. */ -static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ +static int jsonXlateBlobToNode(JsonParse *pParse, u32 i){ u8 t; /* Node type */ u32 sz; /* Node size */ u32 x; /* Index of payload start */ @@ -3535,7 +3537,7 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); u32 j = i+x; while( joom ){ pParse->aNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; @@ -3569,10 +3571,11 @@ static int jsonParseValueFromBlob(JsonParse *pParse, u32 i){ } /* -** Convert pNode that belongs to pParse into the BLOB representation. -** The BLOB representation is added to the pOut->aBlob[] array. +** Translate pNode (which is always a node found in pParse->aNode[]) into +** the JSONB representation and append the translation onto the end of the +** pOut->aBlob[] array. */ -static void jsonRenderNodeAsBlob( +static void jsonXlateNodeToBlob( JsonParse *pParse, /* the complete parse of the JSON */ JsonNode *pNode, /* The node to render */ JsonParse *pOut /* Write the BLOB rendering of JSON here */ @@ -3661,7 +3664,7 @@ static void jsonRenderNodeAsBlob( for(;;){ while( j<=pNode->n ){ if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); + jsonXlateNodeToBlob(pParse, &pNode[j], pOut); } j += jsonNodeSize(&pNode[j]); } @@ -3682,8 +3685,8 @@ static void jsonRenderNodeAsBlob( for(;;){ while( j<=pNode->n ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonRenderNodeAsBlob(pParse, &pNode[j], pOut); - jsonRenderNodeAsBlob(pParse, &pNode[j+1], pOut); + jsonXlateNodeToBlob(pParse, &pNode[j], pOut); + jsonXlateNodeToBlob(pParse, &pNode[j+1], pOut); } j += 1 + jsonNodeSize(&pNode[j+1]); } @@ -3850,7 +3853,7 @@ static void jsonReturnTextJsonFromBlob( x.aBlob = (u8*)aBlob; x.nBlob = nBlob; jsonStringInit(&s, ctx); - jsonRenderBlob(&x, 0, &s); + jsonXlateBlobToText(&x, 0, &s); jsonReturnString(&s); } @@ -4431,13 +4434,13 @@ static void jsonExtractFunc( if( flags & JSON_JSON ){ jsonReturnNodeAsJson(p, pNode, ctx, 0); }else{ - jsonReturnNodeAsSql(p, pNode, ctx); + jsonReturnFromNode(p, pNode, ctx); sqlite3_result_subtype(ctx, 0); } } }else{ pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturnNodeAsSql(p, pNode, ctx); + if( p->nErr==0 && pNode ) jsonReturnFromNode(p, pNode, ctx); } }else{ /* Two or more PATH arguments results in a JSON array with each @@ -4451,7 +4454,7 @@ static void jsonExtractFunc( if( p->nErr ) break; jsonAppendSeparator(&jx); if( pNode ){ - jsonRenderNodeAsText(p, pNode, &jx); + jsonXlateNodeToText(p, pNode, &jx); }else{ jsonAppendRawNZ(&jx, "null", 4); } @@ -5379,7 +5382,7 @@ static int jsonEachColumn( case JEACH_KEY: { if( p->i==0 ) break; if( p->eType==JSON_OBJECT ){ - jsonReturnNodeAsSql(&p->sParse, pThis, ctx); + jsonReturnFromNode(&p->sParse, pThis, ctx); }else if( p->eType==JSON_ARRAY ){ u32 iKey; if( p->bRecursive ){ @@ -5395,7 +5398,7 @@ static int jsonEachColumn( } case JEACH_VALUE: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturnNodeAsSql(&p->sParse, pThis, ctx); + jsonReturnFromNode(&p->sParse, pThis, ctx); break; } case JEACH_TYPE: { @@ -5406,7 +5409,7 @@ static int jsonEachColumn( case JEACH_ATOM: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->eType>=JSON_ARRAY ) break; - jsonReturnNodeAsSql(&p->sParse, pThis, ctx); + jsonReturnFromNode(&p->sParse, pThis, ctx); break; } case JEACH_ID: { From 064c1688dade8ba7b67e9bf43833d3f799b5ad56 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 10 Oct 2023 18:04:40 +0000 Subject: [PATCH 066/347] Fix bugs uncovered by the fuzzer. FossilOrigin-Name: c96eb7fb618dc0a5aeec8a5e85076475b77dcd56309438aba1f9bddfc8921e3c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 15 +++++++++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 592f7c39e5..051af18412 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-10-10T17:34:41.118 +C Fix\sbugs\suncovered\sby\sthe\sfuzzer. +D 2023-10-10T18:04:40.744 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c bbf52181d04e09852e273f11cf31544a8030ffc911359cb7f77e39f252cce7a6 +F src/json.c 7df84bb5b918dca3e9c4361989a3667bf1ca080299fdf9947e6f99b39d7523af F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 db44bd1d62084ef69c808f7d07e0a25d5a089dcb8b98b1b5d026777591bbdefc 65ccf5fef812d43aed9e00af36c90e1a499d197e30148753790445e25ee1324c -R 7120be3e2682c2f2dc1a6290819c2e25 +P f93f16c94d0a58dfa90939e84aba048be958c7b085b45a6f25c2243fdf79b3aa +R 77f1fc6f77b161facff6daa6dc651655 U drh -Z a077701d7ef896cae6a827ec97befa72 +Z 2b3feb335f873a4bcfbf7b38cfcaa6e3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 53e25911e6..abbf08e743 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f93f16c94d0a58dfa90939e84aba048be958c7b085b45a6f25c2243fdf79b3aa \ No newline at end of file +c96eb7fb618dc0a5aeec8a5e85076475b77dcd56309438aba1f9bddfc8921e3c \ No newline at end of file diff --git a/src/json.c b/src/json.c index c2b7e39225..cf622899fa 100644 --- a/src/json.c +++ b/src/json.c @@ -2861,6 +2861,7 @@ json_parse_restart: case '\'': { u8 opcode; char cDelim; + int nn; pParse->hasNonstd = 1; opcode = JSONB_TEXT; goto parse_string; @@ -2869,7 +2870,8 @@ json_parse_restart: opcode = JSONB_TEXT; parse_string: cDelim = z[i]; - for(j=i+1; 1; j++){ + nn = pParse->nJson; + for(j=i+1; j0 ){ jsonAppendRawNZ(pOut, zIn, k); + if( sz2<=k ) break; zIn += k; sz2 -= k; - if( sz2==0 ) break; } assert( zIn[0]=='\\' ); switch( (u8)zIn[1] ){ @@ -3366,8 +3368,12 @@ static u32 jsonXlateBlobToText( case 'x': jsonAppendRawNZ(pOut, "\\u00", 4); jsonAppendRawNZ(pOut, &zIn[2], 2); - zIn += 2; - sz2 -= 2; + if( sz2<2 ){ + sz2 = 0; + }else{ + zIn += 2; + sz2 -= 2; + } break; case '0': jsonAppendRawNZ(pOut, "\\u0000", 6); @@ -3391,6 +3397,7 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, zIn, 2); break; } + if( sz2<2 ) break; zIn += 2; sz2 -= 2; } From d88f378c7d316a9523485504b31705ca1040e2fe Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 10 Oct 2023 18:32:14 +0000 Subject: [PATCH 067/347] Fix a potential buffer overrun due to corrupt JSONB. FossilOrigin-Name: 5cbb861fc6cb7421a81c957ba9d576e0ae92cb4ed725cdb539b364d66f4ee145 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 051af18412..c31090bbce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sbugs\suncovered\sby\sthe\sfuzzer. -D 2023-10-10T18:04:40.744 +C Fix\sa\spotential\sbuffer\soverrun\sdue\sto\scorrupt\sJSONB. +D 2023-10-10T18:32:14.227 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7df84bb5b918dca3e9c4361989a3667bf1ca080299fdf9947e6f99b39d7523af +F src/json.c e6e3641f54366634dc10dfb3684c3643821883b533f89c207b33dd2d23499c7d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 f93f16c94d0a58dfa90939e84aba048be958c7b085b45a6f25c2243fdf79b3aa -R 77f1fc6f77b161facff6daa6dc651655 +P c96eb7fb618dc0a5aeec8a5e85076475b77dcd56309438aba1f9bddfc8921e3c +R 24c9be5529f36a58a40f05b718ffa50e U drh -Z 2b3feb335f873a4bcfbf7b38cfcaa6e3 +Z 1fee0a4053cb04fe5ff07cf374f7fcdd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index abbf08e743..587d87d872 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c96eb7fb618dc0a5aeec8a5e85076475b77dcd56309438aba1f9bddfc8921e3c \ No newline at end of file +5cbb861fc6cb7421a81c957ba9d576e0ae92cb4ed725cdb539b364d66f4ee145 \ No newline at end of file diff --git a/src/json.c b/src/json.c index cf622899fa..2d68aef113 100644 --- a/src/json.c +++ b/src/json.c @@ -1124,7 +1124,7 @@ static void jsonReturnFromNode( assert( pNode->eU==1 ); to_double: z = pNode->u.zJContent; - sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); + sqlite3AtoF(z, &r, pNode->n, SQLITE_UTF8); sqlite3_result_double(pCtx, r); break; } From 59ded6b5f112793ff67957b731ab26e2eefa6dd8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 10 Oct 2023 18:42:08 +0000 Subject: [PATCH 068/347] Improved robustness when translating corrupt JSONB into JSON text. FossilOrigin-Name: 0caa320d9099adbaf98e3719003dbdc4d158abcb3d8a1af20fbcd4c08c970f4a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 22 +++++++++++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index c31090bbce..ec6701a0b7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\spotential\sbuffer\soverrun\sdue\sto\scorrupt\sJSONB. -D 2023-10-10T18:32:14.227 +C Improved\srobustness\swhen\stranslating\scorrupt\sJSONB\sinto\sJSON\stext. +D 2023-10-10T18:42:08.681 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c e6e3641f54366634dc10dfb3684c3643821883b533f89c207b33dd2d23499c7d +F src/json.c fc5b17b021ce7699e08b8709698701d3de4128c07af53295ae1b241c38de7662 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 c96eb7fb618dc0a5aeec8a5e85076475b77dcd56309438aba1f9bddfc8921e3c -R 24c9be5529f36a58a40f05b718ffa50e +P 5cbb861fc6cb7421a81c957ba9d576e0ae92cb4ed725cdb539b364d66f4ee145 +R 56495ee1952f6b69fc5e0b7ca9cd10db U drh -Z 1fee0a4053cb04fe5ff07cf374f7fcdd +Z 53d35a2e8ad88bb30ea85a59c02aea4e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 587d87d872..fdb11253ec 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5cbb861fc6cb7421a81c957ba9d576e0ae92cb4ed725cdb539b364d66f4ee145 \ No newline at end of file +0caa320d9099adbaf98e3719003dbdc4d158abcb3d8a1af20fbcd4c08c970f4a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 2d68aef113..a8f0049e7d 100644 --- a/src/json.c +++ b/src/json.c @@ -3353,7 +3353,10 @@ static u32 jsonXlateBlobToText( for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); - if( sz2<=k ) break; + if( sz2<=k ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } zIn += k; sz2 -= k; } @@ -3369,6 +3372,7 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, "\\u00", 4); jsonAppendRawNZ(pOut, &zIn[2], 2); if( sz2<2 ){ + pOut->eErr |= JSTRING_MALFORMED; sz2 = 0; }else{ zIn += 2; @@ -3387,9 +3391,14 @@ static u32 jsonXlateBlobToText( case '\n': break; case 0xe2: - assert( sz2>=4 ); - assert( 0x80==(u8)zIn[2] ); - assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); + if( sz2<4 + || 0x80!=(u8)zIn[2] + || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) + ){ + pOut->eErr |= JSTRING_MALFORMED; + k = sz2; + break; + } zIn += 2; sz2 -= 2; break; @@ -3397,7 +3406,10 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, zIn, 2); break; } - if( sz2<2 ) break; + if( sz2<2 ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } zIn += 2; sz2 -= 2; } From 6b7b23c5802128205cfcc2c02a38a9e1513a06c6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 10 Oct 2023 18:55:29 +0000 Subject: [PATCH 069/347] Fix an off-by-one error in the changes from the previous check-in on the jsonb branch. FossilOrigin-Name: 216191b113da43516d31301fb133173add66358d503677ab997bc82cc88bfea4 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ec6701a0b7..8b71972a22 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\srobustness\swhen\stranslating\scorrupt\sJSONB\sinto\sJSON\stext. -D 2023-10-10T18:42:08.681 +C Fix\san\soff-by-one\serror\sin\sthe\schanges\sfrom\sthe\sprevious\scheck-in\son\nthe\sjsonb\sbranch. +D 2023-10-10T18:55:29.425 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fc5b17b021ce7699e08b8709698701d3de4128c07af53295ae1b241c38de7662 +F src/json.c 3a0d5a277dfe592998f803b166f2bcd761259643446c4ef3b58f662f4e6e121a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 5cbb861fc6cb7421a81c957ba9d576e0ae92cb4ed725cdb539b364d66f4ee145 -R 56495ee1952f6b69fc5e0b7ca9cd10db +P 0caa320d9099adbaf98e3719003dbdc4d158abcb3d8a1af20fbcd4c08c970f4a +R 2be00e287cc1d614a9c2f8c8c8493433 U drh -Z 53d35a2e8ad88bb30ea85a59c02aea4e +Z f13f9b96095bfab368044d04f5c134b6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fdb11253ec..a728066e52 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0caa320d9099adbaf98e3719003dbdc4d158abcb3d8a1af20fbcd4c08c970f4a \ No newline at end of file +216191b113da43516d31301fb133173add66358d503677ab997bc82cc88bfea4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index a8f0049e7d..b9f13f12f1 100644 --- a/src/json.c +++ b/src/json.c @@ -3354,7 +3354,7 @@ static u32 jsonXlateBlobToText( if( k>0 ){ jsonAppendRawNZ(pOut, zIn, k); if( sz2<=k ){ - pOut->eErr |= JSTRING_MALFORMED; + if( sz2eErr |= JSTRING_MALFORMED; break; } zIn += k; From 71dc3c714b1aaabdae58391584faf790a0b5c86c Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 10 Oct 2023 23:02:31 +0000 Subject: [PATCH 070/347] Improved robustness in the decoding of JSON5 text escape sequences found in malformed JSONB. FossilOrigin-Name: 35e0108af2bdd830375c31c525f8ed0e8df64959d89649a88402dc1a5c376612 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 47 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 8b71972a22..1d877db3b8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\soff-by-one\serror\sin\sthe\schanges\sfrom\sthe\sprevious\scheck-in\son\nthe\sjsonb\sbranch. -D 2023-10-10T18:55:29.425 +C Improved\srobustness\sin\sthe\sdecoding\sof\sJSON5\stext\sescape\ssequences\sfound\nin\smalformed\sJSONB. +D 2023-10-10T23:02:31.182 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 3a0d5a277dfe592998f803b166f2bcd761259643446c4ef3b58f662f4e6e121a +F src/json.c d86e0d27c8d99c4a9e9bbe44401224270d667118742df371a3928e0406243647 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 0caa320d9099adbaf98e3719003dbdc4d158abcb3d8a1af20fbcd4c08c970f4a -R 2be00e287cc1d614a9c2f8c8c8493433 +P 216191b113da43516d31301fb133173add66358d503677ab997bc82cc88bfea4 +R 3e258cb31e76158544ba210b63aec864 U drh -Z f13f9b96095bfab368044d04f5c134b6 +Z 26ce59215821be9c842a531e0d11f121 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a728066e52..37779a76ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -216191b113da43516d31301fb133173add66358d503677ab997bc82cc88bfea4 \ No newline at end of file +35e0108af2bdd830375c31c525f8ed0e8df64959d89649a88402dc1a5c376612 \ No newline at end of file diff --git a/src/json.c b/src/json.c index b9f13f12f1..8f263556a7 100644 --- a/src/json.c +++ b/src/json.c @@ -577,9 +577,13 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ for(i=0; i0 ){ jsonAppendRawNZ(p, zIn, i); + if( i>=N ) break; zIn += i; N -= i; - if( N==0 ) break; + } + if( N<2 ){ + p->eErr |= JSTRING_MALFORMED; + break; } assert( zIn[0]=='\\' ); switch( (u8)zIn[1] ){ @@ -590,6 +594,11 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ jsonAppendRawNZ(p, "\\u0009", 6); break; case 'x': + if( N<4 ){ + N = 2; + p->eErr |= JSTRING_MALFORMED; + break; + } jsonAppendRawNZ(p, "\\u00", 4); jsonAppendRawNZ(p, &zIn[2], 2); zIn += 2; @@ -599,14 +608,22 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ jsonAppendRawNZ(p, "\\u0000", 6); break; case '\r': - if( zIn[2]=='\n' ){ + if( N>2 && zIn[2]=='\n' ){ zIn++; N--; } break; case '\n': break; - case 0xe2: + case 0xe2: /* \ followed by U+2028 or U+2029 line terminator ignored */ + if( N<4 + || 0x80!=(u8)zIn[2] + || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) + ){ + N = 2; + p->eErr |= JSTRING_MALFORMED; + break; + } assert( N>=4 ); assert( 0x80==(u8)zIn[2] ); assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); @@ -617,6 +634,7 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ jsonAppendRawNZ(p, zIn, 2); break; } + assert( N>=2 ); zIn += 2; N -= 2; } @@ -3353,13 +3371,16 @@ static u32 jsonXlateBlobToText( for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); - if( sz2<=k ){ - if( sz2eErr |= JSTRING_MALFORMED; + if( k>=sz2 ){ break; } zIn += k; sz2 -= k; } + if( sz2<2 ){ + if( sz2>0 ) pOut->eErr |= JSTRING_MALFORMED; + if( sz2==0 ) break; + } assert( zIn[0]=='\\' ); switch( (u8)zIn[1] ){ case '\'': @@ -3369,21 +3390,21 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, "\\u0009", 6); break; case 'x': - jsonAppendRawNZ(pOut, "\\u00", 4); - jsonAppendRawNZ(pOut, &zIn[2], 2); if( sz2<2 ){ pOut->eErr |= JSTRING_MALFORMED; sz2 = 0; - }else{ - zIn += 2; - sz2 -= 2; + break; } + jsonAppendRawNZ(pOut, "\\u00", 4); + jsonAppendRawNZ(pOut, &zIn[2], 2); + zIn += 2; + sz2 -= 2; break; case '0': jsonAppendRawNZ(pOut, "\\u0000", 6); break; case '\r': - if( zIn[2]=='\n' ){ + if( sz2>2 && zIn[2]=='\n' ){ zIn++; sz2--; } @@ -3391,6 +3412,9 @@ static u32 jsonXlateBlobToText( case '\n': break; case 0xe2: + /* '\' followed by either U+2028 or U+2029 is ignored as + ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29. + ** U+2029 is the same except for the last byte */ if( sz2<4 || 0x80!=(u8)zIn[2] || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) @@ -3407,6 +3431,7 @@ static u32 jsonXlateBlobToText( break; } if( sz2<2 ){ + sz2 = 0; pOut->eErr |= JSTRING_MALFORMED; break; } From 2a96a1584d58a9959e1f4867de25682793637837 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 11 Oct 2023 11:42:06 +0000 Subject: [PATCH 071/347] Fix an assertion fault in json_patch() that can occur if the patch JSON is malformed JSONB. Report the malformed JSONB instead. FossilOrigin-Name: a72d54645ca0dd80c60a5ed586049dead3ea7f5fa9ad05c6610a506242a7032a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 ++++++++++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 1d877db3b8..6f76814908 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\srobustness\sin\sthe\sdecoding\sof\sJSON5\stext\sescape\ssequences\sfound\nin\smalformed\sJSONB. -D 2023-10-10T23:02:31.182 +C Fix\san\sassertion\sfault\sin\sjson_patch()\sthat\scan\soccur\sif\sthe\spatch\sJSON\nis\smalformed\sJSONB.\s\sReport\sthe\smalformed\sJSONB\sinstead. +D 2023-10-11T11:42:06.265 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d86e0d27c8d99c4a9e9bbe44401224270d667118742df371a3928e0406243647 +F src/json.c 51ae066d271f4a37643921782fbcf61cf76d7166d06e9ad4ab75a32510d8b03a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 216191b113da43516d31301fb133173add66358d503677ab997bc82cc88bfea4 -R 3e258cb31e76158544ba210b63aec864 +P 35e0108af2bdd830375c31c525f8ed0e8df64959d89649a88402dc1a5c376612 +R 574c458e7556ba233ae1bd2b81029222 U drh -Z 26ce59215821be9c842a531e0d11f121 +Z b70fdf7e40754bcaf3921ae744996dea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 37779a76ba..c04985ac0f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -35e0108af2bdd830375c31c525f8ed0e8df64959d89649a88402dc1a5c376612 \ No newline at end of file +a72d54645ca0dd80c60a5ed586049dead3ea7f5fa9ad05c6610a506242a7032a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8f263556a7..0ddc9c498e 100644 --- a/src/json.c +++ b/src/json.c @@ -4536,8 +4536,10 @@ static JsonNode *jsonMergePatch( for(i=1; in; i += jsonNodeSize(&pPatch[i+1])+1){ u32 nKey; const char *zKey; - assert( pPatch[i].eType==JSON_STRING ); - assert( pPatch[i].jnFlags & JNODE_LABEL ); + if( pPatch[i].eType!=JSON_STRING ){ + pParse->nErr = 1; + return 0; + } assert( pPatch[i].eU==1 ); nKey = pPatch[i].n; zKey = pPatch[i].u.zJContent; @@ -4606,13 +4608,15 @@ static void jsonPatchFunc( pX->useMod = 1; pY->useMod = 1; pResult = jsonMergePatch(pX, 0, pY->aNode); - assert( pResult!=0 || pX->oom ); - if( pResult && pX->oom==0 ){ + assert( pResult!=0 || pX->oom || pX->nErr ); + if( pX->oom ){ + sqlite3_result_error_nomem(ctx); + }else if( pX->nErr ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + }else if( pResult ){ jsonDebugPrintParse(pX); jsonDebugPrintNode(pResult); jsonReturnNodeAsJson(pX, pResult, ctx, 0); - }else{ - sqlite3_result_error_nomem(ctx); } } From 33b56217cb7f1ced7553449daccc032230a8b53a Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 11 Oct 2023 12:21:29 +0000 Subject: [PATCH 072/347] Improved robustness against corrupt JSONB. FossilOrigin-Name: 0fbda92bb0eeb40f95c83f717e4e8f5bff1ac82f1c899e9f6d400d67df67214e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 8 ++------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 6f76814908..c5837f11b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sassertion\sfault\sin\sjson_patch()\sthat\scan\soccur\sif\sthe\spatch\sJSON\nis\smalformed\sJSONB.\s\sReport\sthe\smalformed\sJSONB\sinstead. -D 2023-10-11T11:42:06.265 +C Improved\srobustness\sagainst\scorrupt\sJSONB. +D 2023-10-11T12:21:29.280 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 51ae066d271f4a37643921782fbcf61cf76d7166d06e9ad4ab75a32510d8b03a +F src/json.c 4130133dcd43ac5c9b71a88a47f5ba8b6453d7cfbf158ebeb57ddd89f5def919 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 35e0108af2bdd830375c31c525f8ed0e8df64959d89649a88402dc1a5c376612 -R 574c458e7556ba233ae1bd2b81029222 +P a72d54645ca0dd80c60a5ed586049dead3ea7f5fa9ad05c6610a506242a7032a +R e706082c7e7e2d4bd83182da6946ab5f U drh -Z b70fdf7e40754bcaf3921ae744996dea +Z 820cb01f77053065d828d065556f10f5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c04985ac0f..aa52b1f5c1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a72d54645ca0dd80c60a5ed586049dead3ea7f5fa9ad05c6610a506242a7032a \ No newline at end of file +0fbda92bb0eeb40f95c83f717e4e8f5bff1ac82f1c899e9f6d400d67df67214e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0ddc9c498e..ff735f36f3 100644 --- a/src/json.c +++ b/src/json.c @@ -981,7 +981,7 @@ static void jsonXlateNodeToText( u32 j = 1; jsonAppendChar(pOut, '{'); for(;;){ - while( j<=pNode->n ){ + while( jn ){ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ jsonAppendSeparator(pOut); jsonXlateNodeToText(pParse, &pNode[j], pOut); @@ -1054,7 +1054,7 @@ static void jsonReturnNodeAsJson( ** character: 0..9a..fA..F */ static u8 jsonHexToInt(int h){ - assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); + if( !sqlite3Isxdigit(h) ) return 0; #ifdef SQLITE_EBCDIC h += 9*(1&~(h>>4)); #else @@ -1068,10 +1068,6 @@ static u8 jsonHexToInt(int h){ */ static u32 jsonHexToInt4(const char *z){ u32 v; - assert( sqlite3Isxdigit(z[0]) ); - assert( sqlite3Isxdigit(z[1]) ); - assert( sqlite3Isxdigit(z[2]) ); - assert( sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) From 2b7a1f5926278c2698521310d29f39ef20b90789 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 11 Oct 2023 12:44:17 +0000 Subject: [PATCH 073/347] Fix an issue that can arise when processing corrupt JSONB. FossilOrigin-Name: e50045c22296be84c6bea82bb8b310f07bca820c84d4a7349b16da0cf5d2657c --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index c5837f11b5..9d433f7b1d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\srobustness\sagainst\scorrupt\sJSONB. -D 2023-10-11T12:21:29.280 +C Fix\san\sissue\sthat\scan\sarise\swhen\sprocessing\scorrupt\sJSONB. +D 2023-10-11T12:44:17.002 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4130133dcd43ac5c9b71a88a47f5ba8b6453d7cfbf158ebeb57ddd89f5def919 +F src/json.c d2b028758bc86b4ea3bde09effd05460945ee79286f1147bd90b233cdaec440b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 a72d54645ca0dd80c60a5ed586049dead3ea7f5fa9ad05c6610a506242a7032a -R e706082c7e7e2d4bd83182da6946ab5f +P 0fbda92bb0eeb40f95c83f717e4e8f5bff1ac82f1c899e9f6d400d67df67214e +R d76df8cb5e7347049ec337effa1f07fd U drh -Z 820cb01f77053065d828d065556f10f5 +Z 97ae228d7814f1fddc06d3cfd13d0a3f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index aa52b1f5c1..2d38285105 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0fbda92bb0eeb40f95c83f717e4e8f5bff1ac82f1c899e9f6d400d67df67214e \ No newline at end of file +e50045c22296be84c6bea82bb8b310f07bca820c84d4a7349b16da0cf5d2657c \ No newline at end of file diff --git a/src/json.c b/src/json.c index ff735f36f3..b92db836c3 100644 --- a/src/json.c +++ b/src/json.c @@ -2209,7 +2209,7 @@ static JsonParse *jsonParseCached( ** a match. */ static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ - assert( pNode->eU==1 ); + if( pNode->eType!=JSON_STRING ) return 0; if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; } From 86db4555cab9e74f9c141a151af459c0e8069f54 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 11 Oct 2023 13:19:37 +0000 Subject: [PATCH 074/347] Fix a missing zero-terminator on a string when processing JSON aggregates into JSONB. FossilOrigin-Name: fb81d570a3d9b8f55e9f278d5240605c5d0f3c5abde51550797999d03cf193a7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 9 +++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 9d433f7b1d..65f313ceaa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sissue\sthat\scan\sarise\swhen\sprocessing\scorrupt\sJSONB. -D 2023-10-11T12:44:17.002 +C Fix\sa\smissing\szero-terminator\son\sa\sstring\swhen\sprocessing\nJSON\saggregates\sinto\sJSONB. +D 2023-10-11T13:19:37.907 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d2b028758bc86b4ea3bde09effd05460945ee79286f1147bd90b233cdaec440b +F src/json.c 6c394327f052d3be3c4c2d21d9b8874320992e7d6f8b806db73c5b834e71c7bd F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -2129,8 +2129,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 0fbda92bb0eeb40f95c83f717e4e8f5bff1ac82f1c899e9f6d400d67df67214e -R d76df8cb5e7347049ec337effa1f07fd +P e50045c22296be84c6bea82bb8b310f07bca820c84d4a7349b16da0cf5d2657c +R 8c875e6758b9f56e162d8b6b955529e9 U drh -Z 97ae228d7814f1fddc06d3cfd13d0a3f +Z d51d36a4b4191be2ce04b67a30dc0525 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2d38285105..bba78e7854 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e50045c22296be84c6bea82bb8b310f07bca820c84d4a7349b16da0cf5d2657c \ No newline at end of file +fb81d570a3d9b8f55e9f278d5240605c5d0f3c5abde51550797999d03cf193a7 \ No newline at end of file diff --git a/src/json.c b/src/json.c index b92db836c3..3d0bba1941 100644 --- a/src/json.c +++ b/src/json.c @@ -486,6 +486,14 @@ static void jsonAppendChar(JsonString *p, char c){ } } +/* Make sure there is a zero terminator on p->zBuf[] +*/ +static void jsonStringTerminate(JsonString *p){ + if( p->nUsednAlloc || jsonStringGrow(p,1) ){ + p->zBuf[p->nUsed] = 0; + } +} + /* Try to force the string to be a zero-terminated RCStr string. ** ** Return true on success. Return false if an OOM prevents this @@ -3210,6 +3218,7 @@ static int jsonConvertTextToBlob( static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; memset(&px, 0, sizeof(px)); + jsonStringTerminate(pStr); px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; (void)jsonXlateTextToBlob(&px, 0); From 7d1c9da62dcde30f42f2a1d59aaf466207f922b7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 11 Oct 2023 17:21:16 +0000 Subject: [PATCH 075/347] Fix the use of an uninitialized value that occurs when doing a json_insert() of a string value that contains embedded U+0000 characters. FossilOrigin-Name: fc5ee9e51ad4556af526a6cefca5ae5a3b1b7affc4edf09832491d6b4f4ba366 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 6 ++++-- test/json101.test | 3 +++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 65f313ceaa..29b386a1a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\smissing\szero-terminator\son\sa\sstring\swhen\sprocessing\nJSON\saggregates\sinto\sJSONB. -D 2023-10-11T13:19:37.907 +C Fix\sthe\suse\sof\san\suninitialized\svalue\sthat\soccurs\swhen\sdoing\sa\sjson_insert()\nof\sa\sstring\svalue\sthat\scontains\sembedded\sU+0000\scharacters. +D 2023-10-11T17:21:16.637 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -674,7 +674,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6c394327f052d3be3c4c2d21d9b8874320992e7d6f8b806db73c5b834e71c7bd +F src/json.c fc8783af356d27cfdf14a583e5a7809394269763384cde979c0825a29f13e1d6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 @@ -1307,7 +1307,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test abb5a0cfde077a6f1124604e75806fbe889bc1c0acc11d32897f191e1f9c6b2c +F test/json101.test 8e7ee64714deff6deb927f417d90068cee38756cfe54375be46351ac5cb0962a F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2129,8 +2129,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 e50045c22296be84c6bea82bb8b310f07bca820c84d4a7349b16da0cf5d2657c -R 8c875e6758b9f56e162d8b6b955529e9 +P fb81d570a3d9b8f55e9f278d5240605c5d0f3c5abde51550797999d03cf193a7 +R d78e19c15a2441ced442368555cb1a45 U drh -Z d51d36a4b4191be2ce04b67a30dc0525 +Z 3bf927da2cbd54464f0b6fb8e90f7a35 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bba78e7854..0fb4b428b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fb81d570a3d9b8f55e9f278d5240605c5d0f3c5abde51550797999d03cf193a7 \ No newline at end of file +fc5ee9e51ad4556af526a6cefca5ae5a3b1b7affc4edf09832491d6b4f4ba366 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3d0bba1941..688ab32fea 100644 --- a/src/json.c +++ b/src/json.c @@ -4756,11 +4756,13 @@ static void jsonReplaceNode( break; } if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){ - char *zCopy = sqlite3DbStrDup(0, z); + char *zCopy = sqlite3_malloc64( n+1 ); int k; if( zCopy ){ + memcpy(zCopy, z, n); + zCopy[n] = 0; jsonParseAddCleanup(p, sqlite3_free, zCopy); - }else{ + }else{ p->oom = 1; sqlite3_result_error_nomem(pCtx); } diff --git a/test/json101.test b/test/json101.test index 7445cc987c..b8c2a58d34 100644 --- a/test/json101.test +++ b/test/json101.test @@ -169,6 +169,9 @@ do_execsql_test json101-4.7 { do_execsql_test json101-4.8 { SELECT x FROM j1 WHERE json_insert(x)<>x; } {} +do_execsql_test json101-4.9 { + SELECT json_insert('{"a":1}','$.b',CAST(x'0000' AS text)); +} {{{"a":1,"b":"\u0000\u0000"}}} # json_extract(JSON,'$') will return objects and arrays without change. # From 03204e910680096d982139bda04c6f8a1b0f8f67 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 11 Oct 2023 21:08:12 +0000 Subject: [PATCH 076/347] Add the tokendata=1 option to ignore trailing token-data when querying an fts5 table. FossilOrigin-Name: 122935182ad5869ce3a4c6d796c38a0509f6f3384dd1b3e60a3f2f0f366cc5f5 --- ext/fts5/fts5Int.h | 1 + ext/fts5/fts5_config.c | 10 +++++ ext/fts5/fts5_expr.c | 41 +++++++++++-------- ext/fts5/fts5_index.c | 19 +++++++-- ext/fts5/test/fts5_common.tcl | 14 +++++++ ext/fts5/test/fts5aa.test | 68 ++++++++++++++++++++----------- ext/fts5/test/fts5origintext.test | 45 +++++++++++++++++++- manifest | 24 +++++------ manifest.uuid | 2 +- 9 files changed, 164 insertions(+), 60 deletions(-) diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 1687168d5f..4aa578559b 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -196,6 +196,7 @@ struct Fts5Config { char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ + int bTokendata; /* "tokendata=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 5d0770502e..d2e8309cd2 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -398,6 +398,16 @@ static int fts5ConfigParseSpecial( return rc; } + if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){ + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ + *pzErr = sqlite3_mprintf("malformed tokendata=... directive"); + rc = SQLITE_ERROR; + }else{ + pConfig->bTokendata = (zArg[0]=='1'); + } + return rc; + } + *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); return SQLITE_ERROR; } diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 745a5d9fa6..a2c6320719 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -101,7 +101,8 @@ struct Fts5ExprTerm { u8 bPrefix; /* True for a prefix term */ u8 bFirst; /* True if token must be first in column */ char *pTerm; /* Term data */ - int nTerm; /* Size of term in bytes */ + int nQueryTerm; /* Effective size of term in bytes */ + int nFullTerm; /* Size of term in bytes incl. tokendata */ Fts5IndexIter *pIter; /* Iterator for this term */ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ }; @@ -968,7 +969,7 @@ static int fts5ExprNearInitAll( p->pIter = 0; } rc = sqlite3Fts5IndexQuery( - pExpr->pIndex, p->pTerm, p->nTerm, + pExpr->pIndex, p->pTerm, p->nQueryTerm, (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), pNear->pColset, @@ -1703,6 +1704,7 @@ Fts5ExprNearset *sqlite3Fts5ParseNearset( typedef struct TokenCtx TokenCtx; struct TokenCtx { Fts5ExprPhrase *pPhrase; + Fts5Config *pConfig; int rc; }; @@ -1737,7 +1739,8 @@ static int fts5ParseTokenize( }else{ memset(pSyn, 0, (size_t)nByte); pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); - pSyn->nTerm = nToken; + pSyn->nFullTerm = pSyn->nQueryTerm = nToken; + if( pCtx->pConfig->bTokendata ) pSyn->nQueryTerm = strlen(pSyn->pTerm); memcpy(pSyn->pTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; @@ -1764,7 +1767,8 @@ static int fts5ParseTokenize( pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; memset(pTerm, 0, sizeof(Fts5ExprTerm)); pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); - pTerm->nTerm = nToken; + pTerm->nFullTerm = pTerm->nQueryTerm = nToken; + if( pCtx->pConfig->bTokendata ) pTerm->nQueryTerm = strlen(pTerm->pTerm); } } @@ -1831,6 +1835,7 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm( memset(&sCtx, 0, sizeof(TokenCtx)); sCtx.pPhrase = pAppend; + sCtx.pConfig = pConfig; rc = fts5ParseStringFromToken(pToken, &z); if( rc==SQLITE_OK ){ @@ -1880,8 +1885,7 @@ int sqlite3Fts5ExprClonePhrase( int rc = SQLITE_OK; /* Return code */ Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ - TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */ - + TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ pOrig = pExpr->apExprPhrase[iPhrase]; pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); if( rc==SQLITE_OK ){ @@ -1912,11 +1916,12 @@ int sqlite3Fts5ExprClonePhrase( if( pOrig->nTerm ){ int i; /* Used to iterate through phrase terms */ + sCtx.pConfig = pExpr->pConfig; for(i=0; rc==SQLITE_OK && inTerm; i++){ int tflags = 0; Fts5ExprTerm *p; for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ - rc = fts5ParseTokenize((void*)&sCtx, tflags, p->pTerm, p->nTerm, 0, 0); + rc = fts5ParseTokenize((void*)&sCtx, tflags, p->pTerm,p->nFullTerm,0,0); tflags = FTS5_TOKEN_COLOCATED; } if( rc==SQLITE_OK ){ @@ -2298,12 +2303,12 @@ static Fts5ExprNode *fts5ParsePhraseToAnd( fts5ExprPhraseFree(pPhrase); }else{ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; + Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; pParse->apPhrase[pParse->nPhrase++] = pPhrase; pPhrase->nTerm = 1; - pPhrase->aTerm[0].pTerm = sqlite3Fts5Strndup( - &pParse->rc, p->pTerm, p->nTerm - ); - pPhrase->aTerm[0].nTerm = p->nTerm; + pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); + pTo->nQueryTerm = p->nQueryTerm; + pTo->nFullTerm = p->nFullTerm; pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) ); @@ -2488,7 +2493,7 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ /* Determine the maximum amount of space required. */ for(p=pTerm; p; p=p->pSynonym){ - nByte += pTerm->nTerm * 2 + 3 + 2; + nByte += pTerm->nQueryTerm * 2 + 3 + 2; } zQuoted = sqlite3_malloc64(nByte); @@ -2496,7 +2501,7 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ int i = 0; for(p=pTerm; p; p=p->pSynonym){ char *zIn = p->pTerm; - char *zEnd = &zIn[p->nTerm]; + char *zEnd = &zIn[p->nQueryTerm]; zQuoted[i++] = '"'; while( zInnTerm; iTerm++){ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", - p->nTerm, p->pTerm + p->nQueryTerm, p->pTerm ); if( pPhrase->aTerm[iTerm].bPrefix ){ zRet = fts5PrintfAppend(zRet, "*"); @@ -2997,11 +3002,11 @@ static int fts5ExprPopulatePoslistsCb( if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; for(i=0; inPhrase; i++){ - Fts5ExprTerm *pTerm; + Fts5ExprTerm *pT; if( p->aPopulator[i].bOk==0 ) continue; - for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ - if( (pTerm->nTerm==nToken || (pTerm->nTermbPrefix)) - && memcmp(pTerm->pTerm, pToken, pTerm->nTerm)==0 + for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ + if( (pT->nFullTerm==nToken || (pT->nFullTermbPrefix)) + && memcmp(pT->pTerm, pToken, pT->nFullTerm)==0 ){ int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 6cf30b5a00..b90a66b88f 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -6037,11 +6037,12 @@ static void fts5MergePrefixLists( static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ + int bTokenscan, int iIdx, /* Index to scan for data */ u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ - Fts5Iter **ppIter /* OUT: New iterator */ + Fts5Iter **ppIter /* OUT: New iterator */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; @@ -6060,6 +6061,8 @@ static void fts5SetupPrefixIter( xAppend = fts5AppendPoslist; } + assert( bTokenscan==0 || iIdx==0 ); + aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p); @@ -6075,6 +6078,12 @@ static void fts5SetupPrefixIter( int bNewTerm = 1; memset(&doclist, 0, sizeof(doclist)); + + /* If iIdx is non-zero, then it is the number of a prefix-index for + ** prefixes 1 character longer than the prefix being queried for. That + ** index contains all the doclists required, except for the one + ** corresponding to the prefix itself. That one is extracted from the + ** main term index here. */ if( iIdx!=0 ){ int dummy = 0; const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; @@ -6110,6 +6119,7 @@ static void fts5SetupPrefixIter( assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( bNewTerm ){ if( nTermnToken && pTerm[nToken]!=0x00 ) break; } if( p1->base.nData==0 ) continue; @@ -6438,7 +6448,7 @@ int sqlite3Fts5IndexQuery( } } - if( iIdx<=pConfig->nPrefix ){ + if( iIdx<=pConfig->nPrefix && (pConfig->bTokendata==0 || iIdx!=0) ){ /* Straight index lookup */ Fts5Structure *pStruct = fts5StructureRead(p); buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); @@ -6451,7 +6461,10 @@ int sqlite3Fts5IndexQuery( }else{ /* Scan multiple terms in the main index */ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; - fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); + int bTokenscan = (iIdx==0); + fts5SetupPrefixIter( + p, bDesc, bTokenscan, iPrefixIdx, buf.p, nToken+1, pColset, &pRet + ); if( pRet==0 ){ assert( p->rc!=SQLITE_OK ); }else{ diff --git a/ext/fts5/test/fts5_common.tcl b/ext/fts5/test/fts5_common.tcl index 9c012932da..001cad1de2 100644 --- a/ext/fts5/test/fts5_common.tcl +++ b/ext/fts5/test/fts5_common.tcl @@ -438,6 +438,20 @@ proc detail_is_none {} { detail_check ; expr {$::detail == "none"} } proc detail_is_col {} { detail_check ; expr {$::detail == "col" } } proc detail_is_full {} { detail_check ; expr {$::detail == "full"} } +proc foreach_tokenizer_mode {prefix script} { + set saved $::testprefix + foreach {d mapping} { + "" {} + "-origintext" {, tokenize="origintext unicode61", tokendata=1} + } { + set s [string map [list %TOKENIZER% $mapping] $script] + set ::testprefix "$prefix$d" + reset_db + sqlite3_fts5_register_origintext db + uplevel $s + } + set ::testprefix $saved +} #------------------------------------------------------------------------- # Convert a poslist of the type returned by fts5_test_poslist() to a diff --git a/ext/fts5/test/fts5aa.test b/ext/fts5/test/fts5aa.test index e1551fc516..a80a307a49 100644 --- a/ext/fts5/test/fts5aa.test +++ b/ext/fts5/test/fts5aa.test @@ -22,6 +22,7 @@ ifcapable !fts5 { } foreach_detail_mode $::testprefix { +foreach_tokenizer_mode $::testprefix { do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); @@ -44,7 +45,7 @@ do_execsql_test 1.1 { # do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 2.1 { INSERT INTO t1 VALUES('a b c', 'd e f'); @@ -73,8 +74,9 @@ do_execsql_test 2.4 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 3.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } foreach {i x y} { 1 {g f d b f} {h h e i a} @@ -97,8 +99,9 @@ foreach {i x y} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 4.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } foreach {i x y} { @@ -121,8 +124,9 @@ foreach {i x y} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 5.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } foreach {i x y} { @@ -145,8 +149,9 @@ foreach {i x y} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 6.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); } @@ -181,6 +186,7 @@ do_execsql_test 6.6 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db expr srand(0) do_execsql_test 7.0 { CREATE VIRTUAL TABLE t1 USING fts5(x,y,z); @@ -222,6 +228,7 @@ for {set i 1} {$i <= 10} {incr i} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 8.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, prefix="1,2,3"); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); @@ -236,6 +243,7 @@ do_execsql_test 8.1 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db expr srand(0) @@ -280,8 +288,9 @@ for {set i 1} {$i <= 10} {incr i} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 10.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } set d10 { 1 {g f d b f} {h h e i a} @@ -314,19 +323,19 @@ do_execsql_test 10.4.2 { INSERT INTO t1(t1) VALUES('integrity-check') } #------------------------------------------------------------------------- # do_catchsql_test 11.1 { - CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank, detail=%DETAIL%); + CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 column name: rank}} do_catchsql_test 11.2 { - CREATE VIRTUAL TABLE rank USING fts5(a, b, c, detail=%DETAIL%); + CREATE VIRTUAL TABLE rank USING fts5(a, b, c, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 table name: rank}} do_catchsql_test 11.3 { - CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rowid, detail=%DETAIL%); + CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rowid, detail=%DETAIL% %TOKENIZER%); } {1 {reserved fts5 column name: rowid}} #------------------------------------------------------------------------- # do_execsql_test 12.1 { - CREATE VIRTUAL TABLE t2 USING fts5(x,y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t2 USING fts5(x,y, detail=%DETAIL% %TOKENIZER%); } {} do_catchsql_test 12.2 { @@ -341,8 +350,9 @@ do_test 12.3 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 13.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(rowid, x) VALUES(1, 'o n e'), (2, 't w o'); } {} @@ -365,8 +375,9 @@ do_execsql_test 13.6 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 14.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); WITH d(x,y) AS ( SELECT NULL, 'xyz xyz xyz xyz xyz xyz' @@ -449,8 +460,9 @@ do_catchsql_test 16.2 { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 17.1 { - CREATE VIRTUAL TABLE b2 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE b2 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO b2 VALUES('a'); INSERT INTO b2 VALUES('b'); INSERT INTO b2 VALUES('c'); @@ -466,8 +478,9 @@ do_test 17.2 { if {[string match n* %DETAIL%]==0} { reset_db + sqlite3_fts5_register_origintext db do_execsql_test 17.3 { - CREATE VIRTUAL TABLE c2 USING fts5(x, y, detail=%DETAIL%); + CREATE VIRTUAL TABLE c2 USING fts5(x, y, detail=%DETAIL% %TOKENIZER%); INSERT INTO c2 VALUES('x x x', 'x x x'); SELECT rowid FROM c2 WHERE c2 MATCH 'y:x'; } {1} @@ -476,8 +489,9 @@ if {[string match n* %DETAIL%]==0} { #------------------------------------------------------------------------- # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 17.1 { - CREATE VIRTUAL TABLE uio USING fts5(ttt, detail=%DETAIL%); + CREATE VIRTUAL TABLE uio USING fts5(ttt, detail=%DETAIL% %TOKENIZER%); INSERT INTO uio VALUES(NULL); INSERT INTO uio SELECT NULL FROM uio; INSERT INTO uio SELECT NULL FROM uio; @@ -524,8 +538,8 @@ do_execsql_test 17.9 { #-------------------------------------------------------------------- # do_execsql_test 18.1 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%); - CREATE VIRTUAL TABLE t2 USING fts5(c, d, detail=%DETAIL%); + CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL% %TOKENIZER%); + CREATE VIRTUAL TABLE t2 USING fts5(c, d, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1 VALUES('abc*', NULL); INSERT INTO t2 VALUES(1, 'abcdefg'); } @@ -540,8 +554,9 @@ do_execsql_test 18.3 { # fts5 table in the temp schema. # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 19.0 { - CREATE VIRTUAL TABLE temp.t1 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE temp.t1 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t1 VALUES('x y z'); INSERT INTO t1 VALUES('w x 1'); SELECT rowid FROM t1 WHERE t1 MATCH 'x'; @@ -551,8 +566,9 @@ do_execsql_test 19.0 { # Test that 6 and 7 byte varints can be read. # reset_db +sqlite3_fts5_register_origintext db do_execsql_test 20.0 { - CREATE VIRTUAL TABLE temp.tmp USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE temp.tmp USING fts5(x, detail=%DETAIL% %TOKENIZER%); } set ::ids [list \ 0 [expr 1<<36] [expr 2<<36] [expr 1<<43] [expr 2<<43] @@ -570,7 +586,7 @@ do_test 20.1 { # do_execsql_test 21.0 { CREATE TEMP TABLE t8(a, b); - CREATE VIRTUAL TABLE ft USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE ft USING fts5(x, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 21.1 { @@ -581,7 +597,7 @@ do_execsql_test 21.1 { } do_execsql_test 22.0 { - CREATE VIRTUAL TABLE t9 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE t9 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t9(rowid, x) VALUES(2, 'bbb'); BEGIN; INSERT INTO t9(rowid, x) VALUES(1, 'aaa'); @@ -596,7 +612,7 @@ do_execsql_test 22.1 { #------------------------------------------------------------------------- do_execsql_test 23.0 { - CREATE VIRTUAL TABLE t10 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE t10 USING fts5(x, detail=%DETAIL% %TOKENIZER%); CREATE TABLE t11(x); } do_execsql_test 23.1 { @@ -608,7 +624,7 @@ do_execsql_test 23.2 { #------------------------------------------------------------------------- do_execsql_test 24.0 { - CREATE VIRTUAL TABLE t12 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE t12 USING fts5(x, detail=%DETAIL% %TOKENIZER%); INSERT INTO t12 VALUES('aaaa'); } do_execsql_test 24.1 { @@ -618,6 +634,9 @@ do_execsql_test 24.1 { INSERT INTO t12 VALUES('aaaa'); END; } +execsql_pp { + SELECT rowid, hex(block) FROM t12_data +} do_execsql_test 24.2 { INSERT INTO t12(t12) VALUES('integrity-check'); } @@ -627,7 +646,7 @@ do_execsql_test 24.3 { #------------------------------------------------------------------------- do_execsql_test 25.0 { - CREATE VIRTUAL TABLE t13 USING fts5(x, detail=%DETAIL%); + CREATE VIRTUAL TABLE t13 USING fts5(x, detail=%DETAIL% %TOKENIZER%); } do_execsql_test 25.1 { BEGIN; @@ -638,6 +657,7 @@ SELECT * FROM t13('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB } +} } expand_all_sql db diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 155d74c025..3fd5f17e7b 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -112,9 +112,50 @@ do_execsql_test 2.5 { INSERT INTO ft(ft) VALUES('optimize'); } -proc b {x} { string map [list "\0" "."] $x } -db func b b +#------------------------------------------------------------------------- +reset_db + +sqlite3_fts5_register_origintext db +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); + + INSERT INTO ft(rowid, x) VALUES(1, 'hello'); + INSERT INTO ft(rowid, x) VALUES(2, 'Hello'); + INSERT INTO ft(rowid, x) VALUES(3, 'HELLO'); +} + +#proc b {x} { string map [list "\0" "."] $x } +#db func b b #execsql_pp { SELECT b(term) FROM vocab } +do_execsql_test 3.1.1 { SELECT rowid FROM ft('hello') } 1 +do_execsql_test 3.1.2 { SELECT rowid FROM ft('Hello') } 2 +do_execsql_test 3.1.3 { SELECT rowid FROM ft('HELLO') } 3 + +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE ft2 USING fts5(x, + tokenize="origintext unicode61", + tokendata=1 + ); + CREATE VIRTUAL TABLE vocab2 USING fts5vocab(ft2, instance); + + INSERT INTO ft2(rowid, x) VALUES(1, 'hello'); + INSERT INTO ft2(rowid, x) VALUES(2, 'Hello'); + INSERT INTO ft2(rowid, x) VALUES(3, 'HELLO'); + + INSERT INTO ft2(rowid, x) VALUES(10, 'helloooo'); +} + +#proc b {x} { string map [list "\0" "."] $x } +#db func b b +#execsql_pp { SELECT b(term) FROM vocab } + +do_execsql_test 3.1.1 { SELECT rowid FROM ft2('hello') } {1 2 3} +do_execsql_test 3.1.2 { SELECT rowid FROM ft2('Hello') } {1 2 3} +do_execsql_test 3.1.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3} + +do_execsql_test 3.1.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10} + finish_test diff --git a/manifest b/manifest index e0c37bf1e2..92f3bfcf68 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sfor\sfts5\sexpression\sparser\smodule\sto\sallow\sembedded\s0x00\sbytes\sin\stokens. -D 2023-10-03T19:06:52.966 +C Add\sthe\stokendata=1\soption\sto\signore\strailing\stoken-data\swhen\squerying\san\sfts5\stable. +D 2023-10-11T21:08:12.656 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -88,13 +88,13 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h 05501612cc655504c5dce8ba765ab621d50fc478490089beaa0d75e00b23e520 -F ext/fts5/fts5Int.h 66a38b285e2b860baa29745d8eff27f5b0809268e7820498494d9acfaccf8a5c +F ext/fts5/fts5Int.h a21eb1cf036ac9eb943e45ed307762901ea86f0159bf0848baa2079a112ddc2f F ext/fts5/fts5_aux.c 572d5ec92ba7301df2fea3258576332f2f4d2dfd66d8263afd157d9deceac480 F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 -F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 -F ext/fts5/fts5_expr.c cc215d39714b428523d2f2ef42b713c83095a28a67bc7f6f2dc4ac036a29f460 +F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf +F ext/fts5/fts5_expr.c fd091d0558fda2517602ed5886ec615ce3e1bd76fb0bb0e5d1aa85ba8db287a8 F ext/fts5/fts5_hash.c 76765856397eff56f526b0640b23a1677d737d35e07bc00e4b4b2e0fc5fda60d -F ext/fts5/fts5_index.c e472083d371f420d52ec80445b9d2a99b16b23548205cb4064ddcd41bd79f63e +F ext/fts5/fts5_index.c 79a8e45771d0be24f0399b12268299f132ce0970ade941ba8a2d40b1d1aee4d7 F ext/fts5/fts5_main.c 799ec88d2309055f6406bddb0bd6ed80148c5da5eb14594c3c5309a6e944d489 F ext/fts5/fts5_storage.c 3c9b41fce41b6410f2e8f82eb035c6a29b2560483f773e6dc98cf3cb2e4ddbb5 F ext/fts5/fts5_tcl.c 0d2bb0ff7bf6ee136015be118167f0bd956ddd05a8f02c68bd34299b50648f9f @@ -106,8 +106,8 @@ F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d0988 F ext/fts5/fts5_vocab.c 12138e84616b56218532e3e8feb1d3e0e7ae845e33408dbe911df520424dc9d6 F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba -F ext/fts5/test/fts5_common.tcl a9de9c2209cc4e7ae3c753e783504e67206c6c1467d08f209cd0c5923d3e8d8b -F ext/fts5/test/fts5aa.test ba5158eba7d61359becdfca895ef471072c7bf7b20e5e60dcb4d024c8419c926 +F ext/fts5/test/fts5_common.tcl 8b1848ac2baad10e444e4183034a52050b52d20b3796d9d30e78f01ab0d05583 +F ext/fts5/test/fts5aa.test 4db81519863244a3cab35795fe65ab6b592e7970c7409eba098b23ebbfc08d95 F ext/fts5/test/fts5ab.test bd932720c748383277456b81f91bc00453de2174f9762cd05f95d0495dc50390 F ext/fts5/test/fts5ac.test a7aa7e1fefc6e1918aa4d3111d5c44a09177168e962c5fd2cca9620de8a7ed6d F ext/fts5/test/fts5ad.test e8cf959dfcd57c8e46d6f5f25665686f3b6627130a9a981371dafdf6482790de @@ -187,7 +187,7 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 3e1ac3230f65a0d644e9bf0738bebb09b4db9d9f123e1307d8630e42269b4afb +F ext/fts5/test/fts5origintext.test 646df137f1aa5b3d7032374ebe82bfdbe88d9f825d73ce8d44bead480317a9c5 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2123,8 +2123,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 e051120067fd87f57b498e505e3960cf4d14e8e33bad940618cc0823253254f7 -R 5cce41f02eae121cb66e72942ef56113 +P 342c8d0783f449817d3f565ff6b9f010a6c690beeea32f1861640810490a8b5f +R 28168382a793dd4cf732de7d6442ef72 U dan -Z 80b8e2664e768ed2ac03913fcf0180ea +Z cded15b3b6aeefc28aa13f7856e47e8d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4798938837..8bc375eea2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -342c8d0783f449817d3f565ff6b9f010a6c690beeea32f1861640810490a8b5f \ No newline at end of file +122935182ad5869ce3a4c6d796c38a0509f6f3384dd1b3e60a3f2f0f366cc5f5 \ No newline at end of file From 4bcde614d8680fcabd69cd607d65ed5b4c4a0eae Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 22 Oct 2023 12:43:30 +0000 Subject: [PATCH 077/347] Add JNI wrapper1.SqliteStmt.bindXyz() APIs. FossilOrigin-Name: 54fce9bf04a7517cdc8e96fe2efec66f03b0d03983c3a45d0ae7e1f16aa5a6c9 --- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 153 ++++++++++++------ .../src/org/sqlite/jni/wrapper1/Tester2.java | 24 ++- manifest | 17 +- manifest.uuid | 2 +- 4 files changed, 138 insertions(+), 58 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index bcf97b2394..4cf06d355c 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -77,7 +77,7 @@ public final class Sqlite implements AutoCloseable { */ sqlite3 nativeHandle(){ return this.db; } - private sqlite3 affirmOpen(){ + private sqlite3 thisDb(){ if( null==db || 0==db.getNativePointer() ){ throw new IllegalArgumentException("This database instance is closed."); } @@ -88,12 +88,58 @@ public final class Sqlite implements AutoCloseable { // return s==null ? null : s.getBytes(StandardCharsets.UTF_8); // } + /** + If rc!=0, throws an SqliteException. If this db is currently + opened, the error state is extracted from it, else only the + string form of rc is used. + */ private void affirmRcOk(int rc){ if( 0!=rc ){ - throw new SqliteException(db); + if( null==db ) throw new SqliteException(rc); + else throw new SqliteException(db); } } + /** + prepare() TODOs include: + + - overloads taking byte[] and ByteBuffer. + + - multi-statement processing, like CApi.sqlite3_prepare_multi() + but using a callback specific to the higher-level Stmt class + rather than the sqlite3_stmt class. + */ + public Stmt prepare(String sql, int prepFlags){ + final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); + final int rc = sqlite3_prepare_v3(thisDb(), sql, prepFlags, out); + affirmRcOk(rc); + return new Stmt(this, out.take()); + } + + public Stmt prepare(String sql){ + return prepare(sql, 0); + } + + public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){ + int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, + new SqlFunction.ScalarAdapter(f)); + if( 0!=rc ) throw new SqliteException(db); + } + + public void createFunction(String name, int nArg, ScalarFunction f){ + this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); + } + + public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){ + int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, + new SqlFunction.AggregateAdapter(f)); + if( 0!=rc ) throw new SqliteException(db); + } + + public void createFunction(String name, int nArg, AggregateFunction f){ + this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); + } + /** Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to create new instances. @@ -111,7 +157,11 @@ public final class Sqlite implements AutoCloseable { return stmt; } - private sqlite3_stmt affirmOpen(){ + /** + If this statement is still opened, its low-level handle is + returned, eelse an IllegalArgumentException is thrown. + */ + private sqlite3_stmt thisStmt(){ if( null==stmt || 0==stmt.getNativePointer() ){ throw new IllegalArgumentException("This Stmt has been finalized."); } @@ -140,7 +190,9 @@ public final class Sqlite implements AutoCloseable { /** Throws if rc is any value other than 0, SQLITE_ROW, or - SQLITE_DONE, else returns rc. + SQLITE_DONE, else returns rc. Error state for the exception is + extracted from this statement object (if it's opened) or the + string form of rc. */ private int checkRc(int rc){ switch(rc){ @@ -148,7 +200,8 @@ public final class Sqlite implements AutoCloseable { case SQLITE_ROW: case SQLITE_DONE: return rc; default: - throw new SqliteException(this); + if( null==stmt ) throw new SqliteException(rc); + else throw new SqliteException(this); } } @@ -157,7 +210,7 @@ public final class Sqlite implements AutoCloseable { result other than 0, SQLITE_ROW, or SQLITE_DONE. */ public int step(){ - return checkRc(sqlite3_step(affirmOpen())); + return checkRc(sqlite3_step(thisStmt())); } public Sqlite db(){ return this._db; } @@ -166,53 +219,55 @@ public final class Sqlite implements AutoCloseable { Works like sqlite3_reset() but throws on error. */ public void reset(){ - checkRc(sqlite3_reset(affirmOpen())); + checkRc(CApi.sqlite3_reset(thisStmt())); } public void clearBindings(){ - sqlite3_clear_bindings( affirmOpen() ); + CApi.sqlite3_clear_bindings( thisStmt() ); + } + public void bindInt(int ndx, int val){ + checkRc(CApi.sqlite3_bind_int(thisStmt(), ndx, val)); + } + public void bindInt64(int ndx, long val){ + checkRc(CApi.sqlite3_bind_int64(thisStmt(), ndx, val)); + } + public void bindDouble(int ndx, double val){ + checkRc(CApi.sqlite3_bind_double(thisStmt(), ndx, val)); + } + public void bindObject(int ndx, Object o){ + checkRc(CApi.sqlite3_bind_java_object(thisStmt(), ndx, o)); + } + public void bindNull(int ndx){ + checkRc(CApi.sqlite3_bind_null(thisStmt(), ndx)); + } + public int bindParameterCount(){ + return CApi.sqlite3_bind_parameter_count(thisStmt()); + } + public int bindParameterIndex(String paramName){ + return CApi.sqlite3_bind_parameter_index(thisStmt(), paramName); + } + public String bindParameterName(int ndx){ + return CApi.sqlite3_bind_parameter_name(thisStmt(), ndx); + } + public void bindText(int ndx, byte[] utf8){ + checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, utf8)); + } + public void bindText(int ndx, String asUtf8){ + checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, asUtf8)); + } + public void bindText16(int ndx, byte[] utf16){ + checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, utf16)); + } + public void bindText16(int ndx, String txt){ + checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, txt)); + } + public void bindZeroBlob(int ndx, int n){ + checkRc(CApi.sqlite3_bind_zeroblob(thisStmt(), ndx, n)); + } + public void bindBlob(int ndx, byte[] bytes){ + checkRc(CApi.sqlite3_bind_blob(thisStmt(), ndx, bytes)); } - } - - /** - prepare() TODOs include: - - - overloads taking byte[] and ByteBuffer. - - - multi-statement processing, like CApi.sqlite3_prepare_multi() - but using a callback specific to the higher-level Stmt class - rather than the sqlite3_stmt class. - */ - public Stmt prepare(String sql, int prepFlags){ - final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); - final int rc = sqlite3_prepare_v3(affirmOpen(), sql, prepFlags, out); - affirmRcOk(rc); - return new Stmt(this, out.take()); - } - - public Stmt prepare(String sql){ - return prepare(sql, 0); - } - - public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){ - int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep, - new SqlFunction.ScalarAdapter(f)); - if( 0!=rc ) throw new SqliteException(db); - } - - public void createFunction(String name, int nArg, ScalarFunction f){ - this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); - } - - public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){ - int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep, - new SqlFunction.AggregateAdapter(f)); - if( 0!=rc ) throw new SqliteException(db); - } - - public void createFunction(String name, int nArg, AggregateFunction f){ - this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); - } + } /* Stmt class */ } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index f5fd5f84e6..cf3364bc16 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -224,8 +224,26 @@ public class Tester2 implements Runnable { void testPrepare1(){ try (Sqlite db = openDb()) { - Sqlite.Stmt stmt = db.prepare("SELECT 1"); + Sqlite.Stmt stmt = db.prepare("SELECT ?1"); + Exception e = null; affirm( null!=stmt.nativeHandle() ); + affirm( 1==stmt.bindParameterCount() ); + affirm( "?1".equals(stmt.bindParameterName(1)) ); + affirm( null==stmt.bindParameterName(2) ); + stmt.bindInt(1, 1); + stmt.bindInt64(1, 1); + stmt.bindDouble(1, 1.1); + stmt.bindObject(1, db); + stmt.bindNull(1); + stmt.bindText(1, new byte[] {32,32,32}); + stmt.bindText(1, "123"); + stmt.bindText16(1, "123".getBytes(StandardCharsets.UTF_16)); + stmt.bindText16(1, "123"); + stmt.bindZeroBlob(1, 8); + stmt.bindBlob(1, new byte[] {1,2,3,4}); + try{ stmt.bindInt(2,1); } + catch(Exception ex){ e = ex; } + affirm( null!=e ); affirm( CApi.SQLITE_ROW == stmt.step() ); affirm( CApi.SQLITE_DONE == stmt.step() ); stmt.reset(); @@ -269,6 +287,10 @@ public class Tester2 implements Runnable { } void testUdfAggregate(){ + /* FIXME/TODO: once we've added the stmt bind/step/fetch + capabilities, go back and extend these tests to correspond to + the aggregate UDF tests in ext/wasm/tester1.c-pp.js. We first + require the ability to bind/step/fetch, however. */ final ValueHolder xDestroyCalled = new ValueHolder<>(0); final ValueHolder vh = new ValueHolder<>(0); try (Sqlite db = openDb()) { diff --git a/manifest b/manifest index 16ecd5eb71..3110770045 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\simprove\sUB\sprotections\sin\ssqlite3_bind_blob/text/text16(). -D 2023-10-22T12:33:05.772 +C Add\sJNI\swrapper1.SqliteStmt.bindXyz()\sAPIs. +D 2023-10-22T12:43:30.072 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -294,9 +294,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 004394eeb944baa56e36cd7ae69ba6d4a52b52db3c49439db16e98270b861421 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java a9ddc6a9e8c113168cc67592ae24c0e56d30dd06226eeab012f2761a0889d7bb +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 25e4e65ba434d0e110e4adb6782e20cd374d83b7fe00ba5ca48a1dadd2fdd7dd F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java c24b510ebe801c30533cc62efdf69a4a5e2da9ec4b49f8d403f2060693f060a0 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 65437e09115bfef4445957db61fcf6dac9aad37ac00edb445c3de812e9750d6e F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2136,8 +2136,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b8258103fb433d9f5cfa15661b5edf4e60128fb4161d8a18e5cc3253e5aed72b -R 10dd3eddc3858c0c90f96d7af2dd213f +P 5c8383210a87d7f9d37a27053b5b1b6f41794fa8612826c68c1ca49c495cbd97 +R 186cd14750afba6ac11c0a47787c2fc3 +T *branch * jni-post-3.44 +T *sym-jni-post-3.44 * +T -sym-trunk * Cancelled\sby\sbranch. U stephan -Z 39a3acd541e66480809465cadb3002fa +Z dc6c06549901a63430a6e90b2648f01f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bcfb4efa95..166d31ff93 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5c8383210a87d7f9d37a27053b5b1b6f41794fa8612826c68c1ca49c495cbd97 \ No newline at end of file +54fce9bf04a7517cdc8e96fe2efec66f03b0d03983c3a45d0ae7e1f16aa5a6c9 \ No newline at end of file From 4598b6e5aea09f4346e356f7dbb6540eff0c98d1 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 22 Oct 2023 13:09:37 +0000 Subject: [PATCH 078/347] Add API_ARMOR to sqlite3_clear_bindings(). FossilOrigin-Name: f3fb4d345bbf5ae4a35d8076043df601b1bf7dfd68760a416440139eb3e5eb9a --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/vdbeapi.c | 10 +++++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 3110770045..d4e1eb3da2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sJNI\swrapper1.SqliteStmt.bindXyz()\sAPIs. -D 2023-10-22T12:43:30.072 +C Add\sAPI_ARMOR\sto\ssqlite3_clear_bindings(). +D 2023-10-22T13:09:37.276 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -794,7 +794,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 300b1ac9339a5b7db9ccd48c1a13c3d71722da13352a38ee042ca0a399b4dd7e F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c fe654b1f54e1feebcaed6c2ae3ed035cc65bfeb9a1169bed866abc42bfc63ff6 +F src/vdbeapi.c db190d007bdf5b9165edeb12369f4c59a459f88fd652c1671c1238862e662cc3 F src/vdbeaux.c 929a4edecf9845fb063b47b23b9d187473a648470d915521cf72419f5219c4b7 F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c c936e9002af4993b84c4eb7133d6b1190efe46d391cc86117ecd67ba17b1a04b @@ -2136,11 +2136,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 5c8383210a87d7f9d37a27053b5b1b6f41794fa8612826c68c1ca49c495cbd97 -R 186cd14750afba6ac11c0a47787c2fc3 -T *branch * jni-post-3.44 -T *sym-jni-post-3.44 * -T -sym-trunk * Cancelled\sby\sbranch. +P 54fce9bf04a7517cdc8e96fe2efec66f03b0d03983c3a45d0ae7e1f16aa5a6c9 +R 17c045c95304ed4e3d6135c84273c496 U stephan -Z dc6c06549901a63430a6e90b2648f01f +Z 1cc1c3ab8b0755073c3409e264c96da6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 166d31ff93..237845ed02 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -54fce9bf04a7517cdc8e96fe2efec66f03b0d03983c3a45d0ae7e1f16aa5a6c9 \ No newline at end of file +f3fb4d345bbf5ae4a35d8076043df601b1bf7dfd68760a416440139eb3e5eb9a \ No newline at end of file diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 31ebbc6976..6724035fd5 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -152,7 +152,15 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; + sqlite3_mutex *mutex; +#endif +#ifdef SQLITE_ENABLE_API_ARMOR + if( pStmt==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif +#if SQLITE_THREADSAFE + mutex = p->db->mutex; #endif sqlite3_mutex_enter(mutex); for(i=0; inVar; i++){ From 32a0d6129f240829f2d8811800869f7998b4b5e8 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 22 Oct 2023 13:54:26 +0000 Subject: [PATCH 079/347] JNI: add column-get bindings to the wrapper1 Stmt class and extend the AggregateFunction tests to ensure that the aggregate context is honored. FossilOrigin-Name: 60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287 --- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 73 +++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 48 +++++++++--- manifest | 14 ++-- manifest.uuid | 2 +- 4 files changed, 118 insertions(+), 19 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 4cf06d355c..cefc0aa78b 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -147,10 +147,18 @@ public final class Sqlite implements AutoCloseable { public final class Stmt implements AutoCloseable { private Sqlite _db = null; private sqlite3_stmt stmt = null; + /** + We save the result column count in order to prevent having to + call into C to fetch that value every time we need to check + that value for the columnXyz() methods. + */ + private final int resultColCount; + /** Only called by the prepare() factory functions. */ Stmt(Sqlite db, sqlite3_stmt stmt){ this._db = db; this.stmt = stmt; + this.resultColCount = CApi.sqlite3_column_count(stmt); } sqlite3_stmt nativeHandle(){ @@ -168,6 +176,15 @@ public final class Sqlite implements AutoCloseable { return stmt; } + /** Throws if n is out of range of this.resultColCount. Intended + to be used by the columnXyz() methods. */ + private sqlite3_stmt checkColIndex(int n){ + if(n<0 || n>=this.resultColCount){ + throw new IllegalArgumentException("Column index "+n+" is out of range."); + } + return thisStmt(); + } + /** Corresponds to sqlite3_finalize(), but we cannot override the name finalize() here because this one requires a different @@ -211,6 +228,14 @@ public final class Sqlite implements AutoCloseable { */ public int step(){ return checkRc(sqlite3_step(thisStmt())); + /* + Potential signature change TODO: + + boolean step() + + Returning true for SQLITE_ROW and false for anything else. + Those semantics have proven useful in the WASM/JS bindings. + */ } public Sqlite db(){ return this._db; } @@ -268,6 +293,54 @@ public final class Sqlite implements AutoCloseable { checkRc(CApi.sqlite3_bind_blob(thisStmt(), ndx, bytes)); } + public byte[] columnBlob(int ndx){ + return CApi.sqlite3_column_blob( checkColIndex(ndx), ndx ); + } + public byte[] columnText(int ndx){ + return CApi.sqlite3_column_text( checkColIndex(ndx), ndx ); + } + public String columnText16(int ndx){ + return CApi.sqlite3_column_text16( checkColIndex(ndx), ndx ); + } + public int columnBytes(int ndx){ + return CApi.sqlite3_column_bytes( checkColIndex(ndx), ndx ); + } + public int columnBytes16(int ndx){ + return CApi.sqlite3_column_bytes16( checkColIndex(ndx), ndx ); + } + public int columnInt(int ndx){ + return CApi.sqlite3_column_int( checkColIndex(ndx), ndx ); + } + public long columnInt64(int ndx){ + return CApi.sqlite3_column_int64( checkColIndex(ndx), ndx ); + } + public double columnDouble(int ndx){ + return CApi.sqlite3_column_double( checkColIndex(ndx), ndx ); + } + public int columnType(int ndx){ + return CApi.sqlite3_column_type( checkColIndex(ndx), ndx ); + } + public String columnDeclType(int ndx){ + return CApi.sqlite3_column_decltype( checkColIndex(ndx), ndx ); + } + public int columnCount(){ + return resultColCount; + } + public int columnDataCount(){ + return CApi.sqlite3_data_count( thisStmt() ); + } + public String columnName(int ndx){ + return CApi.sqlite3_column_name( checkColIndex(ndx), ndx ); + } + public String columnDatabaseName(int ndx){ + return CApi.sqlite3_column_database_name( checkColIndex(ndx), ndx ); + } + public String columnOriginName(int ndx){ + return CApi.sqlite3_column_origin_name( checkColIndex(ndx), ndx ); + } + public String columnTableName(int ndx){ + return CApi.sqlite3_column_table_name( checkColIndex(ndx), ndx ); + } } /* Stmt class */ } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index cf3364bc16..6756478f58 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -230,7 +230,6 @@ public class Tester2 implements Runnable { affirm( 1==stmt.bindParameterCount() ); affirm( "?1".equals(stmt.bindParameterName(1)) ); affirm( null==stmt.bindParameterName(2) ); - stmt.bindInt(1, 1); stmt.bindInt64(1, 1); stmt.bindDouble(1, 1.1); stmt.bindObject(1, db); @@ -241,10 +240,20 @@ public class Tester2 implements Runnable { stmt.bindText16(1, "123"); stmt.bindZeroBlob(1, 8); stmt.bindBlob(1, new byte[] {1,2,3,4}); + stmt.bindInt(1, 17); try{ stmt.bindInt(2,1); } catch(Exception ex){ e = ex; } affirm( null!=e ); + e = null; affirm( CApi.SQLITE_ROW == stmt.step() ); + try{ stmt.columnInt(1); } + catch(Exception ex){ e = ex; } + affirm( null!=e ); + e = null; + affirm( 17 == stmt.columnInt(0) ); + affirm( 17L == stmt.columnInt64(0) ); + affirm( 17.0 == stmt.columnDouble(0) ); + affirm( "17".equals(stmt.columnText16(0)) ); affirm( CApi.SQLITE_DONE == stmt.step() ); stmt.reset(); affirm( CApi.SQLITE_ROW == stmt.step() ); @@ -287,12 +296,8 @@ public class Tester2 implements Runnable { } void testUdfAggregate(){ - /* FIXME/TODO: once we've added the stmt bind/step/fetch - capabilities, go back and extend these tests to correspond to - the aggregate UDF tests in ext/wasm/tester1.c-pp.js. We first - require the ability to bind/step/fetch, however. */ final ValueHolder xDestroyCalled = new ValueHolder<>(0); - final ValueHolder vh = new ValueHolder<>(0); + Sqlite.Stmt q = null; try (Sqlite db = openDb()) { execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)"); final AggregateFunction f = new AggregateFunction(){ @@ -306,18 +311,39 @@ public class Tester2 implements Runnable { final Integer v = this.takeAggregateState(args); if( null==v ) args.resultNull(); else args.resultInt(v); - vh.value = v; } public void xDestroy(){ ++xDestroyCalled.value; } }; - db.createFunction("myagg", -1, f); - execSql(db, "select myagg(a) from t"); - affirm( 6 == vh.value ); + db.createFunction("summer", 1, f); + q = db.prepare( + "with cte(v) as ("+ + "select 3 union all select 5 union all select 7"+ + ") select summer(v), summer(v+1) from cte" + /* ------------------^^^^^^^^^^^ ensures that we're handling + sqlite3_aggregate_context() properly. */ + ); + affirm( CApi.SQLITE_ROW==q.step() ); + affirm( 15==q.columnInt(0) ); + q.finalizeStmt(); + q = null; affirm( 0 == xDestroyCalled.value ); + db.createFunction("summerN", -1, f); + + q = db.prepare("select summerN(1,8,9), summerN(2,3,4)"); + affirm( CApi.SQLITE_ROW==q.step() ); + affirm( 18==q.columnInt(0) ); + affirm( 9==q.columnInt(1) ); + q.finalizeStmt(); + q = null; + + }/*db*/ + finally{ + if( null!=q ) q.finalizeStmt(); } - affirm( 1 == xDestroyCalled.value ); + affirm( 2 == xDestroyCalled.value + /* because we've bound the same instance twice */ ); } private void runTests(boolean fromThread) throws Exception { diff --git a/manifest b/manifest index d4e1eb3da2..66f9eea687 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sAPI_ARMOR\sto\ssqlite3_clear_bindings(). -D 2023-10-22T13:09:37.276 +C JNI:\sadd\scolumn-get\sbindings\sto\sthe\swrapper1\sStmt\sclass\sand\sextend\sthe\sAggregateFunction\stests\sto\sensure\sthat\sthe\saggregate\scontext\sis\shonored. +D 2023-10-22T13:54:26.716 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -294,9 +294,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 004394eeb944baa56e36cd7ae69ba6d4a52b52db3c49439db16e98270b861421 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 25e4e65ba434d0e110e4adb6782e20cd374d83b7fe00ba5ca48a1dadd2fdd7dd +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b0ee72b60b2a15d3f19571213f62e560134d6517eacece73eb1299b6d9980f14 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 65437e09115bfef4445957db61fcf6dac9aad37ac00edb445c3de812e9750d6e +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 5ff33f3e31f5d88178e485ab334e8fb6ccf8ad1f6666136b4a613fec58888432 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2136,8 +2136,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 54fce9bf04a7517cdc8e96fe2efec66f03b0d03983c3a45d0ae7e1f16aa5a6c9 -R 17c045c95304ed4e3d6135c84273c496 +P f3fb4d345bbf5ae4a35d8076043df601b1bf7dfd68760a416440139eb3e5eb9a +R 4d4dd7480ffdece828c7f766b6d86d96 U stephan -Z 1cc1c3ab8b0755073c3409e264c96da6 +Z e88e3b32c9f23fead743fdff8331a212 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 237845ed02..32e6dcdc5e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f3fb4d345bbf5ae4a35d8076043df601b1bf7dfd68760a416440139eb3e5eb9a \ No newline at end of file +60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287 \ No newline at end of file From 166c8d00671fbd16057cc3dc6523af336591a1c5 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 22 Oct 2023 14:25:37 +0000 Subject: [PATCH 080/347] JNI: flesh out and simplify the APIs for binding and fetching arbitrary Java objects. FossilOrigin-Name: 89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f --- ext/jni/src/c/sqlite3-jni.c | 31 +++++++++++++++--- ext/jni/src/c/sqlite3-jni.h | 8 +++++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 32 ++++++++++++++++++- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 12 ++++--- .../org/sqlite/jni/wrapper1/SqlFunction.java | 6 ++-- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 6 ++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 8 +++-- manifest | 24 +++++++------- manifest.uuid | 2 +- 9 files changed, 101 insertions(+), 28 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 245ce4f9e9..86a4930917 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1670,8 +1670,9 @@ static int encodingTypeIsValid(int eTextRep){ } } -/* For use with sqlite3_result/value_pointer() */ -static const char * const ResultJavaValuePtrStr = "org.sqlite.jni.capi.ResultJavaVal"; +/* For use with sqlite3_result_pointer(), sqlite3_value_pointer(), + sqlite3_bind_java_object(), and sqlite3_column_java_object(). */ +static const char * const s3jni__value_jref_key = "org.sqlite.jni.capi.ResultJavaVal"; /* ** If v is not NULL, it must be a jobject global reference. Its @@ -2418,7 +2419,7 @@ S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( if(pStmt){ jobject const rv = S3JniRefGlobal(val); if( rv ){ - rc = sqlite3_bind_pointer(pStmt, ndx, rv, ResultJavaValuePtrStr, + rc = sqlite3_bind_pointer(pStmt, ndx, rv, s3jni__value_jref_key, S3Jni_jobject_finalizer); }else if(val){ rc = SQLITE_NOMEM; @@ -2870,6 +2871,26 @@ S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } +S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( + JniArgsEnvClass, jlong jpStmt, jint ndx +){ + sqlite3_stmt * const stmt = S3JniLongPtr_sqlite3_stmt(jpStmt); + jobject rv = 0; + if( stmt ){ + sqlite3 * const db = sqlite3_db_handle(stmt); + sqlite3_value * sv; + sqlite3_mutex_enter(sqlite3_db_mutex(db)); + sv = sqlite3_column_value(stmt, (int)ndx); + if( sv ){ + rv = S3JniRefLocal( + sqlite3_value_pointer(sv, s3jni__value_jref_key) + ); + } + sqlite3_mutex_leave(sqlite3_db_mutex(db)); + } + return rv; +} + S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ @@ -4351,7 +4372,7 @@ S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( jobject const rjv = S3JniRefGlobal(v); if( rjv ){ sqlite3_result_pointer(pCx, rjv, - ResultJavaValuePtrStr, S3Jni_jobject_finalizer); + s3jni__value_jref_key, S3Jni_jobject_finalizer); }else{ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } @@ -4889,7 +4910,7 @@ S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( ){ sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); return sv - ? sqlite3_value_pointer(sv, ResultJavaValuePtrStr) + ? sqlite3_value_pointer(sv, s3jni__value_jref_key) : 0; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index bf6df7ac94..6c4d07fc43 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1119,6 +1119,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int64 (JNIEnv *, jclass, jobject, jint); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_column_java_object + * Signature: (JI)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1java_1object + (JNIEnv *, jclass, jlong, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_name diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 302cdb760e..ff5da01c65 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -595,6 +595,36 @@ public final class CApi { @NotNull sqlite3_stmt stmt, int ndx ); + static native Object sqlite3_column_java_object( + @NotNull long ptrToStmt, int ndx + ); + + /** + If the given result column was bound with + sqlite3_bind_java_object() or sqlite3_result_java_object() then + that object is returned, else null is returned. This routine + requires locking the owning database's mutex briefly in order to + extract the object in a thread-safe way. + */ + public static Object sqlite3_column_java_object( + @NotNull sqlite3_stmt stmt, int ndx + ){ + return sqlite3_column_java_object( stmt.getNativePointer(), ndx ); + } + + /** + If the two-parameter overload of sqlite3_column_java_object() + returns non-null and the returned value is an instance of T then + that object is returned, else null is returned. + */ + @SuppressWarnings("unchecked") + public static T sqlite3_column_java_object( + @NotNull sqlite3_stmt stmt, int ndx, @NotNull Class type + ){ + final Object o = sqlite3_column_java_object(stmt, ndx); + return type.isInstance(o) ? (T)o : null; + } + static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_name(@NotNull sqlite3_stmt stmt, int ndx){ @@ -1938,7 +1968,7 @@ public final class CApi { given Class, else it returns null. */ @SuppressWarnings("unchecked") - public static T sqlite3_value_java_casted(@NotNull sqlite3_value v, + public static T sqlite3_value_java_object(@NotNull sqlite3_value v, @NotNull Class type){ final Object o = sqlite3_value_java_object(v); return type.isInstance(o) ? (T)o : null; diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 6fb28e65b9..b1d8df11cd 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -803,13 +803,17 @@ public class Tester1 implements Runnable { affirm( 0==rc ); int n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ + affirm( testResult.value == sqlite3_column_java_object(stmt, 0) ); + affirm( testResult.value == sqlite3_column_java_object(stmt, 0, sqlite3.class) ); + affirm( null == sqlite3_column_java_object(stmt, 0, sqlite3_stmt.class) ); + affirm( null == sqlite3_column_java_object(stmt,1) ); final sqlite3_value v = sqlite3_column_value(stmt, 0); affirm( testResult.value == sqlite3_value_java_object(v) ); - affirm( testResult.value == sqlite3_value_java_casted(v, sqlite3.class) ); + affirm( testResult.value == sqlite3_value_java_object(v, sqlite3.class) ); affirm( testResult.value == - sqlite3_value_java_casted(v, testResult.value.getClass()) ); - affirm( testResult.value == sqlite3_value_java_casted(v, Object.class) ); - affirm( null == sqlite3_value_java_casted(v, String.class) ); + sqlite3_value_java_object(v, testResult.value.getClass()) ); + affirm( testResult.value == sqlite3_value_java_object(v, Object.class) ); + affirm( null == sqlite3_value_java_object(v, String.class) ); ++n; } sqlite3_finalize(stmt); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index d6acda5aa5..2e0be3cd63 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -72,7 +72,7 @@ public interface SqlFunction { public int getBytes(){return a.getBytes(ndx);} public int getBytes16(){return a.getBytes16(ndx);} public Object getObject(){return a.getObject(ndx);} - public T getObjectCasted(Class type){ return a.getObjectCasted(ndx, type); } + public T getObject(Class type){ return a.getObject(ndx, type); } public int getType(){return a.getType(ndx);} public Object getAuxData(){return a.getAuxData(ndx);} public void setAuxData(Object o){a.setAuxData(ndx, o);} @@ -113,8 +113,8 @@ public interface SqlFunction { public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));} public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));} public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));} - public T getObjectCasted(int arg, Class type){ - return CApi.sqlite3_value_java_casted(valueAt(arg), type); + public T getObject(int arg, Class type){ + return CApi.sqlite3_value_java_object(valueAt(arg), type); } public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));} diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index cefc0aa78b..25628e0805 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -329,6 +329,12 @@ public final class Sqlite implements AutoCloseable { public int columnDataCount(){ return CApi.sqlite3_data_count( thisStmt() ); } + public Object columnObject(int ndx){ + return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx ); + } + public T columnObject(int ndx, Class type){ + return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx, type ); + } public String columnName(int ndx){ return CApi.sqlite3_column_name( checkColIndex(ndx), ndx ); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 6756478f58..8fb36a3b54 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -261,9 +261,13 @@ public class Tester2 implements Runnable { affirm( 0 == stmt.finalizeStmt() ); affirm( null==stmt.nativeHandle() ); - stmt = db.prepare("SELECT 1"); + stmt = db.prepare("SELECT ?"); + stmt.bindObject(1, db); affirm( CApi.SQLITE_ROW == stmt.step() ); - affirm( 0 == stmt.finalizeStmt() ) + affirm( db==stmt.columnObject(0) ); + affirm( db==stmt.columnObject(0, Sqlite.class ) ); + affirm( null==stmt.columnObject(0, Sqlite.Stmt.class ) ); + affirm( 0==stmt.finalizeStmt() ) /* getting a non-0 out of sqlite3_finalize() is tricky */; affirm( null==stmt.nativeHandle() ); } diff --git a/manifest b/manifest index 66f9eea687..d20131a353 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\sadd\scolumn-get\sbindings\sto\sthe\swrapper1\sStmt\sclass\sand\sextend\sthe\sAggregateFunction\stests\sto\sensure\sthat\sthe\saggregate\scontext\sis\shonored. -D 2023-10-22T13:54:26.716 +C JNI:\sflesh\sout\sand\ssimplify\sthe\sAPIs\sfor\sbinding\sand\sfetching\sarbitrary\sJava\sobjects. +D 2023-10-22T14:25:37.634 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -239,8 +239,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 5c3ac326bf3853486ebe0d70819abc790cc65c412182ce4ebd5012b008d9b059 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 6f6df9657989e9ca2cfdcc2fe9a71c279de56d5c941adfd09a0f24256de35c8f -F ext/jni/src/c/sqlite3-jni.h b4c413a0d0c734683da1049cfcf89e35ae2719759d0656ec0f8c57188f18cab8 +F ext/jni/src/c/sqlite3-jni.c dcd6534b65b732ad927a49185c76c76abbd5ccadfa972d02f699abc45678e329 +F ext/jni/src/c/sqlite3-jni.h e839090f5ec35aa96983a5621659e55ef897dc0522242fd00f107028ef5e7dd5 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -249,7 +249,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java bc29e986c866c2ddbbb9f93 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java bccb442ca81cd4decb1adae99006a60b7a9f54e5153842e738c01104e97d1de0 +F ext/jni/src/org/sqlite/jni/capi/CApi.java f23e8139cdb1d9c850f28ccf95384e37239cee47fd683bfb199f50aa7ce011fc F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 @@ -267,7 +267,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java fef556adbc3624292423083a648bd F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java ca195521b6bda3e0cd00e76bb71ec8060d1fab76a2f13b1af9feea40789f44bb +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 5c4e7ba5034aeb5c5be0361b9fa0c23fe993774e634750c775d7ad8fa19b22f3 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab @@ -293,10 +293,10 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 004394eeb944baa56e36cd7ae69ba6d4a52b52db3c49439db16e98270b861421 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b0ee72b60b2a15d3f19571213f62e560134d6517eacece73eb1299b6d9980f14 +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 92c28b9de358407c8c5e772e0408db528e47eeeb50ffd87b86563a5f078198ad +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 882e345d925a79b575b1182efd816dcc72d6814922b4f58e7f4d29f04ece1f64 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 5ff33f3e31f5d88178e485ab334e8fb6ccf8ad1f6666136b4a613fec58888432 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java e224efb77dae2f0abe18f2010c0eb5a09df991f2743597a1aff7f9283f71da7d F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2136,8 +2136,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 f3fb4d345bbf5ae4a35d8076043df601b1bf7dfd68760a416440139eb3e5eb9a -R 4d4dd7480ffdece828c7f766b6d86d96 +P 60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287 +R 5a1e4de3384c2859d02b694c480544f0 U stephan -Z e88e3b32c9f23fead743fdff8331a212 +Z 0b6f35d12f929147f1a8dbbd84db96f1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 32e6dcdc5e..a425b28981 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287 \ No newline at end of file +89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f \ No newline at end of file From 96aa4d344d46444042960b8e0bc373b551125e73 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 22 Oct 2023 23:36:16 +0000 Subject: [PATCH 081/347] Add high-level window function wrapper to the JNI wrapper1 interface. FossilOrigin-Name: a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708 --- ext/jni/GNUmakefile | 1 + .../sqlite/jni/capi/AggregateFunction.java | 70 ++++- .../src/org/sqlite/jni/capi/SQLFunction.java | 67 ----- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 1 - .../jni/wrapper1/AggregateFunction.java | 70 ++++- .../org/sqlite/jni/wrapper1/SqlFunction.java | 282 +++++++++--------- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 34 ++- .../src/org/sqlite/jni/wrapper1/Tester2.java | 86 +++++- .../sqlite/jni/wrapper1/WindowFunction.java | 46 +++ manifest | 27 +- manifest.uuid | 2 +- 11 files changed, 436 insertions(+), 250 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 19a5080471..155e4e7f63 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -120,6 +120,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\ Sqlite.java \ SqliteException.java \ ValueHolder.java \ + WindowFunction.java \ ) JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\ diff --git a/ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java index 89c4f27421..1fa6c6b805 100644 --- a/ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java @@ -42,9 +42,75 @@ public abstract class AggregateFunction implements SQLFunction { */ public void xDestroy() {} + /** + PerContextState assists aggregate and window functions in + managing their accumulator state across calls to the UDF's + callbacks. + +

T must be of a type which can be legally stored as a value in + java.util.HashMap. + +

If a given aggregate or window function is called multiple times + in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., + then the clients need some way of knowing which call is which so + that they can map their state between their various UDF callbacks + and reset it via xFinal(). This class takes care of such + mappings. + +

This class works by mapping + sqlite3_context.getAggregateContext() to a single piece of + state, of a client-defined type (the T part of this class), which + persists across a "matching set" of the UDF's callbacks. + +

This class is a helper providing commonly-needed functionality + - it is not required for use with aggregate or window functions. + Client UDFs are free to perform such mappings using custom + approaches. The provided {@link AggregateFunction} and {@link + WindowFunction} classes use this. + */ + public static final class PerContextState { + private final java.util.Map> map + = new java.util.HashMap<>(); + + /** + Should be called from a UDF's xStep(), xValue(), and xInverse() + methods, passing it that method's first argument and an initial + value for the persistent state. If there is currently no + mapping for the given context within the map, one is created + using the given initial value, else the existing one is used + and the 2nd argument is ignored. It returns a ValueHolder + which can be used to modify that state directly without + requiring that the client update the underlying map's entry. + +

The caller is obligated to eventually call + takeAggregateState() to clear the mapping. + */ + public ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ + final Long key = cx.getAggregateContext(true); + ValueHolder rc = null==key ? null : map.get(key); + if( null==rc ){ + map.put(key, rc = new ValueHolder<>(initialValue)); + } + return rc; + } + + /** + Should be called from a UDF's xFinal() method and passed that + method's first argument. This function removes the value + associated with cx.getAggregateContext() from the map and + returns it, returning null if no other UDF method has been + called to set up such a mapping. The latter condition will be + the case if a UDF is used in a statement which has no result + rows. + */ + public T takeAggregateState(sqlite3_context cx){ + final ValueHolder h = map.remove(cx.getAggregateContext(false)); + return null==h ? null : h.value; + } + } + /** Per-invocation state for the UDF. */ - private final SQLFunction.PerContextState map = - new SQLFunction.PerContextState<>(); + private final PerContextState map = new PerContextState<>(); /** To be called from the implementation's xStep() method, as well diff --git a/ext/jni/src/org/sqlite/jni/capi/SQLFunction.java b/ext/jni/src/org/sqlite/jni/capi/SQLFunction.java index 4806e2fc0c..7ad1381a7a 100644 --- a/ext/jni/src/org/sqlite/jni/capi/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/capi/SQLFunction.java @@ -33,71 +33,4 @@ package org.sqlite.jni.capi; */ public interface SQLFunction { - /** - PerContextState assists aggregate and window functions in - managing their accumulator state across calls to the UDF's - callbacks. - -

T must be of a type which can be legally stored as a value in - java.util.HashMap. - -

If a given aggregate or window function is called multiple times - in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., - then the clients need some way of knowing which call is which so - that they can map their state between their various UDF callbacks - and reset it via xFinal(). This class takes care of such - mappings. - -

This class works by mapping - sqlite3_context.getAggregateContext() to a single piece of - state, of a client-defined type (the T part of this class), which - persists across a "matching set" of the UDF's callbacks. - -

This class is a helper providing commonly-needed functionality - - it is not required for use with aggregate or window functions. - Client UDFs are free to perform such mappings using custom - approaches. The provided {@link AggregateFunction} and {@link - WindowFunction} classes use this. - */ - public static final class PerContextState { - private final java.util.Map> map - = new java.util.HashMap<>(); - - /** - Should be called from a UDF's xStep(), xValue(), and xInverse() - methods, passing it that method's first argument and an initial - value for the persistent state. If there is currently no - mapping for the given context within the map, one is created - using the given initial value, else the existing one is used - and the 2nd argument is ignored. It returns a ValueHolder - which can be used to modify that state directly without - requiring that the client update the underlying map's entry. - -

The caller is obligated to eventually call - takeAggregateState() to clear the mapping. - */ - public ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ - final Long key = cx.getAggregateContext(true); - ValueHolder rc = null==key ? null : map.get(key); - if( null==rc ){ - map.put(key, rc = new ValueHolder<>(initialValue)); - } - return rc; - } - - /** - Should be called from a UDF's xFinal() method and passed that - method's first argument. This function removes the value - associated with cx.getAggregateContext() from the map and - returns it, returning null if no other UDF method has been - called to set up such a mapping. The latter condition will be - the case if a UDF is used in a statement which has no result - rows. - */ - public T takeAggregateState(sqlite3_context cx){ - final ValueHolder h = map.remove(cx.getAggregateContext(false)); - return null==h ? null : h.value; - } - } - } diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index b1d8df11cd..4657185658 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -929,7 +929,6 @@ public class Tester1 implements Runnable { "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ ") AS sum_y "+ "FROM twin ORDER BY x;"); - affirm( 0 == rc ); int n = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ final String s = sqlite3_column_text16(stmt, 0); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java index 173d775e62..6a38d4b530 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java @@ -51,9 +51,75 @@ public abstract class AggregateFunction implements SqlFunction { */ public void xDestroy() {} + /** + PerContextState assists aggregate and window functions in + managing their accumulator state across calls to the UDF's + callbacks. + +

T must be of a type which can be legally stored as a value in + java.util.HashMap. + +

If a given aggregate or window function is called multiple times + in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., + then the clients need some way of knowing which call is which so + that they can map their state between their various UDF callbacks + and reset it via xFinal(). This class takes care of such + mappings. + +

This class works by mapping + sqlite3_context.getAggregateContext() to a single piece of + state, of a client-defined type (the T part of this class), which + persists across a "matching set" of the UDF's callbacks. + +

This class is a helper providing commonly-needed functionality + - it is not required for use with aggregate or window functions. + Client UDFs are free to perform such mappings using custom + approaches. The provided {@link AggregateFunction} and {@link + WindowFunction} classes use this. + */ + public static final class PerContextState { + private final java.util.Map> map + = new java.util.HashMap<>(); + + /** + Should be called from a UDF's xStep(), xValue(), and xInverse() + methods, passing it that method's first argument and an initial + value for the persistent state. If there is currently no + mapping for the given context within the map, one is created + using the given initial value, else the existing one is used + and the 2nd argument is ignored. It returns a ValueHolder + which can be used to modify that state directly without + requiring that the client update the underlying map's entry. + +

The caller is obligated to eventually call + takeAggregateState() to clear the mapping. + */ + public ValueHolder getAggregateState(SqlFunction.Arguments args, T initialValue){ + final Long key = args.getContext().getAggregateContext(true); + ValueHolder rc = null==key ? null : map.get(key); + if( null==rc ){ + map.put(key, rc = new ValueHolder<>(initialValue)); + } + return rc; + } + + /** + Should be called from a UDF's xFinal() method and passed that + method's first argument. This function removes the value + associated with with the arguments' aggregate context from the + map and returns it, returning null if no other UDF method has + been called to set up such a mapping. The latter condition will + be the case if a UDF is used in a statement which has no result + rows. + */ + public T takeAggregateState(SqlFunction.Arguments args){ + final ValueHolder h = map.remove(args.getContext().getAggregateContext(false)); + return null==h ? null : h.value; + } + } + /** Per-invocation state for the UDF. */ - private final SqlFunction.PerContextState map = - new SqlFunction.PerContextState<>(); + private final PerContextState map = new PerContextState<>(); /** To be called from the implementation's xStep() method, as well diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 2e0be3cd63..311fdcff0e 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -22,6 +22,17 @@ import org.sqlite.jni.capi.sqlite3_value; */ public interface SqlFunction { + public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC; + public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS; + public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY; + public static final int UTF8 = CApi.SQLITE_UTF8; + public static final int UTF16 = CApi.SQLITE_UTF16; + // /** + // For Window functions only and is not currently bound because + // doing so may require exposing sqlite3_value for effective use. + // */ + // public static final int SUBTYPE = CApi.SQLITE_SUBTYPE; + /** The Arguments type is an abstraction on top of the lower-level UDF function argument types. It provides _most_ of the functionality @@ -49,6 +60,82 @@ public interface SqlFunction { this.length = this.args.length; } + /** + Returns the sqlite3_value at the given argument index or throws + an IllegalArgumentException exception if ndx is out of range. + */ + private sqlite3_value valueAt(int ndx){ + if(ndx<0 || ndx>=args.length){ + throw new IllegalArgumentException( + "SQL function argument index "+ndx+" is out of range." + ); + } + return args[ndx]; + } + + //! Returns the underlying sqlite3_context for these arguments. + sqlite3_context getContext(){return cx;} + + public int getArgCount(){ return args.length; } + + public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));} + public long getInt64(int argNdx){return CApi.sqlite3_value_int64(valueAt(argNdx));} + public double getDouble(int argNdx){return CApi.sqlite3_value_double(valueAt(argNdx));} + public byte[] getBlob(int argNdx){return CApi.sqlite3_value_blob(valueAt(argNdx));} + public byte[] getText(int argNdx){return CApi.sqlite3_value_text(valueAt(argNdx));} + public String getText16(int argNdx){return CApi.sqlite3_value_text16(valueAt(argNdx));} + public int getBytes(int argNdx){return CApi.sqlite3_value_bytes(valueAt(argNdx));} + public int getBytes16(int argNdx){return CApi.sqlite3_value_bytes16(valueAt(argNdx));} + public Object getObject(int argNdx){return CApi.sqlite3_value_java_object(valueAt(argNdx));} + public T getObject(int argNdx, Class type){ + return CApi.sqlite3_value_java_object(valueAt(argNdx), type); + } + + public int getType(int argNdx){return CApi.sqlite3_value_type(valueAt(argNdx));} + public int getSubtype(int argNdx){return CApi.sqlite3_value_subtype(valueAt(argNdx));} + public int getNumericType(int argNdx){return CApi.sqlite3_value_numeric_type(valueAt(argNdx));} + public int getNoChange(int argNdx){return CApi.sqlite3_value_nochange(valueAt(argNdx));} + public boolean getFromBind(int argNdx){return CApi.sqlite3_value_frombind(valueAt(argNdx));} + public int getEncoding(int argNdx){return CApi.sqlite3_value_encoding(valueAt(argNdx));} + + public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); } + public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); } + public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); } + public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);} + public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);} + public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);} + public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);} + public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);} + public void resultNull(){CApi.sqlite3_result_null(cx);} + public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));} + public void resultZeroBlob(long n){ + // Throw on error? If n is too big, + // sqlite3_result_error_toobig() is automatically called. + CApi.sqlite3_result_zeroblob64(cx, n); + } + + public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);} + public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);} + public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);} + public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);} + public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);} + + public void setAuxData(int argNdx, Object o){ + /* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html + + The value of the N parameter to these interfaces should be + non-negative. Future enhancements may make use of negative N + values to define new kinds of function caching behavior. + */ + valueAt(argNdx); + CApi.sqlite3_set_auxdata(cx, argNdx, o); + } + + public Object getAuxData(int argNdx){ + valueAt(argNdx); + return CApi.sqlite3_get_auxdata(cx, argNdx); + } + /** Wrapper for a single SqlFunction argument. Primarily intended for use with the Arguments class's Iterable interface. @@ -87,147 +174,6 @@ public interface SqlFunction { return java.util.Arrays.stream(proxies).iterator(); } - /** - Returns the sqlite3_value at the given argument index or throws - an IllegalArgumentException exception if ndx is out of range. - */ - private sqlite3_value valueAt(int ndx){ - if(ndx<0 || ndx>=args.length){ - throw new IllegalArgumentException( - "SQL function argument index "+ndx+" is out of range." - ); - } - return args[ndx]; - } - - sqlite3_context getContext(){return cx;} - - public int getArgCount(){ return args.length; } - - public int getInt(int arg){return CApi.sqlite3_value_int(valueAt(arg));} - public long getInt64(int arg){return CApi.sqlite3_value_int64(valueAt(arg));} - public double getDouble(int arg){return CApi.sqlite3_value_double(valueAt(arg));} - public byte[] getBlob(int arg){return CApi.sqlite3_value_blob(valueAt(arg));} - public byte[] getText(int arg){return CApi.sqlite3_value_text(valueAt(arg));} - public String getText16(int arg){return CApi.sqlite3_value_text16(valueAt(arg));} - public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));} - public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));} - public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));} - public T getObject(int arg, Class type){ - return CApi.sqlite3_value_java_object(valueAt(arg), type); - } - - public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));} - public int getSubtype(int arg){return CApi.sqlite3_value_subtype(valueAt(arg));} - public int getNumericType(int arg){return CApi.sqlite3_value_numeric_type(valueAt(arg));} - public int getNoChange(int arg){return CApi.sqlite3_value_nochange(valueAt(arg));} - public boolean getFromBind(int arg){return CApi.sqlite3_value_frombind(valueAt(arg));} - public int getEncoding(int arg){return CApi.sqlite3_value_encoding(valueAt(arg));} - - public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); } - public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); } - public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); } - public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);} - public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);} - public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);} - public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);} - public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);} - public void resultNull(){CApi.sqlite3_result_null(cx);} - public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));} - public void resultZeroBlob(long n){ - // Throw on error? If n is too big, - // sqlite3_result_error_toobig() is automatically called. - CApi.sqlite3_result_zeroblob64(cx, n); - } - - public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);} - public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);} - public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);} - public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);} - public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);} - - public void setAuxData(int arg, Object o){ - /* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html - - The value of the N parameter to these interfaces should be - non-negative. Future enhancements may make use of negative N - values to define new kinds of function caching behavior. - */ - valueAt(arg); - CApi.sqlite3_set_auxdata(cx, arg, o); - } - - public Object getAuxData(int arg){ - valueAt(arg); - return CApi.sqlite3_get_auxdata(cx, arg); - } - } - - /** - PerContextState assists aggregate and window functions in - managing their accumulator state across calls to the UDF's - callbacks. - -

T must be of a type which can be legally stored as a value in - java.util.HashMap. - -

If a given aggregate or window function is called multiple times - in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., - then the clients need some way of knowing which call is which so - that they can map their state between their various UDF callbacks - and reset it via xFinal(). This class takes care of such - mappings. - -

This class works by mapping - sqlite3_context.getAggregateContext() to a single piece of - state, of a client-defined type (the T part of this class), which - persists across a "matching set" of the UDF's callbacks. - -

This class is a helper providing commonly-needed functionality - - it is not required for use with aggregate or window functions. - Client UDFs are free to perform such mappings using custom - approaches. The provided {@link AggregateFunction} and {@link - WindowFunction} classes use this. - */ - public static final class PerContextState { - private final java.util.Map> map - = new java.util.HashMap<>(); - - /** - Should be called from a UDF's xStep(), xValue(), and xInverse() - methods, passing it that method's first argument and an initial - value for the persistent state. If there is currently no - mapping for the given context within the map, one is created - using the given initial value, else the existing one is used - and the 2nd argument is ignored. It returns a ValueHolder - which can be used to modify that state directly without - requiring that the client update the underlying map's entry. - -

The caller is obligated to eventually call - takeAggregateState() to clear the mapping. - */ - public ValueHolder getAggregateState(SqlFunction.Arguments args, T initialValue){ - final Long key = args.getContext().getAggregateContext(true); - ValueHolder rc = null==key ? null : map.get(key); - if( null==rc ){ - map.put(key, rc = new ValueHolder<>(initialValue)); - } - return rc; - } - - /** - Should be called from a UDF's xFinal() method and passed that - method's first argument. This function removes the value - associated with with the arguments' aggregate context from the - map and returns it, returning null if no other UDF method has - been called to set up such a mapping. The latter condition will - be the case if a UDF is used in a statement which has no result - rows. - */ - public T takeAggregateState(SqlFunction.Arguments args){ - final ValueHolder h = map.remove(args.getContext().getAggregateContext(false)); - return null==h ? null : h.value; - } } /** @@ -235,7 +181,7 @@ public interface SqlFunction { for use with the org.sqlite.jni.capi.ScalarFunction interface. */ static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction { - final ScalarFunction impl; + private final ScalarFunction impl; ScalarAdapter(ScalarFunction impl){ this.impl = impl; } @@ -261,8 +207,9 @@ public interface SqlFunction { Internal-use adapter for wrapping this package's AggregateFunction for use with the org.sqlite.jni.capi.AggregateFunction interface. */ - static final class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction { - final AggregateFunction impl; + static /*cannot be final without duplicating the whole body in WindowAdapter*/ + class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction { + private final AggregateFunction impl; AggregateAdapter(AggregateFunction impl){ this.impl = impl; } @@ -282,8 +229,9 @@ public interface SqlFunction { } /** - As for the xFinal() argument of the C API's sqlite3_create_function(). - If the proxied function throws, it is translated into a sqlite3_result_error(). + As for the xFinal() argument of the C API's + sqlite3_create_function(). If the proxied function throws, it + is translated into a sqlite3_result_error(). */ public void xFinal(sqlite3_context cx){ try{ @@ -298,4 +246,46 @@ public interface SqlFunction { } } + /** + Internal-use adapter for wrapping this package's WindowFunction + for use with the org.sqlite.jni.capi.WindowFunction interface. + */ + static final class WindowAdapter extends AggregateAdapter { + private final WindowFunction impl; + WindowAdapter(WindowFunction impl){ + super(impl); + this.impl = impl; + } + + /** + Proxies this.impl.xInverse(), adapting the call arguments to that + function's signature. If the proxied function throws, it is + translated to sqlite_result_error() with the exception's + message. + */ + public void xInverse(sqlite3_context cx, sqlite3_value[] args){ + try{ + impl.xInverse( new SqlFunction.Arguments(cx, args) ); + }catch(Exception e){ + CApi.sqlite3_result_error(cx, e); + } + } + + /** + As for the xValue() argument of the C API's sqlite3_create_window_function(). + If the proxied function throws, it is translated into a sqlite3_result_error(). + */ + public void xValue(sqlite3_context cx){ + try{ + impl.xValue( new SqlFunction.Arguments(cx, null) ); + }catch(Exception e){ + CApi.sqlite3_result_error(cx, e); + } + } + + public void xDestroy(){ + impl.xDestroy(); + } + } + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 25628e0805..016eb0b89c 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -29,6 +29,10 @@ import org.sqlite.jni.capi.OutputPointer; public final class Sqlite implements AutoCloseable { private sqlite3 db; + public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; + public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; + public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; @@ -120,7 +124,7 @@ public final class Sqlite implements AutoCloseable { return prepare(sql, 0); } - public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){ + public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.ScalarAdapter(f)); if( 0!=rc ) throw new SqliteException(db); @@ -130,7 +134,7 @@ public final class Sqlite implements AutoCloseable { this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } - public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){ + public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.AggregateAdapter(f)); if( 0!=rc ) throw new SqliteException(db); @@ -140,6 +144,16 @@ public final class Sqlite implements AutoCloseable { this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } + public void createFunction(String name, int nArg, int eTextRep, WindowFunction f){ + int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, + new SqlFunction.WindowAdapter(f)); + if( 0!=rc ) throw new SqliteException(db); + } + + public void createFunction(String name, int nArg, WindowFunction f){ + this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); + } + /** Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to create new instances. @@ -223,11 +237,19 @@ public final class Sqlite implements AutoCloseable { } /** - Works like sqlite3_step() but throws SqliteException for any - result other than 0, SQLITE_ROW, or SQLITE_DONE. + Works like sqlite3_step() but returns true for SQLITE_ROW, + false for SQLITE_DONE, and throws SqliteException for any other + result. */ - public int step(){ - return checkRc(sqlite3_step(thisStmt())); + public boolean step(){ + switch(checkRc(sqlite3_step(thisStmt()))){ + case CApi.SQLITE_ROW: return true; + case CApi.SQLITE_DONE: return false; + default: + throw new IllegalStateException( + "This \"cannot happen\": all possible result codes were checked already." + ); + } /* Potential signature change TODO: diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 8fb36a3b54..ca4e6d0521 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -129,6 +129,11 @@ public class Tester2 implements Runnable { execSql(db, String.join("", sql)); } + /** + Executes all SQL statements in the given string. If throwOnError + is true then it will throw for any prepare/step errors, else it + will return the corresponding non-0 result code. + */ public static int execSql(Sqlite dbw, boolean throwOnError, String sql){ final sqlite3 db = dbw.nativeHandle(); OutputPointer.Int32 oTail = new OutputPointer.Int32(); @@ -145,7 +150,7 @@ public class Tester2 implements Runnable { } if( 0==sqlChunk.length ) break; rc = CApi.sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail); - if(throwOnError) affirm(0 == rc); + if( throwOnError ) affirm(0 == rc); else if( 0!=rc ) break; pos = oTail.value; stmt = outStmt.take(); @@ -158,7 +163,7 @@ public class Tester2 implements Runnable { } CApi.sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer()); - if(0!=rc && CApi.SQLITE_ROW!=rc && CApi.SQLITE_DONE!=rc){ + if(CApi.SQLITE_DONE!=rc){ break; } } @@ -194,9 +199,9 @@ public class Tester2 implements Runnable { } Sqlite openDb(String name){ - final Sqlite db = Sqlite.open(name, CApi.SQLITE_OPEN_READWRITE| - CApi.SQLITE_OPEN_CREATE| - CApi.SQLITE_OPEN_EXRESCODE); + final Sqlite db = Sqlite.open(name, Sqlite.OPEN_READWRITE| + Sqlite.OPEN_CREATE| + Sqlite.OPEN_EXRESCODE); ++metrics.dbOpen; return db; } @@ -245,7 +250,7 @@ public class Tester2 implements Runnable { catch(Exception ex){ e = ex; } affirm( null!=e ); e = null; - affirm( CApi.SQLITE_ROW == stmt.step() ); + affirm( stmt.step() ); try{ stmt.columnInt(1); } catch(Exception ex){ e = ex; } affirm( null!=e ); @@ -254,16 +259,16 @@ public class Tester2 implements Runnable { affirm( 17L == stmt.columnInt64(0) ); affirm( 17.0 == stmt.columnDouble(0) ); affirm( "17".equals(stmt.columnText16(0)) ); - affirm( CApi.SQLITE_DONE == stmt.step() ); + affirm( !stmt.step() ); stmt.reset(); - affirm( CApi.SQLITE_ROW == stmt.step() ); - affirm( CApi.SQLITE_DONE == stmt.step() ); + affirm( stmt.step() ); + affirm( !stmt.step() ); affirm( 0 == stmt.finalizeStmt() ); affirm( null==stmt.nativeHandle() ); stmt = db.prepare("SELECT ?"); stmt.bindObject(1, db); - affirm( CApi.SQLITE_ROW == stmt.step() ); + affirm( stmt.step() ); affirm( db==stmt.columnObject(0) ); affirm( db==stmt.columnObject(0, Sqlite.class ) ); affirm( null==stmt.columnObject(0, Sqlite.Stmt.class ) ); @@ -328,7 +333,7 @@ public class Tester2 implements Runnable { /* ------------------^^^^^^^^^^^ ensures that we're handling sqlite3_aggregate_context() properly. */ ); - affirm( CApi.SQLITE_ROW==q.step() ); + affirm( q.step() ); affirm( 15==q.columnInt(0) ); q.finalizeStmt(); q = null; @@ -336,7 +341,7 @@ public class Tester2 implements Runnable { db.createFunction("summerN", -1, f); q = db.prepare("select summerN(1,8,9), summerN(2,3,4)"); - affirm( CApi.SQLITE_ROW==q.step() ); + affirm( q.step() ); affirm( 18==q.columnInt(0) ); affirm( 9==q.columnInt(1) ); q.finalizeStmt(); @@ -350,6 +355,63 @@ public class Tester2 implements Runnable { /* because we've bound the same instance twice */ ); } + private void testUdfWindow(){ + final Sqlite db = openDb(); + /* Example window function, table, and results taken from: + https://sqlite.org/windowfunctions.html#udfwinfunc */ + final WindowFunction func = new WindowFunction(){ + //! Impl of xStep() and xInverse() + private void xStepInverse(SqlFunction.Arguments args, int v){ + this.getAggregateState(args,0).value += v; + } + @Override public void xStep(SqlFunction.Arguments args){ + this.xStepInverse(args, args.getInt(0)); + } + @Override public void xInverse(SqlFunction.Arguments args){ + this.xStepInverse(args, -args.getInt(0)); + } + //! Impl of xFinal() and xValue() + private void xFinalValue(SqlFunction.Arguments args, Integer v){ + if(null == v) args.resultNull(); + else args.resultInt(v); + } + @Override public void xFinal(SqlFunction.Arguments args){ + xFinalValue(args, this.takeAggregateState(args)); + affirm( null == this.getAggregateState(args,null).value ); + } + @Override public void xValue(SqlFunction.Arguments args){ + xFinalValue(args, this.getAggregateState(args,null).value); + } + }; + db.createFunction("winsumint", 1, func); + execSql(db, new String[] { + "CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES", + "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)" + }); + final Sqlite.Stmt stmt = db.prepare( + "SELECT x, winsumint(y) OVER ("+ + "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ + ") AS sum_y "+ + "FROM twin ORDER BY x;" + ); + int n = 0; + while( stmt.step() ){ + final String s = stmt.columnText16(0); + final int i = stmt.columnInt(1); + switch(++n){ + case 1: affirm( "a".equals(s) && 9==i ); break; + case 2: affirm( "b".equals(s) && 12==i ); break; + case 3: affirm( "c".equals(s) && 16==i ); break; + case 4: affirm( "d".equals(s) && 12==i ); break; + case 5: affirm( "e".equals(s) && 9==i ); break; + default: affirm( false /* cannot happen */ ); + } + } + stmt.close(); + affirm( 5 == n ); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java new file mode 100644 index 0000000000..479fc74d7f --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java @@ -0,0 +1,46 @@ +/* +** 2023-10-16 +** +** 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 is part of the wrapper1 interface for sqlite3. +*/ +package org.sqlite.jni.wrapper1; +import org.sqlite.jni.capi.CApi; +import org.sqlite.jni.annotation.*; +import org.sqlite.jni.capi.sqlite3_context; +import org.sqlite.jni.capi.sqlite3_value; + +/** + A SqlFunction implementation for window functions. The T type + represents the type of data accumulated by this function while it + works. e.g. a SUM()-like UDF might use Integer or Long and a + CONCAT()-like UDF might use a StringBuilder or a List. +*/ +public abstract class WindowFunction extends AggregateFunction { + + /** + As for the xInverse() argument of the C API's + sqlite3_create_window_function(). If this function throws, the + exception is reported via sqlite3_result_error(). + */ + public abstract void xInverse(SqlFunction.Arguments args); + + /** + As for the xValue() argument of the C API's + sqlite3_create_window_function(). If this function throws, it is + translated into sqlite3_result_error(). + + Note that the passed-in object will not actually contain any + arguments for xValue() but will contain the context object needed + for setting the call's result or error state. + */ + public abstract void xValue(SqlFunction.Arguments args); + +} diff --git a/manifest b/manifest index d20131a353..f6e1c2056a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\sflesh\sout\sand\ssimplify\sthe\sAPIs\sfor\sbinding\sand\sfetching\sarbitrary\sJava\sobjects. -D 2023-10-22T14:25:37.634 +C Add\shigh-level\swindow\sfunction\swrapper\sto\sthe\sJNI\swrapper1\sinterface. +D 2023-10-22T23:36:16.690 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 -F ext/jni/GNUmakefile 5c3ac326bf3853486ebe0d70819abc790cc65c412182ce4ebd5012b008d9b059 +F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c dcd6534b65b732ad927a49185c76c76abbd5ccadfa972d02f699abc45678e329 @@ -245,7 +245,7 @@ F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c -F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java bc29e986c866c2ddbbb9f935f5b7264c1c1026864e50a4a735192864f75e37c0 +F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca @@ -263,11 +263,11 @@ F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17 F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java 105e324d09c207100485e7667ad172e64322c62426bb49b547e9b0dc9c33f5f0 -F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java fef556adbc3624292423083a648bdf97fa8a4f6b3b6577c9660dd7bd6a6d3c4a +F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385155fa3b8011a5cca0bb3c28468c7131c1a5 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 5c4e7ba5034aeb5c5be0361b9fa0c23fe993774e634750c775d7ad8fa19b22f3 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab @@ -291,13 +291,14 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java a8e88c3783d21cec51b0748568a96653 F ext/jni/src/org/sqlite/jni/fts5/fts5_extension_function.java 9e2b954d210d572552b28aca523b272fae14bd41e318921b22f65b728d5bf978 F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ade23d329843f809cd0d0f4f1a2856da6e6b4d90 F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e -F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a +F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 92c28b9de358407c8c5e772e0408db528e47eeeb50ffd87b86563a5f078198ad -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 882e345d925a79b575b1182efd816dcc72d6814922b4f58e7f4d29f04ece1f64 +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 585309311ffce6f39626024bf2ea3add91339f6a146b674720165c1955efbe68 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e6cdb5fe1b01a592ba5ca6ae7d11681a85d081786ce8d046ef631a08ae82dde F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java e224efb77dae2f0abe18f2010c0eb5a09df991f2743597a1aff7f9283f71da7d +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af +F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0 @@ -2136,8 +2137,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 60a0e82db26270af9d0a5f55c6173e4fd0bdc90a885e838480ed75f8ef193287 -R 5a1e4de3384c2859d02b694c480544f0 +P 89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f +R 160b97c24140d26b449a4e16b3331ec4 U stephan -Z 0b6f35d12f929147f1a8dbbd84db96f1 +Z 05c9adecf2f51f54547f1d6b2c6aaa3c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a425b28981..fbcdd1ab29 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f \ No newline at end of file +a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708 \ No newline at end of file From 90a5617b556c78d6890dc272e6312dac981614a8 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 23 Oct 2023 01:34:17 +0000 Subject: [PATCH 082/347] Add many more high-level wrappers to the JNI wrapper1 API. Correct the JNI bindings of sqlite3_strglob/strlike() to compare as the core lib does if their glob argument is NULL and the other is not. FossilOrigin-Name: 55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c --- ext/jni/src/c/sqlite3-jni.c | 19 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 276 +++++++++++++++++- .../sqlite/jni/wrapper1/SqliteException.java | 31 +- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 310 insertions(+), 34 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 86a4930917..ed89b86e8b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2182,7 +2182,9 @@ S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( return S3JniCast_P2L(p); } -/* Central auto-extension handler. */ +/* +** Central auto-extension runner for auto-extensions created in Java. +*/ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ int rc = 0; @@ -3537,9 +3539,16 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)( S3JniApi(sqlite3_errstr(),jstring,1errstr)( JniArgsEnvClass, jint rcCode ){ - jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode)) - /* We know these values to be plain ASCII, so pose no MUTF-8 - ** incompatibility */; + jstring rv; + const char * z = sqlite3_errstr((int)rcCode); + if( !z ){ + /* This hypothetically cannot happen, but we'll behave like the + low-level library would in such a case... */ + z = "unknown error"; + } + rv = (*env)->NewStringUTF(env, z) + /* We know these values to be plain ASCII, so pose no MUTF-8 + ** incompatibility */; s3jni_oom_check( rv ); return rv; } @@ -4615,7 +4624,7 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyteArray baG, jbyteArray baT, jint escLike){ int rc = 0; jbyte * const pG = s3jni_jbyteArray_bytes(baG); - jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0; + jbyte * const pT = s3jni_jbyteArray_bytes(baT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 016eb0b89c..b464bd7d5c 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -32,6 +32,34 @@ public final class Sqlite implements AutoCloseable { public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; + public static final int TXN_NONE = CApi.SQLITE_TXN_NONE; + public static final int TXN_READ = CApi.SQLITE_TXN_READ; + public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE; + + public static final int STATUS_MEMORY_USED = CApi.SQLITE_STATUS_MEMORY_USED; + public static final int STATUS_PAGECACHE_USED = CApi.SQLITE_STATUS_PAGECACHE_USED; + public static final int STATUS_PAGECACHE_OVERFLOW = CApi.SQLITE_STATUS_PAGECACHE_OVERFLOW; + public static final int STATUS_MALLOC_SIZE = CApi.SQLITE_STATUS_MALLOC_SIZE; + public static final int STATUS_PARSER_STACK = CApi.SQLITE_STATUS_PARSER_STACK; + public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE; + public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT; + + public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH; + public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH; + public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN; + public static final int LIMIT_EXPR_DEPTH = CApi.SQLITE_LIMIT_EXPR_DEPTH; + public static final int LIMIT_COMPOUND_SELECT = CApi.SQLITE_LIMIT_COMPOUND_SELECT; + public static final int LIMIT_VDBE_OP = CApi.SQLITE_LIMIT_VDBE_OP; + public static final int LIMIT_FUNCTION_ARG = CApi.SQLITE_LIMIT_FUNCTION_ARG; + public static final int LIMIT_ATTACHED = CApi.SQLITE_LIMIT_ATTACHED; + public static final int LIMIT_LIKE_PATTERN_LENGTH = CApi.SQLITE_LIMIT_LIKE_PATTERN_LENGTH; + public static final int LIMIT_VARIABLE_NUMBER = CApi.SQLITE_LIMIT_VARIABLE_NUMBER; + public static final int LIMIT_TRIGGER_DEPTH = CApi.SQLITE_LIMIT_TRIGGER_DEPTH; + public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS; + + public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT; + public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE; + public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB; //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ @@ -67,6 +95,33 @@ public final class Sqlite implements AutoCloseable { return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null); } + public static String libVersion(){ + return CApi.sqlite3_libversion(); + } + + public static int libVersionNumber(){ + return CApi.sqlite3_libversion_number(); + } + + public static String libSourceId(){ + return CApi.sqlite3_sourceid(); + } + + /** + As per sqlite3_status64(), but returns its current and high-water + results as a two-element array. Throws if the first argument is + not one of the STATUS_... constants. + */ + public long[] libStatus(int op, boolean resetStats){ + org.sqlite.jni.capi.OutputPointer.Int64 pCurrent = + new org.sqlite.jni.capi.OutputPointer.Int64(); + org.sqlite.jni.capi.OutputPointer.Int64 pHighwater = + new org.sqlite.jni.capi.OutputPointer.Int64(); + final int rc = CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats); + checkRc(rc); + return new long[] {pCurrent.value, pHighwater.value}; + } + @Override public void close(){ if(null!=this.db){ this.db.close(); @@ -74,6 +129,22 @@ public final class Sqlite implements AutoCloseable { } } + /** + Returns the value of the native library's build-time value of the + SQLITE_THREADSAFE build option. + */ + public static int libThreadsafe(){ + return CApi.sqlite3_threadsafe(); + } + + public static boolean strglob(String glob, String txt){ + return 0==CApi.sqlite3_strglob(glob, txt); + } + + public static boolean strlike(String glob, String txt, char escChar){ + return 0==CApi.sqlite3_strlike(glob, txt, escChar); + } + /** Returns this object's underlying native db handle, or null if this instance has been closed. This is very specifically not @@ -94,17 +165,21 @@ public final class Sqlite implements AutoCloseable { /** If rc!=0, throws an SqliteException. If this db is currently - opened, the error state is extracted from it, else only the - string form of rc is used. + opened and has non-0 sqlite3_errcode(), the error state is + extracted from it, else only the string form of rc is used. It is + the caller's responsibility to filter out non-error codes such as + SQLITE_ROW and SQLITE_DONE before calling this. */ - private void affirmRcOk(int rc){ + private void checkRc(int rc){ if( 0!=rc ){ - if( null==db ) throw new SqliteException(rc); + if( null==db || 0==sqlite3_errcode(db)) throw new SqliteException(rc); else throw new SqliteException(db); } } /** + prepFlags must be 0 or a bitmask of the PREPARE_... constants. + prepare() TODOs include: - overloads taking byte[] and ByteBuffer. @@ -116,8 +191,20 @@ public final class Sqlite implements AutoCloseable { public Stmt prepare(String sql, int prepFlags){ final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); final int rc = sqlite3_prepare_v3(thisDb(), sql, prepFlags, out); - affirmRcOk(rc); - return new Stmt(this, out.take()); + checkRc(rc); + final sqlite3_stmt q = out.take(); + if( null==q ){ + /* The C-level API treats input which is devoid of SQL + statements (e.g. all comments or an empty string) as success + but returns a NULL sqlite3_stmt object. In higher-level APIs, + wrapping a "successful NULL" object that way is tedious to + use because it forces clients and/or wrapper-level code to + check for that unusual case. In practice, higher-level + bindings are generally better-served by treating empty SQL + input as an error. */ + throw new IllegalArgumentException("Input contains no SQL statements."); + } + return new Stmt(this, q); } public Stmt prepare(String sql){ @@ -154,6 +241,183 @@ public final class Sqlite implements AutoCloseable { this.createFunction(name, nArg, CApi.SQLITE_UTF8, f); } + public long changes(){ + return CApi.sqlite3_changes64(thisDb()); + } + + public long totalChanges(){ + return CApi.sqlite3_total_changes64(thisDb()); + } + + public long lastInsertRowId(){ + return CApi.sqlite3_last_insert_rowid(thisDb()); + } + + public void setLastInsertRowId(long rowId){ + CApi.sqlite3_set_last_insert_rowid(thisDb(), rowId); + } + + public void interrupt(){ + CApi.sqlite3_interrupt(thisDb()); + } + + public boolean isInterrupted(){ + return CApi.sqlite3_is_interrupted(thisDb()); + } + + public boolean isAutoCommit(){ + return CApi.sqlite3_get_autocommit(thisDb()); + } + + public void setBusyTimeout(int ms){ + checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms)); + } + + /** + Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ, + or TXN_WRITE to denote this database's current transaction state + for the given schema name (or the most restrictive state of any + schema if zSchema is null). + */ + public int transactionState(String zSchema){ + return CApi.sqlite3_txn_state(thisDb(), zSchema); + } + + /** + Analog to sqlite3_db_name(). Returns null if passed an unknown + index. + */ + public String dbName(int dbNdx){ + return CApi.sqlite3_db_name(thisDb(), dbNdx); + } + + /** + Analog to sqlite3_db_filename(). Returns null if passed an + unknown db name. + */ + public String dbFileName(String dbName){ + return CApi.sqlite3_db_filename(thisDb(), dbName); + } + + /** + Analog to the variant of sqlite3_db_config() for configuring the + SQLITE_DBCONFIG_MAINDBNAME option. Throws on error. + */ + public void setMainDbName(String name){ + checkRc( + CApi.sqlite3_db_config(thisDb(), CApi.SQLITE_DBCONFIG_MAINDBNAME, + name) + ); + } + + /** + Analog to sqlite3_db_readonly() but throws an SqliteException + with result code SQLITE_NOTFOUND if given an unknown database + name. + */ + public boolean readOnly(String dbName){ + final int rc = CApi.sqlite3_db_readonly(thisDb(), dbName); + if( 0==rc ) return false; + else if( rc>0 ) return true; + throw new SqliteException(CApi.SQLITE_NOTFOUND); + } + + /** + Analog to sqlite3_db_release_memory(). + */ + public void releaseMemory(){ + CApi.sqlite3_db_release_memory(thisDb()); + } + + /** + Analog to sqlite3_release_memory(). + */ + public static int releaseMemory(int n){ + return CApi.sqlite3_release_memory(n); + } + + /** + Analog to sqlite3_limit(). limitId must be one of the + LIMIT_... constants. + + Returns the old limit for the given option. If newLimit is + negative, it returns the old limit without modifying the limit. + + If sqlite3_limit() returns a negative value, this function throws + an SqliteException with the SQLITE_RANGE result code but no + further error info (because that case does not qualify as a + db-level error). Such errors may indicate an invalid argument + value or an invalid range for newLimit (the underlying function + does not differentiate between those). + */ + public int limit(int limitId, int newLimit){ + final int rc = CApi.sqlite3_limit(thisDb(), limitId, newLimit); + if( rc<0 ){ + throw new SqliteException(CApi.SQLITE_RANGE); + } + return rc; + } + + /** + Analog to sqlite3_errstr(). + */ + static String errstr(int resultCode){ + return CApi.sqlite3_errstr(resultCode); + } + + /** + A wrapper object for use with tableColumnMetadata(). They are + created and populated only via that interface. + */ + public final class TableColumnMetadata { + Boolean pNotNull = null; + Boolean pPrimaryKey = null; + Boolean pAutoinc = null; + String pzCollSeq = null; + String pzDataType = null; + + private TableColumnMetadata(){} + + public String getDataType(){ return pzDataType; } + public String getCollation(){ return pzCollSeq; } + public boolean isNotNull(){ return pNotNull; } + public boolean isPrimaryKey(){ return pPrimaryKey; } + public boolean isAutoincrement(){ return pAutoinc; } + } + + /** + Returns data about a database, table, and (optionally) column + (which may be null), as per sqlite3_table_column_metadata(). + Throws if passed invalid arguments, else returns the result as a + new TableColumnMetadata object. + */ + TableColumnMetadata tableColumnMetadata( + String zDbName, String zTableName, String zColumnName + ){ + org.sqlite.jni.capi.OutputPointer.String pzDataType + = new org.sqlite.jni.capi.OutputPointer.String(); + org.sqlite.jni.capi.OutputPointer.String pzCollSeq + = new org.sqlite.jni.capi.OutputPointer.String(); + org.sqlite.jni.capi.OutputPointer.Bool pNotNull + = new org.sqlite.jni.capi.OutputPointer.Bool(); + org.sqlite.jni.capi.OutputPointer.Bool pPrimaryKey + = new org.sqlite.jni.capi.OutputPointer.Bool(); + org.sqlite.jni.capi.OutputPointer.Bool pAutoinc + = new org.sqlite.jni.capi.OutputPointer.Bool(); + final int rc = CApi.sqlite3_table_column_metadata( + thisDb(), zDbName, zTableName, zColumnName, + pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc + ); + checkRc(rc); + TableColumnMetadata rv = new TableColumnMetadata(); + rv.pzDataType = pzDataType.value; + rv.pzCollSeq = pzCollSeq.value; + rv.pNotNull = pNotNull.value; + rv.pPrimaryKey = pPrimaryKey.value; + rv.pAutoinc = pAutoinc.value; + return rv; + } + /** Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to create new instances. diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java index 111f004db4..27cfc0e6bb 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java @@ -12,7 +12,7 @@ ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; -import static org.sqlite.jni.capi.CApi.*; +import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; /** @@ -22,10 +22,10 @@ import org.sqlite.jni.capi.sqlite3; and C via JNI. */ public final class SqliteException extends java.lang.RuntimeException { - int errCode = SQLITE_ERROR; - int xerrCode = SQLITE_ERROR; - int errOffset = -1; - int sysErrno = 0; + private int errCode = CApi.SQLITE_ERROR; + private int xerrCode = CApi.SQLITE_ERROR; + private int errOffset = -1; + private int sysErrno = 0; /** Records the given error string and uses SQLITE_ERROR for both the @@ -38,10 +38,13 @@ public final class SqliteException extends java.lang.RuntimeException { /** Uses sqlite3_errstr(sqlite3ResultCode) for the error string and sets both the error code and extended error code to the given - value. + value. This approach includes no database-level information and + systemErrno() will be 0, so is intended only for use with sqlite3 + APIs for which a result code is not an error but which the + higher-level wrapper should treat as one. */ public SqliteException(int sqlite3ResultCode){ - super(sqlite3_errstr(sqlite3ResultCode)); + super(CApi.sqlite3_errstr(sqlite3ResultCode)); errCode = xerrCode = sqlite3ResultCode; } @@ -50,16 +53,16 @@ public final class SqliteException extends java.lang.RuntimeException { must refer to an opened db object). Note that this does NOT close the db. - Design note: closing the db on error is likely only useful during + Design note: closing the db on error is really only useful during a failed db-open operation, and the place(s) where that can happen are inside this library, not client-level code. */ SqliteException(sqlite3 db){ - super(sqlite3_errmsg(db)); - errCode = sqlite3_errcode(db); - xerrCode = sqlite3_extended_errcode(db); - errOffset = sqlite3_error_offset(db); - sysErrno = sqlite3_system_errno(db); + super(CApi.sqlite3_errmsg(db)); + errCode = CApi.sqlite3_errcode(db); + xerrCode = CApi.sqlite3_extended_errcode(db); + errOffset = CApi.sqlite3_error_offset(db); + sysErrno = CApi.sqlite3_system_errno(db); } /** @@ -71,7 +74,7 @@ public final class SqliteException extends java.lang.RuntimeException { } public SqliteException(Sqlite.Stmt stmt){ - this( stmt.db() ); + this(stmt.db()); } public int errcode(){ return errCode; } diff --git a/manifest b/manifest index f6e1c2056a..173d2bbaa7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\shigh-level\swindow\sfunction\swrapper\sto\sthe\sJNI\swrapper1\sinterface. -D 2023-10-22T23:36:16.690 +C Add\smany\smore\shigh-level\swrappers\sto\sthe\sJNI\swrapper1\sAPI.\sCorrect\sthe\sJNI\sbindings\sof\ssqlite3_strglob/strlike()\sto\scompare\sas\sthe\score\slib\sdoes\sif\stheir\sglob\sargument\sis\sNULL\sand\sthe\sother\sis\snot. +D 2023-10-23T01:34:17.318 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -239,7 +239,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c dcd6534b65b732ad927a49185c76c76abbd5ccadfa972d02f699abc45678e329 +F ext/jni/src/c/sqlite3-jni.c 21a218f50cfae116c5dbe780fd14181338e5b608dd6d52751e4a982c1b41c877 F ext/jni/src/c/sqlite3-jni.h e839090f5ec35aa96983a5621659e55ef897dc0522242fd00f107028ef5e7dd5 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -294,8 +294,8 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 585309311ffce6f39626024bf2ea3add91339f6a146b674720165c1955efbe68 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e6cdb5fe1b01a592ba5ca6ae7d11681a85d081786ce8d046ef631a08ae82dde -F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fae71b7454fa3d9243ad26c80e55b590c044a0f0a23d18cae21f0cfa3a92a969 +F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 @@ -2137,8 +2137,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 89fecf1dd8b97941f9b45130a3c8a67af36ec65cc6f70f5026c569c058a4963f -R 160b97c24140d26b449a4e16b3331ec4 +P a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708 +R ba0772b5db9c34b23be02455e60dfd03 U stephan -Z 05c9adecf2f51f54547f1d6b2c6aaa3c +Z eac6f822004436c39a1705e9823cdae5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fbcdd1ab29..f6f9688333 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708 \ No newline at end of file +55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c \ No newline at end of file From 334b6fe418a504129959bc6bbc4ef3e044887244 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 23 Oct 2023 02:06:27 +0000 Subject: [PATCH 083/347] Add missing JNI sqlite3_result_subtype() binding. FossilOrigin-Name: ac920b5386db525c794f4cae332dedcc709cac356f6fb85f3e92bc0a34602511 --- ext/jni/src/c/sqlite3-jni.c | 7 ++++++ ext/jni/src/c/sqlite3-jni.h | 24 ++++++++++++------- ext/jni/src/org/sqlite/jni/capi/CApi.java | 12 ++++++---- .../org/sqlite/jni/wrapper1/SqlFunction.java | 1 + manifest | 18 +++++++------- manifest.uuid | 2 +- 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index ed89b86e8b..3ec0916b45 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -4396,6 +4396,13 @@ S3JniApi(sqlite3_result_null(),void,1result_1null)( sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); } +S3JniApi(sqlite3_result_subtype(),void,1result_1subtype)( + JniArgsEnvClass, jobject jpCx, jint v +){ + sqlite3_result_subtype(PtrGet_sqlite3_context(jpCx), (unsigned int)v); +} + + S3JniApi(sqlite3_result_text(),void,1result_1text)( JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax ){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 6c4d07fc43..e4393ddd8b 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1687,14 +1687,6 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1nom JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1code (JNIEnv *, jclass, jobject, jint); -/* - * Class: org_sqlite_jni_capi_CApi - * Method: sqlite3_result_null - * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)V - */ -JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1null - (JNIEnv *, jclass, jobject); - /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_int @@ -1719,6 +1711,22 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1int64 JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object (JNIEnv *, jclass, jobject, jobject); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_result_null + * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)V + */ +JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1null + (JNIEnv *, jclass, jobject); + +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_result_subtype + * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V + */ +JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1subtype + (JNIEnv *, jclass, jobject, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_value diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index ff5da01c65..d155060999 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -1462,10 +1462,6 @@ public final class CApi { @NotNull sqlite3_context cx, int c ); - public static native void sqlite3_result_null( - @NotNull sqlite3_context cx - ); - public static native void sqlite3_result_int( @NotNull sqlite3_context cx, int v ); @@ -1494,6 +1490,10 @@ public final class CApi { @NotNull sqlite3_context cx, @NotNull Object o ); + public static native void sqlite3_result_null( + @NotNull sqlite3_context cx + ); + public static void sqlite3_result_set( @NotNull sqlite3_context cx, @NotNull Boolean v ){ @@ -1554,6 +1554,10 @@ public final class CApi { else sqlite3_result_blob(cx, blob, blob.length); } + public static native void sqlite3_result_subtype( + @NotNull sqlite3_context cx, int val + ); + public static native void sqlite3_result_value( @NotNull sqlite3_context cx, @NotNull sqlite3_value v ); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 311fdcff0e..2d0ebfaf32 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -108,6 +108,7 @@ public interface SqlFunction { public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);} public void resultNull(){CApi.sqlite3_result_null(cx);} public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));} + public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);} public void resultZeroBlob(long n){ // Throw on error? If n is too big, // sqlite3_result_error_toobig() is automatically called. diff --git a/manifest b/manifest index 173d2bbaa7..d8a1fb48cd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smany\smore\shigh-level\swrappers\sto\sthe\sJNI\swrapper1\sAPI.\sCorrect\sthe\sJNI\sbindings\sof\ssqlite3_strglob/strlike()\sto\scompare\sas\sthe\score\slib\sdoes\sif\stheir\sglob\sargument\sis\sNULL\sand\sthe\sother\sis\snot. -D 2023-10-23T01:34:17.318 +C Add\smissing\sJNI\ssqlite3_result_subtype()\sbinding. +D 2023-10-23T02:06:27.564 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -239,8 +239,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 21a218f50cfae116c5dbe780fd14181338e5b608dd6d52751e4a982c1b41c877 -F ext/jni/src/c/sqlite3-jni.h e839090f5ec35aa96983a5621659e55ef897dc0522242fd00f107028ef5e7dd5 +F ext/jni/src/c/sqlite3-jni.c d6a71910c41f6de49fdd7b769b1b814e5ccdccc8bbda2897dcf61ff5fccf1f32 +F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -249,7 +249,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java f23e8139cdb1d9c850f28ccf95384e37239cee47fd683bfb199f50aa7ce011fc +F ext/jni/src/org/sqlite/jni/capi/CApi.java 23d4073b34b5c07dd412d18395c9db79779bab059164316e14a60b33ec473cff F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 @@ -293,7 +293,7 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 585309311ffce6f39626024bf2ea3add91339f6a146b674720165c1955efbe68 +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 9be20f3ba6edbe63285224d5d640dbfb0d84fb8cf5134084e3ae40004ec9d6d9 F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fae71b7454fa3d9243ad26c80e55b590c044a0f0a23d18cae21f0cfa3a92a969 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4 @@ -2137,8 +2137,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 a27e7471231a24864cbd04b77cbc4b336ce180d738a36ce4318543e2666ed708 -R ba0772b5db9c34b23be02455e60dfd03 +P 55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c +R c2c51077b7eb5ed8cd8afa840132d8db U stephan -Z eac6f822004436c39a1705e9823cdae5 +Z 977f0702c9741cb2cb4a8926563a23c6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f6f9688333..fe7e4a2469 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c \ No newline at end of file +ac920b5386db525c794f4cae332dedcc709cac356f6fb85f3e92bc0a34602511 \ No newline at end of file From 26422f823fe813d45543a43412e98ddab16360a5 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 27 Oct 2023 21:05:50 +0000 Subject: [PATCH 084/347] Export sqlite3_get_autocommit() to WASM and add tests for it. FossilOrigin-Name: 7cb6d3cd3926882240c91a4b90fdf237b931653295d8d94b05ed885dcae59608 --- ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 1 + ext/wasm/api/sqlite3-api-glue.js | 1 + ext/wasm/tester1.c-pp.js | 6 +++++- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index ad2872d839..57cd61eb9d 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -63,6 +63,7 @@ _sqlite3_file_control _sqlite3_finalize _sqlite3_free _sqlite3_get_auxdata +_sqlite3_get_autocommit _sqlite3_initialize _sqlite3_keyword_count _sqlite3_keyword_name diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index f23a02366b..29efb3e07b 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -188,6 +188,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], ["sqlite3_finalize", "int", "sqlite3_stmt*"], ["sqlite3_free", undefined,"*"], + ["sqlite3_get_autocommit", "int", "sqlite3*"], ["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"], ["sqlite3_initialize", undefined], /*["sqlite3_interrupt", undefined, "sqlite3*" diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 92d763f1ba..c26bce25be 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -2644,13 +2644,17 @@ globalThis.sqlite3InitModule = sqlite3InitModule; T.assert( 0 === rc /*void pointer*/ ); // Commit hook... + T.assert( 0!=capi.sqlite3_get_autocommit(db) ); db.exec("BEGIN; SELECT 1; COMMIT"); T.assert(0 === countCommit, "No-op transactions (mostly) do not trigger commit hook."); db.exec("BEGIN EXCLUSIVE; SELECT 1; COMMIT"); T.assert(1 === countCommit, "But EXCLUSIVE transactions do."); - db.transaction((d)=>{d.exec("create table t(a)");}); + db.transaction((d)=>{ + T.assert( 0==capi.sqlite3_get_autocommit(db) ); + d.exec("create table t(a)"); + }); T.assert(2 === countCommit); // Rollback hook: diff --git a/manifest b/manifest index d8a1fb48cd..dbfd82067a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smissing\sJNI\ssqlite3_result_subtype()\sbinding. -D 2023-10-23T02:06:27.564 +C Export\ssqlite3_get_autocommit()\sto\sWASM\sand\sadd\stests\sfor\sit. +D 2023-10-27T21:05:50.750 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -570,7 +570,7 @@ F ext/wasm/SQLTester/SQLTester.mjs ec2f6ba63a0f2f0562941a0fb8e46b7dc55589711513f F ext/wasm/SQLTester/SQLTester.run.mjs c72b7fe2072d05992f7a3d8c6a1d34e95712513ceabe40849784e24e41c84638 F ext/wasm/SQLTester/index.html 3f8a016df0776be76605abf20e815ecaafbe055abac0e1fe5ea080e7846b760d F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api f442460ed9a109e637dd3ea1caa4489553ad9414e8988118b208bb7a4bbece6b F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md 5eb44fa02e9c693a1884a3692428647894b0380b24bca120866b7a24c8786134 @@ -580,7 +580,7 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219 F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e -F ext/wasm/api/sqlite3-api-glue.js 26aedfb27915f4f316f6eac84078443e4f0d2dfe5f012310014923ed4b77b2b6 +F ext/wasm/api/sqlite3-api-glue.js 119b91c8a7ce6648679eb66fcdd1ed07ef7fd892eb501d658fbfefcc962012d9 F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8 F ext/wasm/api/sqlite3-api-prologue.js 9aeba7b45cf41b3a26d34d7fb2525633cd1adfc544888c1ea8dbb077496f4ce9 F ext/wasm/api/sqlite3-api-worker1.js f941382f21006b4a817754184e2661b0a63ce650201f3419cd60f4758b6fd60e @@ -634,7 +634,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2 -F ext/wasm/tester1.c-pp.js fb20d9e1c308ea34a29d8afdda1a6c5edc406241b5560fa23c19c14747ee41bc +F ext/wasm/tester1.c-pp.js d628826e936bd143d64e0fa3089752abeeeea38a34a7e2b18d364f090d4e99c6 F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2137,8 +2137,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 55c4b1dc402b358d53d65fa1f6ec063e9e38e95c81a05d98dae3cb58c52ef55c -R c2c51077b7eb5ed8cd8afa840132d8db +P ac920b5386db525c794f4cae332dedcc709cac356f6fb85f3e92bc0a34602511 +R 7ff5dd75150e8cac0c211f64ca609fc2 U stephan -Z 977f0702c9741cb2cb4a8926563a23c6 +Z 90696a3c18b398d4eb36aae7078d8581 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fe7e4a2469..4cd797eb12 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac920b5386db525c794f4cae332dedcc709cac356f6fb85f3e92bc0a34602511 \ No newline at end of file +7cb6d3cd3926882240c91a4b90fdf237b931653295d8d94b05ed885dcae59608 \ No newline at end of file From 95c11b1158b43a3e0661e47334d0e371b70e879d Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 27 Oct 2023 23:02:01 +0000 Subject: [PATCH 085/347] Add missing docs for the JS Worker1 export method, as pointed out in [forum:75524f7342|forum post 75524f7342]. FossilOrigin-Name: a7b267bd11216ee990cdd855044fbc18c300dff07cf25b317f27c1bbbc340dcc --- ext/wasm/api/sqlite3-api-worker1.js | 33 ++++++++++++++++++++++++++++- manifest | 12 +++++------ manifest.uuid | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 29f7d2be63..7c65dd1d3e 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -62,7 +62,7 @@ ``` { - type: string, // one of: 'open', 'close', 'exec', 'config-get' + type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get' messageId: OPTIONAL arbitrary value. The worker will copy it as-is into response messages to assist in client-side dispatching. @@ -325,6 +325,37 @@ passed only a string), noting that options.resultRows and options.columnNames may be populated by the call to db.exec(). + + ==================================================================== + "export" the current db + + To export the underlying database as a byte array... + + Message format: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + } + ``` + + Response: + + ``` + { + type: "export", + messageId: ...as above..., + dbId: ...as above... + result: { + byteArray: Uint8Array (as per sqlite3_js_db_export()), + filename: the db filename, + mimetype: "application/x-sqlite3" + } + } + ``` + */ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.initWorker1API = function(){ diff --git a/manifest b/manifest index dbfd82067a..9a4dde3ead 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Export\ssqlite3_get_autocommit()\sto\sWASM\sand\sadd\stests\sfor\sit. -D 2023-10-27T21:05:50.750 +C Add\smissing\sdocs\sfor\sthe\sJS\sWorker1\sexport\smethod,\sas\spointed\sout\sin\s[forum:75524f7342|forum\spost\s75524f7342]. +D 2023-10-27T23:02:01.291 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -583,7 +583,7 @@ F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b18817 F ext/wasm/api/sqlite3-api-glue.js 119b91c8a7ce6648679eb66fcdd1ed07ef7fd892eb501d658fbfefcc962012d9 F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8 F ext/wasm/api/sqlite3-api-prologue.js 9aeba7b45cf41b3a26d34d7fb2525633cd1adfc544888c1ea8dbb077496f4ce9 -F ext/wasm/api/sqlite3-api-worker1.js f941382f21006b4a817754184e2661b0a63ce650201f3419cd60f4758b6fd60e +F ext/wasm/api/sqlite3-api-worker1.js 88770ac01fc756f89a3e060eec17111d6c1688e89ebfd34cb9d9e54d25affbb9 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 @@ -2137,8 +2137,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 ac920b5386db525c794f4cae332dedcc709cac356f6fb85f3e92bc0a34602511 -R 7ff5dd75150e8cac0c211f64ca609fc2 +P 7cb6d3cd3926882240c91a4b90fdf237b931653295d8d94b05ed885dcae59608 +R 53009930e6bc65b4da12fb26b9e7e080 U stephan -Z 90696a3c18b398d4eb36aae7078d8581 +Z 6e447e009637f07735108793303337a5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4cd797eb12..cddb3f9f7d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7cb6d3cd3926882240c91a4b90fdf237b931653295d8d94b05ed885dcae59608 \ No newline at end of file +a7b267bd11216ee990cdd855044fbc18c300dff07cf25b317f27c1bbbc340dcc \ No newline at end of file From 54e7a0f360f73799f4b4d91c8a72dd08c52668b5 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 1 Nov 2023 14:33:44 +0000 Subject: [PATCH 086/347] Fix testrunner.tcl so that it accepts a relative path as an argument to the --zipvfs switch. FossilOrigin-Name: c1ed7ed02269e5b26142eb307e7889fc636672f101ea7fc192a2445e16a7dd6f --- manifest | 17 +++++++---------- manifest.uuid | 2 +- test/testrunner.tcl | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 603321be0f..ca82d7d002 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Version\s3.44.0 -D 2023-11-01T11:23:50.173 +C Fix\stestrunner.tcl\sso\sthat\sit\saccepts\sa\srelative\spath\sas\san\sargument\sto\sthe\s--zipvfs\sswitch. +D 2023-11-01T14:33:44.381 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1647,7 +1647,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 14c8b8ece841b1dd17516a0dc9c7ad9b5f4d4db7987974d3fdf66ae56b2a71fa +F test/testrunner.tcl 8a6721213bce1cfd3b33e1588cc6431143d96b98819206bf91f5a205fbb150d4 F test/testrunner_data.tcl 7f73f93634d32dafc857ed491b840f371113d09fde6a8bfb9e47b938d47b8c85 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2139,11 +2139,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 d1895dd8f5757a339f619f22b29c8a739398ded673bb9c93f1b8eb8a4b38f510 -R 06db090922d30265c1e1381238ea6702 -T +sym-major-release * -T +sym-release * -T +sym-version-3.44.0 * -U drh -Z 14f36413d8197ea0c35026cb3842c9f7 +P 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301 +R 95342b1e9b6483a07de6e1272f8f583b +U dan +Z ad9ef3268962ff3bfef4d4d0ad2ec6f3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1cefea455e..a8db7ce6b3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301 \ No newline at end of file +c1ed7ed02269e5b26142eb307e7889fc636672f101ea7fc192a2445e16a7dd6f \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index 1f1862ffdd..025f2ecdb4 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -425,7 +425,7 @@ for {set ii 0} {$ii < [llength $argv]} {incr ii} { if {$isLast} { usage } } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} { incr ii - set TRG(zipvfs) [lindex $argv $ii] + set TRG(zipvfs) [file normalize [lindex $argv $ii]] if {$isLast} { usage } } else { usage From 0577cdec5fc2b654096da49a733c68a594a3a0eb Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 1 Nov 2023 19:35:15 +0000 Subject: [PATCH 087/347] Update the windows build documentation to provide the exact command used to build the DLLs that appear on the download page. FossilOrigin-Name: 3524a8914c62f7987d948e5ef50cfdcb07254489e80e2257f38d0050c4baf450 --- doc/compile-for-windows.md | 14 +++++++++++++- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/doc/compile-for-windows.md b/doc/compile-for-windows.md index 20f79afa93..b8a50afb32 100644 --- a/doc/compile-for-windows.md +++ b/doc/compile-for-windows.md @@ -1,7 +1,7 @@ # Notes On Compiling SQLite On Windows 11 Here are step-by-step instructions on how to build SQLite from -canonical source on a new Windows 11 PC, as of 2023-08-16: +canonical source on a new Windows 11 PC, as of 2023-11-01: 1. Install Microsoft Visual Studio. The free "community edition" will work fine. Do a standard install for C++ development. @@ -84,6 +84,18 @@ following minor changes:

  • `set PATH=c:\tcl32\bin;%PATH%` +## Building a DLL + +The command the developers use for building the deliverable DLL on the +[download page](https://sqlite.org/download.html) is as follows: + +> ~~~~ +nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1" +~~~~ + +That command generates both the sqlite3.dll and sqlite3.def files. The same +command works for both 32-bit and 64-bit builds. + ## Statically Linking The TCL Library Some utility programs associated with SQLite need to be linked diff --git a/manifest b/manifest index ca82d7d002..e07f83e557 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\stestrunner.tcl\sso\sthat\sit\saccepts\sa\srelative\spath\sas\san\sargument\sto\sthe\s--zipvfs\sswitch. -D 2023-11-01T14:33:44.381 +C Update\sthe\swindows\sbuild\sdocumentation\sto\sprovide\sthe\sexact\scommand\sused\nto\sbuild\sthe\sDLLs\sthat\sappear\son\sthe\sdownload\spage. +D 2023-11-01T19:35:15.525 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -37,7 +37,7 @@ F configure 1d9cbcb416cb5387b3f750ae6db63cfedb703a2e46bf8ca61daecff31d208252 x F configure.ac de31fea7d975bb7ebafbe0e2190a855cc80d48558bf0c9a6578a1836daf1cd3a F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/compile-for-windows.md 922ba580d210a1f1bd3ef9d0413121556f9b5714fb5c01e664d980f85fa4ac8c +F doc/compile-for-windows.md 50b27d77be96195c66031a3181cb8684ed822327ea834e07f9c014213e5e3bcf F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/lemon.html 44a53a1d2b42d7751f7b2f478efb23c978e258d794bfd172442307a755b9fa44 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 @@ -2139,8 +2139,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 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301 -R 95342b1e9b6483a07de6e1272f8f583b -U dan -Z ad9ef3268962ff3bfef4d4d0ad2ec6f3 +P c1ed7ed02269e5b26142eb307e7889fc636672f101ea7fc192a2445e16a7dd6f +R 9f52bda73840b08593c0a2719141f789 +U drh +Z 0993941d7491a4e53ffe83c0d4aa911d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a8db7ce6b3..3178ed16a3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c1ed7ed02269e5b26142eb307e7889fc636672f101ea7fc192a2445e16a7dd6f \ No newline at end of file +3524a8914c62f7987d948e5ef50cfdcb07254489e80e2257f38d0050c4baf450 \ No newline at end of file From 7d522628896b4bcf6d077bd9088b0d19d4e55bda Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 1 Nov 2023 20:09:02 +0000 Subject: [PATCH 088/347] Remove some outdated JNI docs. FossilOrigin-Name: 52aef3d015bad12c4bd9334ec795276a68a0dabdc6fd0ff2a1068f640171eb77 --- ext/jni/src/org/sqlite/jni/capi/CApi.java | 9 --------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index d155060999..6eeeb29a24 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -32,15 +32,6 @@ import java.util.Arrays;

    The C-side part can be found in sqlite3-jni.c. -

    This class is package-private in order to keep Java clients from - having direct access to the low-level C-style APIs, a design - decision made by Java developers based on the C-style API being - riddled with opportunities for Java developers to proverbially shoot - themselves in the foot with. Third-party copies of this code may - eliminate that guard by simply changing this class from - package-private to public. Its methods which are intended to be - exposed that way are all public. -

    Only functions which materially differ from their C counterparts are documented here, and only those material differences are documented. The C documentation is otherwise applicable for these diff --git a/manifest b/manifest index 2040c8baa6..1ad5acd831 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\sinto\sjni-post-3.44\sbranch. -D 2023-10-28T03:56:14.902 +C Remove\ssome\soutdated\sJNI\sdocs. +D 2023-11-01T20:09:02.325 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -249,7 +249,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 23d4073b34b5c07dd412d18395c9db79779bab059164316e14a60b33ec473cff +F ext/jni/src/org/sqlite/jni/capi/CApi.java 24aba7b14b11da52cd47083608d37c186122c2e2072e75b2ff923d1f15bfb9e5 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 @@ -2140,8 +2140,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 a7b267bd11216ee990cdd855044fbc18c300dff07cf25b317f27c1bbbc340dcc f6e1137919243c5ce86725df64b40b7e12e82cbceaff210ca41616d620f0dd1b -R 624bf302e62c1cc32ad38a47599efaea +P 9670eb2496b4005cf718d9fd12dbd11733f7cf6704a967ebb316504ea6a51e82 +R 97a2b4ce6a38c0f0d2c6b5ea82f2a285 U stephan -Z e297eea85124209717df39b3b4767cd9 +Z 679d5351cf0eeba3692c3f5d2e6b7df2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4d5592d9bf..56e74409bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9670eb2496b4005cf718d9fd12dbd11733f7cf6704a967ebb316504ea6a51e82 \ No newline at end of file +52aef3d015bad12c4bd9334ec795276a68a0dabdc6fd0ff2a1068f640171eb77 \ No newline at end of file From 57a7fdd81487846e1e73074bdcc152a6c7dc85b0 Mon Sep 17 00:00:00 2001 From: larrybr Date: Thu, 2 Nov 2023 11:04:37 +0000 Subject: [PATCH 089/347] CLI to compile with older MSVC compiler. FossilOrigin-Name: c89f75f8eef449f6869bda423593f7e14080e805f8ed192aff9463460f11b1a9 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 5 ++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index e07f83e557..2076e838b1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\swindows\sbuild\sdocumentation\sto\sprovide\sthe\sexact\scommand\sused\nto\sbuild\sthe\sDLLs\sthat\sappear\son\sthe\sdownload\spage. -D 2023-11-01T19:35:15.525 +C CLI\sto\scompile\swith\solder\sMSVC\scompiler. +D 2023-11-02T11:04:37.702 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -722,7 +722,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 31229276a8eb5b5de1428cd2d80f6f1cf8ffc5248be25e47cf575df12f1b8f23 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in 3e9371ca6a93294931a8ed8b098bc3cb15d57567aa9a1f2ade72db7f5c572795 +F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2139,8 +2139,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 c1ed7ed02269e5b26142eb307e7889fc636672f101ea7fc192a2445e16a7dd6f -R 9f52bda73840b08593c0a2719141f789 -U drh -Z 0993941d7491a4e53ffe83c0d4aa911d +P 3524a8914c62f7987d948e5ef50cfdcb07254489e80e2257f38d0050c4baf450 +R 1426ab6e5bb9ef707c7806f924ffe51a +U larrybr +Z 3840fc65c1d88cda0ad731f26c553ff7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3178ed16a3..3c06af71c9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3524a8914c62f7987d948e5ef50cfdcb07254489e80e2257f38d0050c4baf450 \ No newline at end of file +c89f75f8eef449f6869bda423593f7e14080e805f8ed192aff9463460f11b1a9 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index ad16d72044..3cfe1a94a4 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -704,9 +704,8 @@ static void console_prepare_utf8(void){ conState.outCodePage = GetConsoleOutputCP(); if( in_console ){ SetConsoleCP(CP_UTF8); - DWORD newConsoleMode = conState.consoleMode - | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - SetConsoleMode(conState.hConsole, newConsoleMode); + SetConsoleMode(conState.hConsole, conState.consoleMode + | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT); console_utf8_in = 1; } From 8a93ce7458f85a3ab7849f588853852e38c99abd Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 2 Nov 2023 12:05:55 +0000 Subject: [PATCH 090/347] Update the srctree-check.tcl script and child script so that they can be run on a read-only source tree and so that if any inconsistencies are found, the script returns a non-zero exit code and thus halts the build. FossilOrigin-Name: 58eb5440d75fb13c1a089cb935de6fb94b4322e10e349db8f11f37b9a2fda2fc --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- tool/mkctimec.tcl | 18 +++++++++++++++--- tool/mkpragmatab.tcl | 16 ++++++++++++++-- tool/srctree-check.tcl | 38 +++++++++++++++++++++++++------------- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/manifest b/manifest index 2076e838b1..bcc42ebe99 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI\sto\scompile\swith\solder\sMSVC\scompiler. -D 2023-11-02T11:04:37.702 +C Update\sthe\ssrctree-check.tcl\sscript\sand\schild\sscript\sso\sthat\sthey\scan\sbe\srun\non\sa\sread-only\ssource\stree\sand\sso\sthat\sif\sany\sinconsistencies\sare\sfound,\sthe\nscript\sreturns\sa\snon-zero\sexit\scode\sand\sthus\shalts\sthe\sbuild. +D 2023-11-02T12:05:55.199 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -2059,13 +2059,13 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439 F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176 F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x -F tool/mkctimec.tcl 372452e24267dfe1b496eec3992d10c6e5e7d7870a152560cdcfe5404bc8cc04 x +F tool/mkctimec.tcl a16682eae5f01f85e5861b2aa215ca0d46b4230658ee25977e02b4508566fb75 x F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd F tool/mkmsvcmin.tcl 8897d515ef7f94772322db95a3b6fce6c614d84fe0bdd06ba5a1c786351d5a1d F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef F tool/mkopcodeh.tcl 769d9e6a8b462323150dc13a8539d6064664b72974f7894befe2491cc73e05cd F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa -F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d +F tool/mkpragmatab.tcl 32e359ccb21011958a821955254bd7a5fa7915d01a8c16fed91ffc8b40cb4adf F tool/mkshellc.tcl b7adf08b82de60811d2cb6af05ff59fc17e5cd6f3e98743c14eaaa3f8971fed0 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 @@ -2106,7 +2106,7 @@ F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaa F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848 F tool/src-verify.c 41c586dee84d0b190ad13e0282ed83d4a65ec9fefde9adf4943efdf6558eea7f F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f -F tool/srctree-check.tcl cef630bc4ff21a460d72479c43a42bf1c1ed61897659305c35c8d72e91bcb176 +F tool/srctree-check.tcl 32712f58821aa224ff3f6401ff985f5f24fa8b9a75250443ed70c5a51a13d84c F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d @@ -2139,8 +2139,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 3524a8914c62f7987d948e5ef50cfdcb07254489e80e2257f38d0050c4baf450 -R 1426ab6e5bb9ef707c7806f924ffe51a -U larrybr -Z 3840fc65c1d88cda0ad731f26c553ff7 +P c89f75f8eef449f6869bda423593f7e14080e805f8ed192aff9463460f11b1a9 +R 6b4fbbc7f44186f715baaef1e0827b96 +U drh +Z 1f35d6921bfbf38634fcb89e7db9b288 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3c06af71c9..0ece8ea040 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c89f75f8eef449f6869bda423593f7e14080e805f8ed192aff9463460f11b1a9 \ No newline at end of file +58eb5440d75fb13c1a089cb935de6fb94b4322e10e349db8f11f37b9a2fda2fc \ No newline at end of file diff --git a/tool/mkctimec.tcl b/tool/mkctimec.tcl index 098bf16e3b..9e425c0fdc 100755 --- a/tool/mkctimec.tcl +++ b/tool/mkctimec.tcl @@ -7,6 +7,13 @@ # definition used in src/ctime.c, run this script from # the checkout root. It generates src/ctime.c . # +# Results are normally written into src/ctime.c. But if an argument is +# provided, results are written there instead. Examples: +# +# tclsh tool/mkctimec.tcl ;# <-- results to src/ctime.c +# +# tclsh tool/mkctimec.tcl /dev/tty ;# <-- results to the terminal +# set ::headWarning {/* DO NOT EDIT! @@ -429,10 +436,15 @@ foreach v $value2_options { }] } -set ctime_c "src/ctime.c" +if {$argc>0} { + set destfile [lindex $argv 0] +} else { + set destfile "[file dir [file dir [file normal $argv0]]]/src/ctime.c" + puts "Overwriting $destfile..." +} -if {[catch {set cfd [open $ctime_c w]}]!=0} { - puts stderr "File '$ctime_c' unwritable." +if {[catch {set cfd [open $destfile w]}]!=0} { + puts stderr "File '$destfile' unwritable." exit 1; } diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index 5c36df0497..2b5b6fdcdb 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -9,6 +9,14 @@ # Then add the extra "case PragTyp_XXXXX:" and subsequent code for the # new pragma in ../src/pragma.c. # +# The results are normally written into the ../src/pragma.h file. However, +# if an alternative output file name is provided as an argument, then +# results are written into the alternative. For example: +# +# tclsh tool/mkpragmatab.tcl ;# <--- Results to src/pragma.h +# +# tclsh tool/mkpragmatab.tcl /dev/tty ;# <-- results to terminal +# # Flag meanings: set flagMeaning(NeedSchema) {Force schema load before running} @@ -402,8 +410,12 @@ set pragma_def { # Open the output file # -set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h" -puts "Overwriting $destfile with new pragma table..." +if {$argc>0} { + set destfile [lindex $argv 0] +} else { + set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h" + puts "Overwriting $destfile with new pragma table..." +} set fd [open $destfile wb] puts $fd {/* DO NOT EDIT! ** This file is automatically generated by the script at diff --git a/tool/srctree-check.tcl b/tool/srctree-check.tcl index 234e70fae9..5327e63930 100644 --- a/tool/srctree-check.tcl +++ b/tool/srctree-check.tcl @@ -33,6 +33,10 @@ cd $ROOT # set TCLSH [info nameofexe] +# Number of errors seen. +# +set NERR 0 + ######################### autoconf/tea/configure.ac ########################### set confac [readfile $ROOT/autoconf/tea/configure.ac] @@ -42,38 +46,46 @@ append pattern [string trim $vers] append pattern {])} if {[string first $pattern $confac]<=0} { puts "ERROR: ./autoconf/tea/configure.ac does not agree with ./VERSION" - exit 1 + puts "...... Fix: manually edit ./autoconf/tea/configure.ac to" + incr NERR } ######################### autoconf/Makefile.msc ############################### set f1 [readfile $ROOT/autoconf/Makefile.msc] -exec mv $ROOT/autoconf/Makefile.msc $ROOT/autoconf/Makefile.msc.tmp -exec $TCLSH $ROOT/tool/mkmsvcmin.tcl -set f2 [readfile $ROOT/autoconf/Makefile.msc] -exec mv $ROOT/autoconf/Makefile.msc.tmp $ROOT/autoconf/Makefile.msc +exec $TCLSH $ROOT/tool/mkmsvcmin.tcl $ROOT/Makefile.msc tmp1.txt +set f2 [readfile tmp1.txt] +file delete tmp1.txt if {$f1 != $f2} { puts "ERROR: ./autoconf/Makefile.msc does not agree with ./Makefile.msc" + puts "...... Fix: tclsh tool/mkmsvcmin.tcl" + incr NERR } ######################### src/pragma.h ######################################## set f1 [readfile $ROOT/src/pragma.h] -exec mv $ROOT/src/pragma.h $ROOT/src/pragma.h.tmp -exec $TCLSH $ROOT/tool/mkpragmatab.tcl -set f2 [readfile $ROOT/src/pragma.h] -exec mv $ROOT/src/pragma.h.tmp $ROOT/src/pragma.h +exec $TCLSH $ROOT/tool/mkpragmatab.tcl tmp2.txt +set f2 [readfile tmp2.txt] +file delete tmp2.txt if {$f1 != $f2} { puts "ERROR: ./src/pragma.h does not agree with ./tool/mkpragmatab.tcl" + puts "...... Fix: tclsh tool/mkpragmatab.tcl" + incr NERR } ######################### src/ctime.c ######################################## set f1 [readfile $ROOT/src/ctime.c] -exec mv $ROOT/src/ctime.c $ROOT/src/ctime.c.tmp -exec $TCLSH $ROOT/tool/mkctimec.tcl -set f2 [readfile $ROOT/src/ctime.c] -exec mv $ROOT/src/ctime.c.tmp $ROOT/src/ctime.c +exec $TCLSH $ROOT/tool/mkctimec.tcl tmp3.txt +set f2 [readfile tmp3.txt] +file delete tmp3.txt if {$f1 != $f2} { puts "ERROR: ./src/ctime.c does not agree with ./tool/mkctimec.tcl" + puts "..... Fix: tclsh tool/mkctimec.tcl" + incr NERR } + +# If any errors are seen, exit 1 so that the build will fail. +# +if {$NERR>0} {exit 1} From 845a2e0cc246a80c99509e7319f2d506ce0528fa Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 2 Nov 2023 13:10:16 +0000 Subject: [PATCH 091/347] One more tweak to tool/srctree-check.tcl so that a complete build can be accomplished from a read-only source tree. FossilOrigin-Name: cba9f0601ca995ac6952aa3a83f7264f6d25aaaa2ea36b19e90cbf591077de72 --- manifest | 12 ++++++------ manifest.uuid | 2 +- tool/srctree-check.tcl | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index bcc42ebe99..df7a5eb535 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\ssrctree-check.tcl\sscript\sand\schild\sscript\sso\sthat\sthey\scan\sbe\srun\non\sa\sread-only\ssource\stree\sand\sso\sthat\sif\sany\sinconsistencies\sare\sfound,\sthe\nscript\sreturns\sa\snon-zero\sexit\scode\sand\sthus\shalts\sthe\sbuild. -D 2023-11-02T12:05:55.199 +C One\smore\stweak\sto\stool/srctree-check.tcl\sso\sthat\sa\scomplete\sbuild\scan\sbe\naccomplished\sfrom\sa\sread-only\ssource\stree. +D 2023-11-02T13:10:16.285 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -2106,7 +2106,7 @@ F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaa F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848 F tool/src-verify.c 41c586dee84d0b190ad13e0282ed83d4a65ec9fefde9adf4943efdf6558eea7f F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f -F tool/srctree-check.tcl 32712f58821aa224ff3f6401ff985f5f24fa8b9a75250443ed70c5a51a13d84c +F tool/srctree-check.tcl 9f1098d439159692ece45cc9e701b35df4956269399fe0c770e41f235d1ce5e6 F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d @@ -2139,8 +2139,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 c89f75f8eef449f6869bda423593f7e14080e805f8ed192aff9463460f11b1a9 -R 6b4fbbc7f44186f715baaef1e0827b96 +P 58eb5440d75fb13c1a089cb935de6fb94b4322e10e349db8f11f37b9a2fda2fc +R 18054cea9bc23d801f864db646273e7d U drh -Z 1f35d6921bfbf38634fcb89e7db9b288 +Z 72328c40506425c913ea7fab75e23b19 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0ece8ea040..7142949e45 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58eb5440d75fb13c1a089cb935de6fb94b4322e10e349db8f11f37b9a2fda2fc \ No newline at end of file +cba9f0601ca995ac6952aa3a83f7264f6d25aaaa2ea36b19e90cbf591077de72 \ No newline at end of file diff --git a/tool/srctree-check.tcl b/tool/srctree-check.tcl index 5327e63930..f44e3ceec5 100644 --- a/tool/srctree-check.tcl +++ b/tool/srctree-check.tcl @@ -27,7 +27,6 @@ proc readfile {filename} { # Find the root of the tree. # set ROOT [file dir [file dir [file normalize $argv0]]] -cd $ROOT # Name of the TCL interpreter # From e186fe20f5bddfd9f4d209f354dd6f5efa8e9c85 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 2 Nov 2023 17:31:06 +0000 Subject: [PATCH 092/347] Add the "remove_diacritics" option to the fts5 trigram tokenizer. FossilOrigin-Name: 83da80135b6105f47d1de560232449562ae8ac176c8011a6f75589f62bc9b1db --- ext/fts5/fts5_tokenize.c | 96 ++++++++++++++++++++-------- ext/fts5/test/fts5trigram2.test | 109 ++++++++++++++++++++++++++++++++ manifest | 15 +++-- manifest.uuid | 2 +- 4 files changed, 187 insertions(+), 35 deletions(-) create mode 100644 ext/fts5/test/fts5trigram2.test diff --git a/ext/fts5/fts5_tokenize.c b/ext/fts5/fts5_tokenize.c index e61d6b1edd..098d0c86b7 100644 --- a/ext/fts5/fts5_tokenize.c +++ b/ext/fts5/fts5_tokenize.c @@ -227,6 +227,12 @@ static const unsigned char sqlite3Utf8Trans1[] = { } \ } +#define SKIP_UTF8(zIn) { \ + if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \ + while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ + } \ +} + #endif /* ifndef SQLITE_AMALGAMATION */ typedef struct Unicode61Tokenizer Unicode61Tokenizer; @@ -1264,6 +1270,7 @@ static int fts5PorterTokenize( typedef struct TrigramTokenizer TrigramTokenizer; struct TrigramTokenizer { int bFold; /* True to fold to lower-case */ + int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ }; /* @@ -1298,10 +1305,21 @@ static int fts5TriCreate( }else{ pNew->bFold = (zArg[0]=='0'); } + }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ + if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ + rc = SQLITE_ERROR; + }else{ + pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; + } }else{ rc = SQLITE_ERROR; } } + + if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ + rc = SQLITE_ERROR; + } + if( rc!=SQLITE_OK ){ fts5TriDelete((Fts5Tokenizer*)pNew); pNew = 0; @@ -1324,40 +1342,62 @@ static int fts5TriTokenize( TrigramTokenizer *p = (TrigramTokenizer*)pTok; int rc = SQLITE_OK; char aBuf[32]; + char *zOut = aBuf; + int ii; const unsigned char *zIn = (const unsigned char*)pText; const unsigned char *zEof = &zIn[nText]; u32 iCode; + int aStart[3]; /* Input offset of each character in aBuf[] */ UNUSED_PARAM(unusedFlags); - while( 1 ){ - char *zOut = aBuf; - int iStart = zIn - (const unsigned char*)pText; - const unsigned char *zNext; - READ_UTF8(zIn, zEof, iCode); - if( iCode==0 ) break; - zNext = zIn; - if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); + /* Populate aBuf[] with the characters for the first trigram. */ + for(ii=0; ii<3; ii++){ + do { + aStart[ii] = zIn - (const unsigned char*)pText; + READ_UTF8(zIn, zEof, iCode); + if( iCode==0 ) return SQLITE_OK; + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); + }while( iCode==0 ); + WRITE_UTF8(zOut, iCode); + } + + /* At the start of each iteration of this loop: + ** + ** aBuf: Contains 3 characters. The 3 characters of the next trigram. + ** zOut: Points to the byte following the last character in aBuf. + ** aStart[3]: Contains the byte offset in the input text corresponding + ** to the start of each of the three characters in the buffer. + */ + assert( zIn<=zEof ); + while( 1 ){ + int iNext; /* Start of character following current tri */ + const char *z1; + + /* Read characters from the input up until the first non-diacritic */ + do { + iNext = zIn - (const unsigned char*)pText; READ_UTF8(zIn, zEof, iCode); if( iCode==0 ) break; - }else{ - break; - } - if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); - READ_UTF8(zIn, zEof, iCode); - if( iCode==0 ) break; - if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); - }else{ - break; - } - rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf); - if( rc!=SQLITE_OK ) break; - zIn = zNext; + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); + }while( iCode==0 ); + + /* Pass the current trigram back to fts5 */ + rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); + if( iCode==0 || rc!=SQLITE_OK ) break; + + /* Remove the first character from buffer aBuf[]. Append the character + ** with codepoint iCode. */ + z1 = aBuf; + SKIP_UTF8(z1); + memmove(aBuf, z1, zOut - z1); + zOut -= (z1 - aBuf); + WRITE_UTF8(zOut, iCode); + + /* Update the aStart[] array */ + aStart[0] = aStart[1]; + aStart[1] = aStart[2]; + aStart[2] = iNext; } return rc; @@ -1380,7 +1420,9 @@ int sqlite3Fts5TokenizerPattern( ){ if( xCreate==fts5TriCreate ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; - return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; + if( p->iFoldParam==0 ){ + return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; + } } return FTS5_PATTERN_NONE; } diff --git a/ext/fts5/test/fts5trigram2.test b/ext/fts5/test/fts5trigram2.test new file mode 100644 index 0000000000..f5beae5b28 --- /dev/null +++ b/ext/fts5/test/fts5trigram2.test @@ -0,0 +1,109 @@ +# 2023 October 24 +# +# 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. +# +#************************************************************************* +# +# Tests for the fts5 "trigram" tokenizer. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +ifcapable !fts5 { finish_test ; return } +set ::testprefix fts5trigram2 + +do_execsql_test 1.0 " + CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize='trigram remove_diacritics 1'); + INSERT INTO t1 VALUES('abc\u0303defghijklm'); + INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm'); +" + +do_execsql_test 1.1 { + SELECT highlight(t1, 0, '(', ')') FROM t1('abc'); +} [list \ + "(abc\u0303)defghijklm" \ + "(a\u0303b\u0303c\u0303)defghijklm" \ +] + +do_execsql_test 1.2 { + SELECT highlight(t1, 0, '(', ')') FROM t1('bcde'); +} [list \ + "a(bc\u0303de)fghijklm" \ + "a\u0303(b\u0303c\u0303de)fghijklm" \ +] + +do_execsql_test 1.3 { + SELECT highlight(t1, 0, '(', ')') FROM t1('cdef'); +} [list \ + "ab(c\u0303def)ghijklm" \ + "a\u0303b\u0303(c\u0303def)ghijklm" \ +] + +do_execsql_test 1.4 { + SELECT highlight(t1, 0, '(', ')') FROM t1('def'); +} [list \ + "abc\u0303(def)ghijklm" \ + "a\u0303b\u0303c\u0303(def)ghijklm" \ +] + + +#------------------------------------------------------------------------- +do_catchsql_test 2.0 { + CREATE VIRTUAL TABLE t2 USING fts5( + z, tokenize='trigram case_sensitive 1 remove_diacritics 1' + ); +} {1 {error in tokenizer constructor}} + +do_execsql_test 2.1 { + CREATE VIRTUAL TABLE t2 USING fts5( + z, tokenize='trigram case_sensitive 0 remove_diacritics 1' + ); +} +do_execsql_test 2.2 " + INSERT INTO t2 VALUES('\u00E3bcdef'); + INSERT INTO t2 VALUES('b\u00E3cdef'); + INSERT INTO t2 VALUES('bc\u00E3def'); + INSERT INTO t2 VALUES('bcd\u00E3ef'); +" + +do_execsql_test 2.3 { + SELECT highlight(t2, 0, '(', ')') FROM t2('abc'); +} "(\u00E3bc)def" +do_execsql_test 2.4 { + SELECT highlight(t2, 0, '(', ')') FROM t2('bac'); +} "(b\u00E3c)def" +do_execsql_test 2.5 { + SELECT highlight(t2, 0, '(', ')') FROM t2('bca'); +} "(bc\u00E3)def" +do_execsql_test 2.6 " + SELECT highlight(t2, 0, '(', ')') FROM t2('\u00E3bc'); +" "(\u00E3bc)def" + +#------------------------------------------------------------------------- +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE t3 USING fts5( + z, tokenize='trigram remove_diacritics 1' + ); +} {} +do_execsql_test 3.1 " + INSERT INTO t3 VALUES ('\u0303abc\u0303'); +" +do_execsql_test 3.2 { + SELECT highlight(t3, 0, '(', ')') FROM t3('abc'); +} "\u0303(abc\u0303)" + +#------------------------------------------------------------------------- +do_execsql_test 4.0 { + CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram); +} {} + +breakpoint +do_execsql_test 4.1 { + INSERT INTO t4 VALUES('ABCD'); +} {} + +finish_test diff --git a/manifest b/manifest index df7a5eb535..ad9b0ea28f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C One\smore\stweak\sto\stool/srctree-check.tcl\sso\sthat\sa\scomplete\sbuild\scan\sbe\naccomplished\sfrom\sa\sread-only\ssource\stree. -D 2023-11-02T13:10:16.285 +C Add\sthe\s"remove_diacritics"\soption\sto\sthe\sfts5\strigram\stokenizer. +D 2023-11-02T17:31:06.153 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -100,7 +100,7 @@ F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca5 F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b -F ext/fts5/fts5_tokenize.c 5e251efb0f1af99a25ed50010ba6b1ad1250aca5921af1988fdcabe5ebc3cb43 +F ext/fts5/fts5_tokenize.c 5a895f3bf366a87cb8c0812445736248c236e7ea664a10125021662acd6b0ba4 F ext/fts5/fts5_unicode2.c eca63dbc797f8ff0572e97caf4631389c0ab900d6364861b915bdd4735973f00 F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80 F ext/fts5/fts5_vocab.c aed56169ae5c1aa9b8189c779ffeef04ed516d3c712c06914e6d91a6759f4e4a @@ -217,6 +217,7 @@ F ext/fts5/test/fts5tok1.test 1f7817499f5971450d8c4a652114b3d833393c8134e32422d0 F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2 F ext/fts5/test/fts5tokenizer.test ac3c9112b263a639fb0508ae73a3ee886bf4866d2153771a8e8a20c721305a43 F ext/fts5/test/fts5trigram.test 6c4e37864f3e7d90673db5563d9736d7e40080ab94d10ebdffa94c1b77941da0 +F ext/fts5/test/fts5trigram2.test 9fe4207f8a4241747aff1005258b564958588d21bfd240d6cd4c2e955d31c156 F ext/fts5/test/fts5ubsan.test 783d5a8d13ebfa169e634940228db54540780e3ba7a87ad1e4510e61440bf64b F ext/fts5/test/fts5umlaut.test a42fe2fe6387c40c49ab27ccbd070e1ae38e07f38d05926482cc0bccac9ad602 F ext/fts5/test/fts5unicode.test 17056f4efe6b0a5d4f41fdf7a7dc9af2873004562eaa899d40633b93dc95f5a9 @@ -2139,8 +2140,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 58eb5440d75fb13c1a089cb935de6fb94b4322e10e349db8f11f37b9a2fda2fc -R 18054cea9bc23d801f864db646273e7d -U drh -Z 72328c40506425c913ea7fab75e23b19 +P cba9f0601ca995ac6952aa3a83f7264f6d25aaaa2ea36b19e90cbf591077de72 +R 91c6db43050bc1c48384df29a062813f +U dan +Z e9e46724e46ba97d1a7a1bb71106fcd0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7142949e45..c9ea7d6610 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cba9f0601ca995ac6952aa3a83f7264f6d25aaaa2ea36b19e90cbf591077de72 \ No newline at end of file +83da80135b6105f47d1de560232449562ae8ac176c8011a6f75589f62bc9b1db \ No newline at end of file From 7b0fd0c5644f77a44dc915611aa1213cb4c8c99a Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 2 Nov 2023 18:10:22 +0000 Subject: [PATCH 093/347] Fix a problem with amalgamation builds on this branch. FossilOrigin-Name: 8f046c82c9cf51fc349674577c68d3d2499ee37009deacbf937d711d9930fd49 --- ext/fts5/fts5_tokenize.c | 9 +++++---- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ext/fts5/fts5_tokenize.c b/ext/fts5/fts5_tokenize.c index 098d0c86b7..f12056170f 100644 --- a/ext/fts5/fts5_tokenize.c +++ b/ext/fts5/fts5_tokenize.c @@ -227,14 +227,14 @@ static const unsigned char sqlite3Utf8Trans1[] = { } \ } -#define SKIP_UTF8(zIn) { \ +#endif /* ifndef SQLITE_AMALGAMATION */ + +#define FTS5_SKIP_UTF8(zIn) { \ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ } \ } -#endif /* ifndef SQLITE_AMALGAMATION */ - typedef struct Unicode61Tokenizer Unicode61Tokenizer; struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ @@ -1297,6 +1297,7 @@ static int fts5TriCreate( }else{ int i; pNew->bFold = 1; + pNew->iFoldParam = 0; for(i=0; rc==SQLITE_OK && i Date: Thu, 2 Nov 2023 21:02:53 +0000 Subject: [PATCH 094/347] Fix a spurious "misuse of aggregate function" error that could occur when an aggregate function was used within the FROM clause of a sub-select of the select that owns the aggregate. e.g. "SELECT (SELECT x FROM (SELECT sum(t1.a) AS x)) FROM t1". [forum:/forumpost/c9970a37ed | Forum post c9970a37ed]. FossilOrigin-Name: 4470f657d2069972d02a00983252dec1f814d90c0d8d0906e320e955111e8c11 --- manifest | 19 ++++++++-------- manifest.uuid | 2 +- src/resolve.c | 5 ++++- src/sqliteInt.h | 1 + test/aggnested.test | 54 +++++++++++++++++++++++++++++++++++++++++++++ test/window1.test | 2 +- 6 files changed, 70 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 362a98e526..d29835ad88 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s"remove_diacritics"\soption\sto\sthe\sfts5\strigram\stokenizer. -D 2023-11-02T18:22:35.452 +C Fix\sa\sspurious\s"misuse\sof\saggregate\sfunction"\serror\sthat\scould\soccur\swhen\san\saggregate\sfunction\swas\sused\swithin\sthe\sFROM\sclause\sof\sa\ssub-select\sof\sthe\sselect\sthat\sowns\sthe\saggregate.\se.g.\s"SELECT\s(SELECT\sx\sFROM\s(SELECT\ssum(t1.a)\sAS\sx))\sFROM\st1".\s[forum:/forumpost/c9970a37ed\s|\sForum\spost\sc9970a37ed]. +D 2023-11-02T21:02:53.455 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -721,14 +721,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c bde74add20fc0e8ce0c4e937a1f70a36d17413afe4f71d3e103f5cb74b17c8d9 F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c 31229276a8eb5b5de1428cd2d80f6f1cf8ffc5248be25e47cf575df12f1b8f23 +F src/resolve.c c74f10e6adf35e2b60e63cba7306a07f976cf119fc29a57845a1ddf3c35ddc68 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 567e317f8631883897b7d3da43fce778b7c30dd0dd7f714558c9725fc1c1196c +F src/sqliteInt.h f0185af5bbc434fd047c01e8ddd81c9b093fd304b5abb08547301fca70788b8b F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -818,7 +818,7 @@ F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867d F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggfault.test 777f269d0da5b0c2524c7ff6d99ae9a93db4f1b1839a914dd2a12e3035c29829 -F test/aggnested.test 2e738bfe2980df301a782f6e7bbf9459266f64f7e72f58f3b5c843bf897c568c +F test/aggnested.test 7929208e173f5dbdbe8f67afbc59c07db99199d39ba5ce2d8a16be2c63600f53 F test/aggorderby.test e6b98dbbf3ababa96892435d387de2dcf602ef02c2b848d2d817473066f154ba F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13 @@ -1984,7 +1984,7 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2 F test/win32lock.test e0924eb8daac02bf80e9da88930747bd44dd9b230b7759fed927b1655b467c9c F test/win32longpath.test 4baffc3acb2e5188a5e3a895b2b543ed09e62f7c72d713c1feebf76222fe9976 F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc -F test/window1.test ccfeaf116afc6a8f748a8122a4f1ee6b69e6bbc5acee61197d3c17167338b100 +F test/window1.test 5e8abe56a7d667eeddbba6de180086dcf69ed528d046447a25464f945ece101f F test/window2.tcl 492c125fa550cda1dd3555768a2303b3effbeceee215293adf8871efc25f1476 F test/window2.test e466a88bd626d66edc3d352d7d7e1d5531e0079b549ba44efb029d1fbff9fd3c F test/window3.tcl acea6e86a4324a210fd608d06741010ca83ded9fde438341cb978c49928faf03 @@ -2141,9 +2141,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 e8c97faec339430fa2d1a7e915050d9b363bdba606a4419397cb099141da254d 8f046c82c9cf51fc349674577c68d3d2499ee37009deacbf937d711d9930fd49 -R e431cbffcb397fe64b4dba15715dbe90 -T +closed 8f046c82c9cf51fc349674577c68d3d2499ee37009deacbf937d711d9930fd49 +P 0d50172477064dce3f7c61641d88aa15d8a1edea9872adaffcc1a7a1c9ab0e93 +R b1c591059f07d73b87055d331d542a79 U dan -Z a985144f5adb4bfbc320c60d16272d9c +Z b418469e6326d7ed346db9d2f8a70c19 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f8430c08fd..652d910b8f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0d50172477064dce3f7c61641d88aa15d8a1edea9872adaffcc1a7a1c9ab0e93 \ No newline at end of file +4470f657d2069972d02a00983252dec1f814d90c0d8d0906e320e955111e8c11 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index 0072f6b6aa..a0134fd57c 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1249,11 +1249,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ while( pNC2 && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 ){ - pExpr->op2++; + pExpr->op2 += (1 + pNC2->nNestedSelect); pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ + pExpr->op2 += pNC2->nNestedSelect; assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); @@ -1812,6 +1813,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ /* Recursively resolve names in all subqueries in the FROM clause */ + if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ @@ -1836,6 +1838,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } } + if( pOuterNC ) pOuterNC->nNestedSelect--; /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index dbf01dd139..c8288736b3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3397,6 +3397,7 @@ struct NameContext { int nRef; /* Number of names resolved by this context */ int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ + int nNestedSelect; /* Number of nested selects using this NC */ Select *pWinSelect; /* SELECT statement for any window functions */ }; diff --git a/test/aggnested.test b/test/aggnested.test index 5f033a246c..933df06ce0 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -358,6 +358,60 @@ do_execsql_test 6.2.2 { FROM t2 GROUP BY 'constant_string'; } {{}} +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 7.0 { + CREATE TABLE invoice ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + amount DOUBLE PRECISION DEFAULT NULL, + name VARCHAR(100) DEFAULT NULL + ); + + INSERT INTO invoice (amount, name) VALUES + (4.0, 'Michael'), (15.0, 'Bara'), (4.0, 'Michael'), (6.0, 'John'); +} + +do_execsql_test 7.1 { + SELECT sum(amount), name + from invoice + group by name + having (select v > 6 from (select sum(amount) v) t) +} { + 15.0 Bara + 8.0 Michael +} + +do_execsql_test 7.2 { + SELECT (select 1 from (select sum(amount))) FROM invoice +} {1} + +do_execsql_test 8.0 { + CREATE TABLE t1(x INT); + INSERT INTO t1 VALUES(100); + INSERT INTO t1 VALUES(20); + INSERT INTO t1 VALUES(3); + SELECT (SELECT y FROM (SELECT sum(x) AS y) AS t2 ) FROM t1; +} {123} + +do_execsql_test 8.1 { + SELECT ( + SELECT y FROM ( + SELECT z AS y FROM (SELECT sum(x) AS z) AS t2 + ) + ) FROM t1; +} {123} + +do_execsql_test 8.2 { + SELECT ( + SELECT a FROM ( + SELECT y AS a FROM ( + SELECT z AS y FROM (SELECT sum(x) AS z) AS t2 + ) + ) + ) FROM t1; +} {123} + diff --git a/test/window1.test b/test/window1.test index c9bbae3ee0..d8348a8980 100644 --- a/test/window1.test +++ b/test/window1.test @@ -1881,7 +1881,7 @@ do_catchsql_test 57.3 { SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1))) ) FROM t3; -} {1 {misuse of aggregate: sum()}} +} {0 5} # 2020-06-06 ticket 1f6f353b684fc708 reset_db From 792103a0f2f805d5682ae443d50d6f731a9e3bfc Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 2 Nov 2023 22:11:35 +0000 Subject: [PATCH 095/347] In the previous check-in, use a u32 instead of an int, to make it easier to prove that the integer will never overflow. FossilOrigin-Name: 51002079e0b499e116415189a913a238e95ed9766a8af9d042928f4263861a87 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/resolve.c | 4 +++- src/sqliteInt.h | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index d29835ad88..b820b0b5e0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sspurious\s"misuse\sof\saggregate\sfunction"\serror\sthat\scould\soccur\swhen\san\saggregate\sfunction\swas\sused\swithin\sthe\sFROM\sclause\sof\sa\ssub-select\sof\sthe\sselect\sthat\sowns\sthe\saggregate.\se.g.\s"SELECT\s(SELECT\sx\sFROM\s(SELECT\ssum(t1.a)\sAS\sx))\sFROM\st1".\s[forum:/forumpost/c9970a37ed\s|\sForum\spost\sc9970a37ed]. -D 2023-11-02T21:02:53.455 +C In\sthe\sprevious\scheck-in,\suse\sa\su32\sinstead\sof\san\sint,\sto\smake\sit\seasier\sto\nprove\sthat\sthe\sinteger\swill\snever\soverflow. +D 2023-11-02T22:11:35.443 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -721,14 +721,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c bde74add20fc0e8ce0c4e937a1f70a36d17413afe4f71d3e103f5cb74b17c8d9 F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c c74f10e6adf35e2b60e63cba7306a07f976cf119fc29a57845a1ddf3c35ddc68 +F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h f0185af5bbc434fd047c01e8ddd81c9b093fd304b5abb08547301fca70788b8b +F src/sqliteInt.h 707095a0591e02f4866ed9798cbdd97a8ae8cf4d98f061808944c2cd1c95d1a9 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2141,8 +2141,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 0d50172477064dce3f7c61641d88aa15d8a1edea9872adaffcc1a7a1c9ab0e93 -R b1c591059f07d73b87055d331d542a79 -U dan -Z b418469e6326d7ed346db9d2f8a70c19 +P 4470f657d2069972d02a00983252dec1f814d90c0d8d0906e320e955111e8c11 +R 79c60d9c26c18e4bd3eb97f7d6131505 +U drh +Z b5e2ca2c6ff3e7234971fa445deb54ad # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 652d910b8f..1eec801737 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4470f657d2069972d02a00983252dec1f814d90c0d8d0906e320e955111e8c11 \ No newline at end of file +51002079e0b499e116415189a913a238e95ed9766a8af9d042928f4263861a87 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index a0134fd57c..5f675c1d2d 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1838,7 +1838,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } } - if( pOuterNC ) pOuterNC->nNestedSelect--; + if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ + pOuterNC->nNestedSelect--; + } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c8288736b3..23beb48de3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3397,7 +3397,7 @@ struct NameContext { int nRef; /* Number of names resolved by this context */ int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ - int nNestedSelect; /* Number of nested selects using this NC */ + u32 nNestedSelect; /* Number of nested selects using this NC */ Select *pWinSelect; /* SELECT statement for any window functions */ }; From dcd6d21fe90ba7cf11649211719000894dd86925 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 2 Nov 2023 22:39:11 +0000 Subject: [PATCH 096/347] Mark a branch made always-true by the penultimate check-in with ALWAYS(). FossilOrigin-Name: 268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/select.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index b820b0b5e0..a19fbf6d2d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sthe\sprevious\scheck-in,\suse\sa\su32\sinstead\sof\san\sint,\sto\smake\sit\seasier\sto\nprove\sthat\sthe\sinteger\swill\snever\soverflow. -D 2023-11-02T22:11:35.443 +C Mark\sa\sbranch\smade\salways-true\sby\sthe\spenultimate\scheck-in\swith\sALWAYS(). +D 2023-11-02T22:39:11.206 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -723,7 +723,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 +F src/select.c 8ebbc67478e2d43f0b24c79258597646e526c5001d07801bba7d4059088c8b12 F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2141,8 +2141,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 4470f657d2069972d02a00983252dec1f814d90c0d8d0906e320e955111e8c11 -R 79c60d9c26c18e4bd3eb97f7d6131505 +P 51002079e0b499e116415189a913a238e95ed9766a8af9d042928f4263861a87 +R 9d50cc29331e7855a28167bfb1447137 U drh -Z b5e2ca2c6ff3e7234971fa445deb54ad +Z 2c58aac6deeb8398d0c95f4c1cc7c832 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1eec801737..d1709e346a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -51002079e0b499e116415189a913a238e95ed9766a8af9d042928f4263861a87 \ No newline at end of file +268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f \ No newline at end of file diff --git a/src/select.c b/src/select.c index 7a79385e0b..e97a5afb74 100644 --- a/src/select.c +++ b/src/select.c @@ -8472,7 +8472,7 @@ int sqlite3Select( updateAccumulator(pParse, regAcc, pAggInfo, eDist); if( eDist!=WHERE_DISTINCT_NOOP ){ struct AggInfo_func *pF = pAggInfo->aFunc; - if( pF ){ + if( ALWAYS(pF) ){ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); } } From d706adba48766b93197e5dea7ba64c0ce7776e3d Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 3 Nov 2023 10:15:16 +0000 Subject: [PATCH 097/347] Update the version number to 3.45.0 to begin the next development cycle. FossilOrigin-Name: 15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 --- VERSION | 2 +- autoconf/tea/configure.ac | 2 +- configure | 18 +++++++++--------- configure.ac | 2 +- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/VERSION b/VERSION index faf0dcbb0e..ff3ff28f6b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.44.0 +3.45.0 diff --git a/autoconf/tea/configure.ac b/autoconf/tea/configure.ac index e34bda9f2e..4df57344be 100644 --- a/autoconf/tea/configure.ac +++ b/autoconf/tea/configure.ac @@ -19,7 +19,7 @@ dnl to configure the system for the local environment. # so that we create the export library with the dll. #----------------------------------------------------------------------- -AC_INIT([sqlite],[3.44.0]) +AC_INIT([sqlite],[3.45.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. diff --git a/configure b/configure index 7b31f6f7e8..6a47c4dcc8 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.44.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.45.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -726,8 +726,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.44.0' -PACKAGE_STRING='sqlite 3.44.0' +PACKAGE_VERSION='3.45.0' +PACKAGE_STRING='sqlite 3.45.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1472,7 +1472,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.44.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.45.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1537,7 +1537,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.44.0:";; + short | recursive ) echo "Configuration of sqlite 3.45.0:";; esac cat <<\_ACEOF @@ -1668,7 +1668,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.44.0 +sqlite configure 3.45.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2087,7 +2087,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.44.0, which was +It was created by sqlite $as_me 3.45.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -12481,7 +12481,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.44.0, which was +This file was extended by sqlite $as_me 3.45.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12547,7 +12547,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.44.0 +sqlite config.status 3.45.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 837d2fb015..54f985fd70 100644 --- a/configure.ac +++ b/configure.ac @@ -74,7 +74,7 @@ # you don't need (for example BLT) by erasing or commenting out # the corresponding code. # -AC_INIT([sqlite],[m4_esyscmd(cat VERSION | tr -d '\n')]) +AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n')) dnl Make sure the local VERSION file matches this configure script sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'` diff --git a/manifest b/manifest index a19fbf6d2d..030821495e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Mark\sa\sbranch\smade\salways-true\sby\sthe\spenultimate\scheck-in\swith\sALWAYS(). -D 2023-11-02T22:39:11.206 +C Update\sthe\sversion\snumber\sto\s3.45.0\sto\sbegin\sthe\snext\sdevelopment\scycle. +D 2023-11-03T10:15:16.434 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -7,7 +7,7 @@ F Makefile.in 8b59912fc1538f96a08555605c5886cdcc733696ae7f22e374b2a4752196ca20 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc f0cf219350d9af4fba411b4f6306dce2adc897484e8f446de1fb4f40de674d00 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 -F VERSION 4c09b629c03b8ae32317cb336a32f3aa3252841d6dcd51184cecc4278d08f21e +F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 @@ -22,7 +22,7 @@ F autoconf/configure.ac ec7fa914c5e74ff212fe879f9bb6918e1234497e05facfb641f30c4d F autoconf/tea/Makefile.in 106a96f2f745d41a0f6193f1de98d7355830b65d45032c18cd7c90295ec24196 F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873 F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43 -F autoconf/tea/configure.ac 1ee185f2f8c2e9bed69d0113222724202f962d04eafd57197e65c662d2d9b180 +F autoconf/tea/configure.ac 4c32b08691a5b296206b38422b53b92b65be3d3f6b3dd6552a50981a61f5acda F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523 F autoconf/tea/pkgIndex.tcl.in b9eb6dd37f64e08e637d576b3c83259814b9cddd78bec4af2e5abfc6c5c750ce @@ -33,8 +33,8 @@ F autoconf/tea/win/nmakehlp.c b01f822eabbe1ed2b64e70882d97d48402b42d2689a1ea0034 F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6 F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559 -F configure 1d9cbcb416cb5387b3f750ae6db63cfedb703a2e46bf8ca61daecff31d208252 x -F configure.ac de31fea7d975bb7ebafbe0e2190a855cc80d48558bf0c9a6578a1836daf1cd3a +F configure bcb1042e92775424a1021d2f4c89c78a699a6225df01fa8c593df7df0be6ad10 x +F configure.ac f25bd7843120f2c2b8bc9db5a92b0502bbdd28e68907415c3d42fc8e57c657b9 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd F doc/compile-for-windows.md 50b27d77be96195c66031a3181cb8684ed822327ea834e07f9c014213e5e3bcf @@ -2141,8 +2141,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 51002079e0b499e116415189a913a238e95ed9766a8af9d042928f4263861a87 -R 9d50cc29331e7855a28167bfb1447137 +P 268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f +R dd1ef45ff2a5ccfd075e4867f283d51c U drh -Z 2c58aac6deeb8398d0c95f4c1cc7c832 +Z b80b47952b8aa2091fd6d596beedfe43 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1709e346a..c98a1f3b06 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f \ No newline at end of file +15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 \ No newline at end of file From 1d5f2af063863713a874be9e885006fa9e76b278 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 3 Nov 2023 11:41:24 +0000 Subject: [PATCH 098/347] Update the tool/srctree-check.tcl script so that it verifies that autoconf has been run following edits to VERSION. FossilOrigin-Name: 52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 --- manifest | 12 ++++++------ manifest.uuid | 2 +- tool/srctree-check.tcl | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 030821495e..8d752c5796 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\sversion\snumber\sto\s3.45.0\sto\sbegin\sthe\snext\sdevelopment\scycle. -D 2023-11-03T10:15:16.434 +C Update\sthe\stool/srctree-check.tcl\sscript\sso\sthat\sit\sverifies\sthat\sautoconf\nhas\sbeen\srun\sfollowing\sedits\sto\sVERSION. +D 2023-11-03T11:41:24.817 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -2108,7 +2108,7 @@ F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaa F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848 F tool/src-verify.c 41c586dee84d0b190ad13e0282ed83d4a65ec9fefde9adf4943efdf6558eea7f F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f -F tool/srctree-check.tcl 9f1098d439159692ece45cc9e701b35df4956269399fe0c770e41f235d1ce5e6 +F tool/srctree-check.tcl c15f860a3c97d5f7b4c14b60392d9466af29dd006c4ef18127f502641e2977a8 F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d @@ -2141,8 +2141,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 268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f -R dd1ef45ff2a5ccfd075e4867f283d51c +P 15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 +R 341d9b485cad2a5cb5b07c2f4f98a2ae U drh -Z b80b47952b8aa2091fd6d596beedfe43 +Z 90bfecd59f7685a9235e07a588127673 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c98a1f3b06..877f63d98b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 \ No newline at end of file +52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 \ No newline at end of file diff --git a/tool/srctree-check.tcl b/tool/srctree-check.tcl index f44e3ceec5..51226cda46 100644 --- a/tool/srctree-check.tcl +++ b/tool/srctree-check.tcl @@ -36,6 +36,17 @@ set TCLSH [info nameofexe] # set NERR 0 +######################### configure ########################################### + +set conf [readfile $ROOT/configure] +set vers [readfile $ROOT/VERSION] +if {[string first $vers $conf]<=0} { + puts "ERROR: ./configure does not agree with ./VERSION" + puts "...... Fix: run autoconf" + incr NERR +} +unset conf + ######################### autoconf/tea/configure.ac ########################### set confac [readfile $ROOT/autoconf/tea/configure.ac] @@ -45,9 +56,11 @@ append pattern [string trim $vers] append pattern {])} if {[string first $pattern $confac]<=0} { puts "ERROR: ./autoconf/tea/configure.ac does not agree with ./VERSION" - puts "...... Fix: manually edit ./autoconf/tea/configure.ac to" + puts "...... Fix: manually edit ./autoconf/tea/configure.ac and put the" + puts "...... correct version number in AC_INIT()" incr NERR } +unset confac ######################### autoconf/Makefile.msc ############################### From 073f8f54581a6303c2c6ef140fe76557a805a240 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 3 Nov 2023 11:53:13 +0000 Subject: [PATCH 099/347] Bind the sqlite3_keyword_...() and sqlite3_compileoption_...() family of functions to the JNI wrapper1 API. FossilOrigin-Name: b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd --- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 41 +++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 12 +++++- manifest | 16 ++++---- manifest.uuid | 2 +- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index b464bd7d5c..6bb87cc392 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -137,6 +137,47 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_threadsafe(); } + /** + Analog to sqlite3_compileoption_get(). + */ + public static String compileOptionGet(int n){ + return CApi.sqlite3_compileoption_get(n); + } + + /** + Analog to sqlite3_compileoption_used(). + */ + public static boolean compileOptionUsed(String optName){ + return CApi.sqlite3_compileoption_used(optName); + } + + /** + Analog to sqlite3_complete(). + */ + public static boolean isCompleteStatement(String sql){ + switch(CApi.sqlite3_complete(sql)){ + case 0: return false; + case CApi.SQLITE_MISUSE: + throw new IllegalArgumentException("Input may not be null."); + case CApi.SQLITE_NOMEM: + throw new OutOfMemoryError(); + default: + return true; + } + } + + public static int keywordCount(){ + return CApi.sqlite3_keyword_count(); + } + + public static boolean keywordCheck(String word){ + return CApi.sqlite3_keyword_check(word); + } + + public static String keywordName(int index){ + return CApi.sqlite3_keyword_name(index); + } + public static boolean strglob(String glob, String txt){ return 0==CApi.sqlite3_strglob(glob, txt); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index ca4e6d0521..e6de9ab018 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -412,6 +412,16 @@ public class Tester2 implements Runnable { db.close(); } + private void testKeyword(){ + final int n = Sqlite.keywordCount(); + affirm( n>0 ); + affirm( !Sqlite.keywordCheck("_nope_") ); + affirm( Sqlite.keywordCheck("seLect") ); + affirm( null!=Sqlite.keywordName(0) ); + affirm( null!=Sqlite.keywordName(n-1) ); + affirm( null==Sqlite.keywordName(n) ); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); @@ -529,7 +539,7 @@ public class Tester2 implements Runnable { } if( sqlLog ){ - if( CApi.sqlite3_compileoption_used("ENABLE_SQLLOG") ){ + if( Sqlite.compileOptionUsed("ENABLE_SQLLOG") ){ final ConfigSqllogCallback log = new ConfigSqllogCallback() { @Override public void call(sqlite3 db, String msg, int op){ switch(op){ diff --git a/manifest b/manifest index 8d752c5796..a27a5977bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\stool/srctree-check.tcl\sscript\sso\sthat\sit\sverifies\sthat\sautoconf\nhas\sbeen\srun\sfollowing\sedits\sto\sVERSION. -D 2023-11-03T11:41:24.817 +C Bind\sthe\ssqlite3_keyword_...()\sand\ssqlite3_compileoption_...()\sfamily\sof\sfunctions\sto\sthe\sJNI\swrapper1\sAPI. +D 2023-11-03T11:53:13.823 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -295,9 +295,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 9be20f3ba6edbe63285224d5d640dbfb0d84fb8cf5134084e3ae40004ec9d6d9 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fae71b7454fa3d9243ad26c80e55b590c044a0f0a23d18cae21f0cfa3a92a969 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 698fd699dd113fa2bceb78d3565f3d4578dee86150fb86e1838ec915994aa57b F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java f74b9000cc24d415cfdd9fd77234aa6ab0549871f495529993cee6176cad0329 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2141,8 +2141,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 15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 -R 341d9b485cad2a5cb5b07c2f4f98a2ae -U drh -Z 90bfecd59f7685a9235e07a588127673 +P 52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 +R 713111c1d3b2b5aa68a4fe22b929906b +U stephan +Z 5f0fb960919ed3f05e59594d37935544 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 877f63d98b..0a15fa2c89 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 \ No newline at end of file +b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd \ No newline at end of file From 7b5123c7963c0b956e298e9e355ee45fdbd4de02 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 3 Nov 2023 12:09:22 +0000 Subject: [PATCH 100/347] Enhance the JSONB lookup routine with logic to apply edits. The new logic is currently unused and hence untested but does not create any regressions. FossilOrigin-Name: b12110276fc15d1b6b0cc455f89747ace7a09650d5ba433d8bb431bb4e5bc951 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 102 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 869a09d63f..9aadbdf941 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\sfixes\sand\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-11-03T11:35:33.434 +C Enhance\sthe\sJSONB\slookup\sroutine\swith\slogic\sto\sapply\sedits.\s\sThe\snew\slogic\sis\ncurrently\sunused\sand\shence\suntested\sbut\sdoes\snot\screate\sany\sregressions. +D 2023-11-03T12:09:22.342 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -683,7 +683,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 2c0599c54aef24739015a0b7f7b569adecd760e4d06afd3d9c01c8c53713d3f0 +F src/json.c e109da4d5f29e239db837dad09a886a78b6a217592ea8c71e3b584483ce9b179 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c @@ -2142,8 +2142,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 a420a4f7ff76b0e9cf5f6d515ccfa31e526d58f4001a4015a367e2aa3c82091f 15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344 -R 7bce1ab3b73fc9ca8b3b94f2470fc2d6 +P b089bf46374b374d02d7654c65eb3e75d1777638b398061e47644af0dab48c9b +R 02d78f9919449761c67674ad6f60265a U drh -Z 13124fe09b7592eb801d594e79ae949a +Z f8a451c94f8b9175a3b99dd9c9c86acd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 71f504a420..c8ced97e73 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b089bf46374b374d02d7654c65eb3e75d1777638b398061e47644af0dab48c9b \ No newline at end of file +b12110276fc15d1b6b0cc455f89747ace7a09650d5ba433d8bb431bb4e5bc951 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 48d456e536..e998af5230 100644 --- a/src/json.c +++ b/src/json.c @@ -341,8 +341,19 @@ struct JsonParse { u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[] */ u8 *aBlob; /* BLOB representation of zJson */ + /* Search and edit information. See jsonLookupBlobStep() */ + u8 eEdit; /* Edit operation to apply */ + int delta; /* Size change due to the edit */ + u32 nIns; /* Number of bytes to insert */ + u8 *aIns; /* Content to be inserted */ }; +/* Allowed values for JsonParse.eEdit */ +#define JEDIT_DEL 0x01 /* Delete if exists */ +#define JEDIT_INS 0x02 /* Insert if not exists */ +#define JEDIT_REPL 0x04 /* Overwrite if exists */ +#define JEDIT_SET 0x08 /* Insert or overwrite */ + /* ** Maximum nesting depth of JSON for this implementation. ** @@ -720,8 +731,6 @@ static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ } } - - /* ** Append an sqlite3_value (such as a function parameter) to the JSON ** string under construction in p. @@ -2558,6 +2567,31 @@ static int jsonBlobExpand(JsonParse *pParse, u32 N){ return 0; } +/* +** If pParse->aBlob is not previously editable (because it is taken +** from sqlite3_value_blob(), as indicated by the fact that +** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable +** by making a copy into space obtained from malloc. +** +** Return true on success. Return false on OOM. +*/ +static int jsonBlobMakeEditable(JsonParse *pParse){ + u8 *aOld; + u32 nSize; + if( pParse->nBlobAlloc>0 ) return 1; + aOld = pParse->aBlob; + nSize = pParse->nBlob + pParse->nIns; + if( nSize>100 ) nSize -= 100; + pParse->aBlob = 0; + if( jsonBlobExpand(pParse, nSize) ){ + return 0; + } + assert( pParse->nBlobAlloc >= pParse->nBlob + pParse->nIns ); + memcpy(pParse->aBlob, aOld, pParse->nBlob); + return 1; +} + + /* Expand pParse->aBlob and append N bytes. ** ** Return the number of errors. @@ -3767,12 +3801,24 @@ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ return k; } +/* +** Edit the size of the element at iRoot by the amount in pParse->delta. +*/ +static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ + u32 sz; + assert( pParse->delta==0 ); + (void)jsonbPayloadSize(pParse, iRoot, &sz); + sz += pParse->delta; + jsonBlobChangePayloadSize(pParse, iRoot, sz); +} + /* ** Error returns from jsonLookupBlobStep() */ #define JSON_BLOB_ERROR 0xffffffff #define JSON_BLOB_NOTFOUND 0xfffffffe #define JSON_BLOB_PATHERROR 0xfffffffd +#define JSON_BLOB_ISERROR(x) ((x)>=JSON_BLOB_PATHERROR) /* ** Search along zPath to find the Json element specified. Return an @@ -3785,12 +3831,39 @@ static u32 jsonLookupBlobStep( u32 iRoot, /* Begin the search at this element of aBlob[] */ const char *zPath /* The path to search */ ){ - u32 i, j, k, nKey, sz, n, iEnd; + u32 i, j, k, nKey, sz, n, iEnd, rc; const char *zKey; u8 x; - if( pParse->oom ) return 0; - if( zPath[0]==0 ) return iRoot; + if( zPath[0]==0 ){ + if( pParse->eEdit && jsonBlobMakeEditable(pParse) ){ + n = jsonbPayloadSize(pParse, iRoot, &sz); + sz += n; + if( pParse->eEdit==JEDIT_DEL ){ + memmove(&pParse->aBlob[iRoot], &pParse->aBlob[iRoot+sz], + pParse->nBlob - iRoot); + pParse->nBlob -= n; + pParse->delta = -(int)n; + }else if( pParse->eEdit==JEDIT_INS ){ + /* Already exists, so json_insert() is a no-op */ + }else{ + /* json_set() or json_replace() */ + int d = (int)pParse->nIns - (int)sz; + pParse->delta = d; + if( d!=0 ){ + if( pParse->nBlob + d > pParse->nBlobAlloc ){ + jsonBlobExpand(pParse, pParse->nBlob+d); + if( pParse->oom ) return iRoot; + } + memmove(&pParse->aBlob[iRoot+pParse->nIns], + &pParse->aBlob[iRoot+sz], + pParse->nBlob - iRoot - sz); + } + memcpy(&pParse->aBlob[iRoot], pParse->aIns, pParse->nIns); + } + } + return iRoot; + } if( zPath[0]=='.' ){ x = pParse->aBlob[iRoot]; zPath++; @@ -3828,7 +3901,9 @@ static u32 jsonLookupBlobStep( if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 || j+n+sz>iEnd ) return JSON_BLOB_ERROR; - return jsonLookupBlobStep(pParse, j, &zPath[i]); + rc = jsonLookupBlobStep(pParse, j, &zPath[i]); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + return rc; } j = k+sz; if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; @@ -3872,7 +3947,9 @@ static u32 jsonLookupBlobStep( iEnd = j+sz; while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); + return rc; } k--; n = jsonbPayloadSize(pParse, j, &sz); @@ -3880,9 +3957,20 @@ static u32 jsonLookupBlobStep( j += n+sz; } if( j>iEnd ) return JSON_BLOB_ERROR; + if( k>1 ) return JSON_BLOB_NOTFOUND; }else{ return JSON_BLOB_PATHERROR; } + if( pParse->eEdit==JEDIT_INS && jsonBlobMakeEditable(pParse) ){ + assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); + memmove(&pParse->aBlob[j], &pParse->aBlob[j+pParse->nIns], + pParse->nBlob - j); + memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); + pParse->delta = pParse->nIns; + pParse->nBlob += pParse->nIns; + jsonAfterEditSizeAdjust(pParse, iRoot); + return j; + } return JSON_BLOB_NOTFOUND; } From b94518319572c478982c0ebc5a39dbe3e7cadca7 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 3 Nov 2023 13:00:51 +0000 Subject: [PATCH 101/347] Wrap more of the stmt API behind the JNI wrapper1 API. FossilOrigin-Name: 8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a --- .../org/sqlite/jni/capi/CallbackProxy.java | 5 +- .../src/org/sqlite/jni/capi/ValueHolder.java | 5 +- .../org/sqlite/jni/wrapper1/SqlFunction.java | 15 ++++ .../src/org/sqlite/jni/wrapper1/Sqlite.java | 69 +++++++++++++++++-- .../src/org/sqlite/jni/wrapper1/Tester2.java | 22 ++++++ manifest | 20 +++--- manifest.uuid | 2 +- 7 files changed, 117 insertions(+), 21 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java b/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java index 0495702561..7df748e8d8 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java +++ b/ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java @@ -24,8 +24,9 @@ package org.sqlite.jni.capi; propagated. For callback interfaces which support returning error info to the core, the JNI binding will convert any exceptions to C-level error information. For callback interfaces which do not - support, all exceptions will necessarily be suppressed in order to - retain the C-style no-throw semantics. + support returning error information, all exceptions will + necessarily be suppressed in order to retain the C-style no-throw + semantics and avoid invoking undefined behavior in the C layer.

    Callbacks of this style follow a common naming convention: diff --git a/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java b/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java index b3f03ac867..6d5db3f669 100644 --- a/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java +++ b/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java @@ -9,14 +9,15 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains a set of tests for the sqlite3 JNI bindings. +** This file holds utility code for the sqlite3 JNI bindings. */ package org.sqlite.jni.capi; /** A helper class which simply holds a single value. Its primary use is for communicating values out of anonymous classes, as doing so - requires a "final" reference. + requires a "final" reference, as well as communicating aggregate + SQL function state across calls to such functions. */ public class ValueHolder { public T value; diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 2d0ebfaf32..5bcb3bd5fa 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -121,6 +121,17 @@ public interface SqlFunction { public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);} public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);} + /** + Callbacks should invoke this on OOM errors, instead of throwing + OutOfMemoryError, because the latter cannot be propagated + through the C API. + */ + public void resultNoMem(){CApi.sqlite3_result_error_nomem(cx);} + + /** + Analog to sqlite3_set_auxdata() but throws if argNdx is out of + range. + */ public void setAuxData(int argNdx, Object o){ /* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html @@ -132,6 +143,10 @@ public interface SqlFunction { CApi.sqlite3_set_auxdata(cx, argNdx, o); } + /** + Analog to sqlite3_get_auxdata() but throws if argNdx is out of + range. + */ public Object getAuxData(int argNdx){ valueAt(argNdx); return CApi.sqlite3_get_auxdata(cx, argNdx); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 6bb87cc392..907755d99a 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -210,11 +210,19 @@ public final class Sqlite implements AutoCloseable { extracted from it, else only the string form of rc is used. It is the caller's responsibility to filter out non-error codes such as SQLITE_ROW and SQLITE_DONE before calling this. + + As a special case, if rc is SQLITE_NOMEM, an OutOfMemoryError is + thrown. */ private void checkRc(int rc){ if( 0!=rc ){ - if( null==db || 0==sqlite3_errcode(db)) throw new SqliteException(rc); - else throw new SqliteException(db); + if( CApi.SQLITE_NOMEM==rc ){ + throw new OutOfMemoryError(); + }else if( null==db || 0==sqlite3_errcode(db)){ + throw new SqliteException(rc); + }else{ + throw new SqliteException(db); + } } } @@ -470,8 +478,11 @@ public final class Sqlite implements AutoCloseable { We save the result column count in order to prevent having to call into C to fetch that value every time we need to check that value for the columnXyz() methods. + + Design note: if this is final then we cannot zero it in + finalizeStmt(). */ - private final int resultColCount; + private int resultColCount; /** Only called by the prepare() factory functions. */ Stmt(Sqlite db, sqlite3_stmt stmt){ @@ -509,13 +520,17 @@ public final class Sqlite implements AutoCloseable { name finalize() here because this one requires a different signature. It does not throw on error here because "destructors do not throw." If it returns non-0, the object is still - finalized. + finalized, but the result code is an indication that something + went wrong in a prior call into the statement's API, as + documented for sqlite3_finalize(). */ public int finalizeStmt(){ int rc = 0; if( null!=stmt ){ sqlite3_finalize(stmt); stmt = null; + _db = null; + resultColCount = 0; } return rc; } @@ -565,6 +580,10 @@ public final class Sqlite implements AutoCloseable { */ } + /** + Returns the Sqlite which prepared this statement, or null if + this statement has been finalized. + */ public Sqlite db(){ return this._db; } /** @@ -574,6 +593,44 @@ public final class Sqlite implements AutoCloseable { checkRc(CApi.sqlite3_reset(thisStmt())); } + public boolean isBusy(){ + return CApi.sqlite3_stmt_busy(thisStmt()); + } + + public boolean isReadOnly(){ + return CApi.sqlite3_stmt_readonly(thisStmt()); + } + + public String sql(){ + return CApi.sqlite3_sql(thisStmt()); + } + + public String expandedSql(){ + return CApi.sqlite3_expanded_sql(thisStmt()); + } + + /** + Analog to sqlite3_stmt_explain() but throws if op is invalid. + */ + public void explain(int op){ + checkRc(CApi.sqlite3_stmt_explain(thisStmt(), op)); + } + + /** + Analog to sqlite3_stmt_isexplain(). + */ + public int isExplain(){ + return CApi.sqlite3_stmt_isexplain(thisStmt()); + } + + /** + Analog to sqlite3_normalized_sql(), returning null if the + library is built without the SQLITE_ENABLE_NORMALIZE flag. + */ + public String normalizedSql(){ + return CApi.sqlite3_normalized_sql(thisStmt()); + } + public void clearBindings(){ CApi.sqlite3_clear_bindings( thisStmt() ); } @@ -610,8 +667,8 @@ public final class Sqlite implements AutoCloseable { public void bindText16(int ndx, byte[] utf16){ checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, utf16)); } - public void bindText16(int ndx, String txt){ - checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, txt)); + public void bindText16(int ndx, String asUtf16){ + checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, asUtf16)); } public void bindZeroBlob(int ndx, int n){ checkRc(CApi.sqlite3_bind_zeroblob(thisStmt(), ndx, n)); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index e6de9ab018..d24551ebf1 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -422,6 +422,28 @@ public class Tester2 implements Runnable { affirm( null==Sqlite.keywordName(n) ); } + + private void testExplain(){ + final Sqlite db = openDb(); + Sqlite.Stmt q = db.prepare("SELECT 1"); + affirm( 0 == q.isExplain() ); + q.explain(0); + affirm( 0 == q.isExplain() ); + q.explain(1); + affirm( 1 == q.isExplain() ); + q.explain(2); + affirm( 2 == q.isExplain() ); + Exception ex = null; + try{ + q.explain(-1); + }catch(Exception e){ + ex = e; + } + affirm( ex instanceof SqliteException ); + q.finalizeStmt(); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index a27a5977bc..0a014ab96c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\sthe\ssqlite3_keyword_...()\sand\ssqlite3_compileoption_...()\sfamily\sof\sfunctions\sto\sthe\sJNI\swrapper1\sAPI. -D 2023-11-03T11:53:13.823 +C Wrap\smore\sof\sthe\sstmt\sAPI\sbehind\sthe\sJNI\swrapper1\sAPI. +D 2023-11-03T13:00:51.184 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca F ext/jni/src/org/sqlite/jni/capi/CApi.java 24aba7b14b11da52cd47083608d37c186122c2e2072e75b2ff923d1f15bfb9e5 -F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d +F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 29c002f3c638cc80f7db1594564a262d1beb32637824c3dca2d60a224d1f71d7 @@ -271,7 +271,7 @@ F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff22 F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 -F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab +F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e @@ -294,10 +294,10 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 9be20f3ba6edbe63285224d5d640dbfb0d84fb8cf5134084e3ae40004ec9d6d9 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 698fd699dd113fa2bceb78d3565f3d4578dee86150fb86e1838ec915994aa57b +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 1c95f5e0f872aeb9cdd174cbb2e254d158df1f8b2fee9f0e6ec82c348602a7bd F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java f74b9000cc24d415cfdd9fd77234aa6ab0549871f495529993cee6176cad0329 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 9ab7e38e6741842f8e3b74cd3ecb4953e2f1957f5229bd32663df7331245ce95 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2141,8 +2141,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 52ab3deba4d26ca0e9a84a6dff254195b4a0e2cc6cf948cf7a66bb11117e7002 -R 713111c1d3b2b5aa68a4fe22b929906b +P b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd +R 05c1b264db19fc6b766555a4fdb5a28d U stephan -Z 5f0fb960919ed3f05e59594d37935544 +Z 7009b77b29804732443dad34d2696167 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0a15fa2c89..88247987ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd \ No newline at end of file +8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a \ No newline at end of file From ab8a4b2bb04750942009346c6267a3d72dea5e60 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 3 Nov 2023 17:20:20 +0000 Subject: [PATCH 102/347] Fix a case where adjacent tokens are handled incorrectly by the fts5 snippet() function. FossilOrigin-Name: 8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 --- ext/fts5/fts5_aux.c | 8 +++ ext/fts5/test/fts5tokenizer2.test | 89 +++++++++++++++++++++++++++++++ manifest | 15 +++--- manifest.uuid | 2 +- 4 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 ext/fts5/test/fts5tokenizer2.test diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c index fa58b9aac3..b6919fc9cc 100644 --- a/ext/fts5/fts5_aux.c +++ b/ext/fts5/fts5_aux.c @@ -211,6 +211,14 @@ static int fts5HighlightCb( } if( iPos==p->iRangeEnd ){ + if( p->bOpen ){ + if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ + fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); + p->iOff = iEndOff; + } + fts5HighlightAppend(&rc, p, p->zClose, -1); + p->bOpen = 0; + } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } diff --git a/ext/fts5/test/fts5tokenizer2.test b/ext/fts5/test/fts5tokenizer2.test new file mode 100644 index 0000000000..bdabd53127 --- /dev/null +++ b/ext/fts5/test/fts5tokenizer2.test @@ -0,0 +1,89 @@ +# 2023 Nov 03 +# +# 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. +# +#*********************************************************************** +# +# Tests focusing on the built-in fts5 tokenizers. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5tokenizer2 + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +sqlite3_fts5_create_tokenizer db tst get_tst_tokenizer +proc get_tst_tokenizer {args} { + return "tst_tokenizer" +} +proc tst_tokenizer {flags txt} { + set token "" + set lTok [list] + + foreach c [split $txt {}] { + if {$token==""} { + append token $c + } else { + set t1 [string is upper $token] + set t2 [string is upper $c] + + if {$t1!=$t2} { + lappend lTok $token + set token "" + } + append token $c + } + } + if {$token!=""} { lappend lTok $token } + + set iOff 0 + foreach t $lTok { + set n [string length $t] + sqlite3_fts5_token $t $iOff [expr $iOff+$n] + incr iOff $n + } +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING fts5(t, tokenize=tst); +} + +do_execsql_test 1.1 { + INSERT INTO t1 VALUES('AAdontBBmess'); +} + +do_execsql_test 1.2 { + SELECT snippet(t1, 0, '>', '<', '...', 4) FROM t1('BB'); +} {AAdont>BB', '<') FROM t1('BB'); +} {AAdont>BB', '<') FROM t1('AA'); +} {>AA', '<') FROM t1('dont'); +} {AA>dont', '<') FROM t1('mess'); +} {AAdontBB>mess<} + +do_execsql_test 1.7 { + SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess'); +} {AAdont>BBmess<} + + +finish_test diff --git a/manifest b/manifest index 0a014ab96c..300e384233 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Wrap\smore\sof\sthe\sstmt\sAPI\sbehind\sthe\sJNI\swrapper1\sAPI. -D 2023-11-03T13:00:51.184 +C Fix\sa\scase\swhere\sadjacent\stokens\sare\shandled\sincorrectly\sby\sthe\sfts5\ssnippet()\sfunction. +D 2023-11-03T17:20:20.721 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -89,7 +89,7 @@ F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a0 F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h 05501612cc655504c5dce8ba765ab621d50fc478490089beaa0d75e00b23e520 F ext/fts5/fts5Int.h 78a63cc0795186cde5384816a9403a68c65774b35d952e05b81a1b4b158e07c8 -F ext/fts5/fts5_aux.c 35c4101613eff86902877a4dedd9400b07922e412cbdd637b45041dce2fd5388 +F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 @@ -216,6 +216,7 @@ F ext/fts5/test/fts5synonym2.test 8f891fc49cc1e8daed727051e77e1f42849c784a6a54be F ext/fts5/test/fts5tok1.test 1f7817499f5971450d8c4a652114b3d833393c8134e32422d0af27884ffe9cef F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2 F ext/fts5/test/fts5tokenizer.test ac3c9112b263a639fb0508ae73a3ee886bf4866d2153771a8e8a20c721305a43 +F ext/fts5/test/fts5tokenizer2.test cb5428c7cfb3b6a74b7adfcde65506e329112003e8dffa7501d01c2d18d02569 F ext/fts5/test/fts5trigram.test 6c4e37864f3e7d90673db5563d9736d7e40080ab94d10ebdffa94c1b77941da0 F ext/fts5/test/fts5trigram2.test 9fe4207f8a4241747aff1005258b564958588d21bfd240d6cd4c2e955d31c156 F ext/fts5/test/fts5ubsan.test 783d5a8d13ebfa169e634940228db54540780e3ba7a87ad1e4510e61440bf64b @@ -2141,8 +2142,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 b27242414d6023eac7e62cf6120e1f02b0ddc7b8f0a1e4c48111cfe88d197cbd -R 05c1b264db19fc6b766555a4fdb5a28d -U stephan -Z 7009b77b29804732443dad34d2696167 +P 8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a +R bb87795876c08bb47ab6453eb0b91bfd +U dan +Z 9c5031581d089ce3bdbdbf8fe6594110 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 88247987ba..5c122d0f90 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a \ No newline at end of file +8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 \ No newline at end of file From 5189ef98d3d942121465d07cf7b7167a1bd8955d Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 3 Nov 2023 18:45:26 +0000 Subject: [PATCH 103/347] Back out the ALWAYS inserted late yesterday. The fuzzer discovered a counter-example. FossilOrigin-Name: 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 --- manifest | 15 ++++++++------- manifest.uuid | 2 +- src/select.c | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 300e384233..18d8f5e182 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\scase\swhere\sadjacent\stokens\sare\shandled\sincorrectly\sby\sthe\sfts5\ssnippet()\sfunction. -D 2023-11-03T17:20:20.721 +C Back\sout\sthe\sALWAYS\sinserted\slate\syesterday.\s\sThe\sfuzzer\sdiscovered\sa\ncounter-example. +D 2023-11-03T18:45:26.322 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -724,7 +724,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 8ebbc67478e2d43f0b24c79258597646e526c5001d07801bba7d4059088c8b12 +F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2142,8 +2142,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8fea23dc3af023ccf2909f1b4c6f91e7df0ffaac875b15f1fb3e264fba169b6a -R bb87795876c08bb47ab6453eb0b91bfd -U dan -Z 9c5031581d089ce3bdbdbf8fe6594110 +P 8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 +Q -268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f +R 4994e654abc4ea7988499736bdf83ad0 +U drh +Z 5983c90319f7844a8c9e3e4e981877ce # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5c122d0f90..f69aa3731a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 \ No newline at end of file +570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 \ No newline at end of file diff --git a/src/select.c b/src/select.c index e97a5afb74..7a79385e0b 100644 --- a/src/select.c +++ b/src/select.c @@ -8472,7 +8472,7 @@ int sqlite3Select( updateAccumulator(pParse, regAcc, pAggInfo, eDist); if( eDist!=WHERE_DISTINCT_NOOP ){ struct AggInfo_func *pF = pAggInfo->aFunc; - if( ALWAYS(pF) ){ + if( pF ){ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); } } From a501791d59697456b4167b86abbf0efa3d62408e Mon Sep 17 00:00:00 2001 From: larrybr Date: Sat, 4 Nov 2023 02:22:04 +0000 Subject: [PATCH 104/347] Define interface between project command-line apps and a console I/O "library". FossilOrigin-Name: 64abef8314b8544fdc7b71317d61a4641dc607a1ae42b8ff21543226fd338ba2 --- manifest | 17 +++--- manifest.uuid | 2 +- src/console_io.h | 143 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 src/console_io.h diff --git a/manifest b/manifest index 18d8f5e182..ea6e754aed 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\sout\sthe\sALWAYS\sinserted\slate\syesterday.\s\sThe\sfuzzer\sdiscovered\sa\ncounter-example. -D 2023-11-03T18:45:26.322 +C Define\sinterface\sbetween\sproject\scommand-line\sapps\sand\sa\sconsole\sI/O\s"library". +D 2023-11-04T02:22:04.015 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,6 +669,7 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e +F src/console_io.h 14057195f16cace1f669f723510190f90300ee6c1ef0a6f417a54344bc57bdd0 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -2142,9 +2143,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 -Q -268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f -R 4994e654abc4ea7988499736bdf83ad0 -U drh -Z 5983c90319f7844a8c9e3e4e981877ce +P 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 +R 44e2680e2cc755f86742bfd0211316ba +T *branch * console-io-lib +T *sym-console-io-lib * +T -sym-trunk * +U larrybr +Z e3b2358cc83000c61d02936c7cb34f1b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f69aa3731a..66b2569455 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 \ No newline at end of file +64abef8314b8544fdc7b71317d61a4641dc607a1ae42b8ff21543226fd338ba2 \ No newline at end of file diff --git a/src/console_io.h b/src/console_io.h new file mode 100644 index 0000000000..b8c1ea982d --- /dev/null +++ b/src/console_io.h @@ -0,0 +1,143 @@ +/* +** 2023 November 1 +** +** 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 exposes various interfaces used for console I/O by the +** SQLite project command-line tools. These interfaces are used at +** either source conglomeration time, compilation time, or run time. +** This source provides for either inclusion into conglomerated, +** "single-source" forms or separate compilation then linking. (TBD) +** +** Platform dependencies are "hidden" here by various stratagems so +** that, provided certain conditions are met, the programs using +** this source or object code compiled from it need no explicit +** conditional compilation in their source for their console I/O. +** +** The symbols and functionality exposed here are not a public API. +** This code may change in tandem with other project code as needed. +*/ + +/* Define enum for use with following function. */ +enum ConsoleStdStreams { + CSC_NoConsole = 0, + CSC_InConsole = 1, CSC_OutConsole = 2, CSC_ErrConsole = 4, + CSC_AnyConsole = 0x7 +}; + +/* +** Classify the three standard I/O streams according to whether +** they are connected to a console attached to the process. +** +** Returns the bit-wise OR of CSC_{In,Out,Err}Console values, +** or CSC_NoConsole if none of the streams reaches a console. +** +** This function should be called before any I/O is done with +** the given streams. As a side-effect, the given inputs are +** recorded so that later I/O operations on them may be done +** differently than the C library FILE* I/O would be done, +** iff the stream is used for the I/O functions that follow. +** +** On some platforms, stream or console mode alteration (aka +** "Setup") may be made which is undone by consoleRestore(). +*/ +INT_LINKAGE ConsoleStdStreams +consoleClassifySetup( FILE *pfIn, FILE *pfOut,FILE *pfErr ); + +/* +** Undo any side-effects left by consoleClassifySetup(...). +** +** This should be called after consoleClassifySetup() and +** before the process terminates normally. It is suitable +** for use with the atexit() C library procedure. After +** this call, no I/O should be done with the console. +*/ +INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); + +/* +** Render output like fprintf(). If the output is going to the +** console and translation from UTF-8 is necessary, perform +** the needed translation. Otherwise, write formatted output +** to the provided stream almost as-is, with possibly with +** newline translation as set by set{Binary,Text}Mode(). +*/ +INT_LINKAGE int fprintfUtf8(FILE *, const char *zFmt, ...); + +/* +** Collect input like fgets(...) with special provisions for input +** from the console on platforms that require same. Defers to the +** C library fgets() when input is not from the console. Newline +** translation may be done as set by set{Binary,Text}Mode(). +*/ +INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn); + +/* +** Set given stream for binary mode, where newline translation is +** not done, or to text mode where, for some platforms, newlines +** are translated to the platform's conventional char sequence. +** +** An additional side-effect is that if the stream is one passed +** to consoleClassifySetup() as an output, it is flushed. +*/ +INT_LINKAGE void setBinaryMode(File *); +INT_LINKAGE void setTextMode(File *); + +/* +** Macros for use of a line editor. +** +** The following macros define operations involving use of a +** line-editing library or simple console interaction. +** A "T" argument is a text (char *) buffer or filename. +** A "N" argument is an integer. +** +** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. +** SHELL_READ_HISTORY(T) // Read history from file named by T. +** SHELL_WRITE_HISTORY(T) // Write history to file named by T. +** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. +** +** A console program which does interactive console input is +** expected to call: +** SHELL_READ_HISTORY(T) before collecting such input; +** SHELL_ADD_HISTORY(T) as record-worthy input is taken; +** SHELL_STIFLE_HISTORY(N) after console input ceases; then +** SHELL_WRITE_HISTORY(T) before the program exits. +*/ + +/* +** Retrieve a single line of input text from an input stream. +** +** If pfIn is the input stream passed to consoleClassifySetup(), +** and azPrompt is not NULL, then a prompt is issued before the +** line is collected, as selected by the isContinuation flag. +** Array azPrompt[{0,1}] holds the {main,continuation} prompt. +** +** If zBufPrior is not NULL then it is a buffer from a prior +** call to this routine that can be reused, or will be freed. +** +** The result is stored in space obtained from malloc() and +** must either be freed by the caller or else passed back to +** this function as zBufPrior for reuse. +** +** This function may call upon services of a line-editing +** library to interactively collect line edited input. +*/ +INT_LINKAGE char * +shellGetLine(File *pfIn, char *zBufPrior, int nLen, + short isContinuation, const char *azPrompt[2]); + +/* +** TBD: Define an interface for application(s) to generate +** completion candidates for use by the line-editor. +** +** This may be premature; the CLI is the only application +** that does this. Yet, getting line-editing melded into +** console I/O is desirable because a line-editing library +** may have to establish console operating mode, possibly +** in a way that interferes with the above functionality. +*/ From aad6808efc0b93d914ef156f976dd4b555e27e68 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 12:53:00 +0000 Subject: [PATCH 105/347] Bind the trace API to the JNI wrapper1 API and add a way to map the native-level db/stmt types to their high-level counterparts (required for translating callbacks such as tracers). FossilOrigin-Name: 702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3 --- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 11 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 111 +++++++++++++++++- .../src/org/sqlite/jni/wrapper1/Tester2.java | 58 ++++++++- manifest | 19 ++- manifest.uuid | 2 +- 5 files changed, 180 insertions(+), 21 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 4657185658..a0445adb3a 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -46,7 +46,7 @@ public class Tester1 implements Runnable { //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. - private static boolean listRunTests = false; + private static int listRunTests = 0; //! True to squelch all out() and outln() output. private static boolean quietMode = false; //! Total number of runTests() calls. @@ -1701,7 +1701,7 @@ public class Tester1 implements Runnable { mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); java.util.Collections.shuffle(mlist); } - if( listRunTests ){ + if( (!fromThread && listRunTests>0) || listRunTests>1 ){ synchronized(this.getClass()){ if( !fromThread ){ out("Initial test"," list: "); @@ -1763,8 +1763,11 @@ public class Tester1 implements Runnable { -naps: sleep small random intervals between tests in order to add some chaos for cross-thread contention. + -list-tests: outputs the list of tests being run, minus some - which are hard-coded. This is noisy in multi-threaded mode. + which are hard-coded. In multi-threaded mode, use this twice to + to emit the list run by each thread (which may differ from the initial + list, in particular if -shuffle is used). -fail: forces an exception to be thrown during the test run. Use with -shuffle to make its appearance unpredictable. @@ -1793,7 +1796,7 @@ public class Tester1 implements Runnable { }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ - listRunTests = true; + ++listRunTests; }else if(arg.equals("fail")){ forceFail = true; }else if(arg.equals("sqllog")){ diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 907755d99a..685e8c326c 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -61,11 +61,31 @@ public final class Sqlite implements AutoCloseable { public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE; public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB; + public static final int TRACE_STMT = CApi.SQLITE_TRACE_STMT; + public static final int TRACE_PROFILE = CApi.SQLITE_TRACE_PROFILE; + public static final int TRACE_ROW = CApi.SQLITE_TRACE_ROW; + public static final int TRACE_CLOSE = CApi.SQLITE_TRACE_CLOSE; + public static final int TRACE_ALL = TRACE_STMT | TRACE_PROFILE | TRACE_ROW | TRACE_CLOSE; + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; } + /** Maps org.sqlite.jni.capi.sqlite3 to Sqlite instances. */ + private static final java.util.Map nativeToWrapper + = new java.util.HashMap<>(); + + /** + Returns the Sqlite object associated with the given sqlite3 + object, or null if there is no such mapping. + */ + static Sqlite fromNative(sqlite3 low){ + synchronized(nativeToWrapper){ + return nativeToWrapper.get(low); + } + } + /** Returns a newly-opened db connection or throws SqliteException if opening fails. All arguments are as documented for @@ -84,7 +104,11 @@ public final class Sqlite implements AutoCloseable { n.close(); throw ex; } - return new Sqlite(n); + Sqlite rv = new Sqlite(n); + synchronized(nativeToWrapper){ + nativeToWrapper.put(n, rv); + } + return rv; } public static Sqlite open(String filename, int flags){ @@ -124,6 +148,9 @@ public final class Sqlite implements AutoCloseable { @Override public void close(){ if(null!=this.db){ + synchronized(nativeToWrapper){ + nativeToWrapper.remove(this.db); + } this.db.close(); this.db = null; } @@ -467,11 +494,71 @@ public final class Sqlite implements AutoCloseable { return rv; } + public interface TraceCallback { + /** + Called by sqlite3 for various tracing operations, as per + sqlite3_trace_v2(). Note that this interface elides the 2nd + argument to the native trace callback, as that role is better + filled by instance-local state. + +

    These callbacks may throw, in which case their exceptions are + converted to C-level error information. + +

    The 2nd argument to this function, if non-null, will be a an + Sqlite or Sqlite.Stmt object, depending on the first argument + (see below). + +

    The final argument to this function is the "X" argument + documented for sqlite3_trace() and sqlite3_trace_v2(). Its type + depends on value of the first argument: + +

    - SQLITE_TRACE_STMT: pNative is a Sqlite.Stmt. pX is a String + containing the prepared SQL. + +

    - SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long + holding an approximate number of nanoseconds the statement took + to run. + +

    - SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null. + +

    - SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null. + */ + void call(int traceFlag, Object pNative, Object pX); + } + + /** + Analog to sqlite3_trace_v2(). traceMask must be a mask of the + TRACE_... constants. Pass a null callback to remove tracing. + + Throws on error. + */ + public void trace(int traceMask, TraceCallback callback){ + final Sqlite self = this; + final org.sqlite.jni.capi.TraceV2Callback tc = + (null==callback) ? null : new org.sqlite.jni.capi.TraceV2Callback(){ + @SuppressWarnings("unchecked") + @Override public int call(int flag, Object pNative, Object pX){ + switch(flag){ + case TRACE_ROW: + case TRACE_PROFILE: + case TRACE_STMT: + callback.call(flag, Sqlite.Stmt.fromNative((sqlite3_stmt)pNative), pX); + break; + case TRACE_CLOSE: + callback.call(flag, self, pX); + break; + } + return 0; + } + }; + checkRc( CApi.sqlite3_trace_v2(thisDb(), traceMask, tc) ); + }; + /** Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to create new instances. */ - public final class Stmt implements AutoCloseable { + public static final class Stmt implements AutoCloseable { private Sqlite _db = null; private sqlite3_stmt stmt = null; /** @@ -489,12 +576,29 @@ public final class Sqlite implements AutoCloseable { this._db = db; this.stmt = stmt; this.resultColCount = CApi.sqlite3_column_count(stmt); + synchronized(nativeToWrapper){ + nativeToWrapper.put(this.stmt, this); + } } sqlite3_stmt nativeHandle(){ return stmt; } + /** Maps org.sqlite.jni.capi.sqlite3_stmt to Stmt instances. */ + private static final java.util.Map nativeToWrapper + = new java.util.HashMap<>(); + + /** + Returns the Stmt object associated with the given sqlite3_stmt + object, or null if there is no such mapping. + */ + static Stmt fromNative(sqlite3_stmt low){ + synchronized(nativeToWrapper){ + return nativeToWrapper.get(low); + } + } + /** If this statement is still opened, its low-level handle is returned, eelse an IllegalArgumentException is thrown. @@ -527,6 +631,9 @@ public final class Sqlite implements AutoCloseable { public int finalizeStmt(){ int rc = 0; if( null!=stmt ){ + synchronized(nativeToWrapper){ + nativeToWrapper.remove(this.stmt); + } sqlite3_finalize(stmt); stmt = null; _db = null; diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index d24551ebf1..b6f1011489 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -46,7 +46,7 @@ public class Tester2 implements Runnable { //! True to shuffle the order of the tests. private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. - private static boolean listRunTests = false; + private static int listRunTests = 0; //! True to squelch all out() and outln() output. private static boolean quietMode = false; //! Total number of runTests() calls. @@ -444,6 +444,54 @@ public class Tester2 implements Runnable { db.close(); } + + private void testTrace(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + /* Ensure that characters outside of the UTF BMP survive the trip + from Java to sqlite3 and back to Java. (At no small efficiency + penalty.) */ + final String nonBmpChar = "😃"; + db.trace( + Sqlite.TRACE_ALL, + new Sqlite.TraceCallback(){ + @Override public void call(int traceFlag, Object pNative, Object x){ + ++counter.value; + //outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName()); + switch(traceFlag){ + case Sqlite.TRACE_STMT: + affirm(pNative instanceof Sqlite.Stmt); + //outln("TRACE_STMT sql = "+x); + affirm(x instanceof String); + affirm( ((String)x).indexOf(nonBmpChar) > 0 ); + break; + case Sqlite.TRACE_PROFILE: + affirm(pNative instanceof Sqlite.Stmt); + affirm(x instanceof Long); + //outln("TRACE_PROFILE time = "+x); + break; + case Sqlite.TRACE_ROW: + affirm(pNative instanceof Sqlite.Stmt); + affirm(null == x); + //outln("TRACE_ROW = "+sqlite3_column_text16((sqlite3_stmt)pNative, 0)); + break; + case Sqlite.TRACE_CLOSE: + affirm(pNative instanceof Sqlite); + affirm(null == x); + break; + default: + affirm(false /*cannot happen*/); + break; + } + } + }); + execSql(db, "SELECT coalesce(null,null,'"+nonBmpChar+"'); "+ + "SELECT 'w"+nonBmpChar+"orld'"); + affirm( 6 == counter.value ); + db.close(); + affirm( 7 == counter.value ); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); @@ -451,7 +499,7 @@ public class Tester2 implements Runnable { mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); java.util.Collections.shuffle(mlist); } - if( listRunTests ){ + if( (!fromThread && listRunTests>0) || listRunTests>1 ){ synchronized(this.getClass()){ if( !fromThread ){ out("Initial test"," list: "); @@ -514,7 +562,9 @@ public class Tester2 implements Runnable { some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some - which are hard-coded. This is noisy in multi-threaded mode. + which are hard-coded. In multi-threaded mode, use this twice to + to emit the list run by each thread (which may differ from the initial + list, in particular if -shuffle is used). -fail: forces an exception to be thrown during the test run. Use with -shuffle to make its appearance unpredictable. @@ -543,7 +593,7 @@ public class Tester2 implements Runnable { }else if(arg.equals("shuffle")){ shuffle = true; }else if(arg.equals("list-tests")){ - listRunTests = true; + ++listRunTests; }else if(arg.equals("fail")){ forceFail = true; }else if(arg.equals("sqllog")){ diff --git a/manifest b/manifest index 18d8f5e182..ae3068b124 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\sout\sthe\sALWAYS\sinserted\slate\syesterday.\s\sThe\sfuzzer\sdiscovered\sa\ncounter-example. -D 2023-11-03T18:45:26.322 +C Bind\sthe\strace\sAPI\sto\sthe\sJNI\swrapper1\sAPI\sand\sadd\sa\sway\sto\smap\sthe\snative-level\sdb/stmt\stypes\sto\stheir\shigh-level\scounterparts\s(required\sfor\stranslating\scallbacks\ssuch\sas\stracers). +D 2023-11-04T12:53:00.737 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java fba87e2c39ba186bb7add972d9e84b7f817f656452cf4f317679575bd5a738e7 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 1c95f5e0f872aeb9cdd174cbb2e254d158df1f8b2fee9f0e6ec82c348602a7bd +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12a9323a74e38e7c6229dc73c5b62bf50088a65310100f383469308549381907 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 9ab7e38e6741842f8e3b74cd3ecb4953e2f1957f5229bd32663df7331245ce95 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 83cfe6583c8df226eda985eed059f47efaefaca3951c618c286ffc8c63210ee8 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,9 +2142,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 8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795 -Q -268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f -R 4994e654abc4ea7988499736bdf83ad0 -U drh -Z 5983c90319f7844a8c9e3e4e981877ce +P 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 +R cb275d62547ff275bdfb4b29d97a5241 +U stephan +Z 2824e678eb263c5ade76d9ba9fb58525 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f69aa3731a..0e828aa55c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 \ No newline at end of file +702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3 \ No newline at end of file From 5f3b13d1367f13e8f70d85522228c71c33cf1fd9 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 13:16:49 +0000 Subject: [PATCH 106/347] Bind the bool-flag sqlite3_db_config() variants to the JNI wrapper1 API. FossilOrigin-Name: b5cdcb9279d9276f24b67083839f463beecd731f46f2e8bf68fff716df0a3921 --- ext/jni/src/c/sqlite3-jni.c | 1 - ext/jni/src/org/sqlite/jni/capi/CApi.java | 3 +- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 1 + .../src/org/sqlite/jni/wrapper1/Sqlite.java | 32 +++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 21 ++++++++---- manifest | 20 ++++++------ manifest.uuid | 2 +- 7 files changed, 60 insertions(+), 20 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 6d54391e18..e6b0750cbd 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -3429,7 +3429,6 @@ S3JniApi( } break; } - case 0: default: rc = SQLITE_MISUSE; } diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 6eeeb29a24..ef1ca17b22 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -828,7 +828,7 @@ public final class CApi { SQLITE_DBCONFIG_... options which uses this call form.

    Unlike the C API, this returns SQLITE_MISUSE if its db argument - are null (as opposed to invoking UB). + is null (as opposed to invoking UB). */ public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out @@ -851,7 +851,6 @@ public final class CApi { return null==db ? null : sqlite3_db_name(db.getNativePointer(), ndx); } - public static native String sqlite3_db_filename( @NotNull sqlite3 db, @NotNull String dbName ); diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index a0445adb3a..fdadb3e29d 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1049,6 +1049,7 @@ public class Tester1 implements Runnable { rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); affirm( "foo".equals( sqlite3_db_name(db1, 0) ) ); + affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) ); final ValueHolder xBusyCalled = new ValueHolder<>(0); BusyHandlerCallback handler = new BusyHandlerCallback(){ diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 685e8c326c..3e24f52f33 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -67,6 +67,25 @@ public final class Sqlite implements AutoCloseable { public static final int TRACE_CLOSE = CApi.SQLITE_TRACE_CLOSE; public static final int TRACE_ALL = TRACE_STMT | TRACE_PROFILE | TRACE_ROW | TRACE_CLOSE; + public static final int DBCONFIG_ENABLE_FKEY = CApi.SQLITE_DBCONFIG_ENABLE_FKEY; + public static final int DBCONFIG_ENABLE_TRIGGER = CApi.SQLITE_DBCONFIG_ENABLE_TRIGGER; + public static final int DBCONFIG_ENABLE_FTS3_TOKENIZER = CApi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER; + public static final int DBCONFIG_ENABLE_LOAD_EXTENSION = CApi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION; + public static final int DBCONFIG_NO_CKPT_ON_CLOSE = CApi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE; + public static final int DBCONFIG_ENABLE_QPSG = CApi.SQLITE_DBCONFIG_ENABLE_QPSG; + public static final int DBCONFIG_TRIGGER_EQP = CApi.SQLITE_DBCONFIG_TRIGGER_EQP; + public static final int DBCONFIG_RESET_DATABASE = CApi.SQLITE_DBCONFIG_RESET_DATABASE; + public static final int DBCONFIG_DEFENSIVE = CApi.SQLITE_DBCONFIG_DEFENSIVE; + public static final int DBCONFIG_WRITABLE_SCHEMA = CApi.SQLITE_DBCONFIG_WRITABLE_SCHEMA; + public static final int DBCONFIG_LEGACY_ALTER_TABLE = CApi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE; + public static final int DBCONFIG_DQS_DML = CApi.SQLITE_DBCONFIG_DQS_DML; + public static final int DBCONFIG_DQS_DDL = CApi.SQLITE_DBCONFIG_DQS_DDL; + public static final int DBCONFIG_ENABLE_VIEW = CApi.SQLITE_DBCONFIG_ENABLE_VIEW; + public static final int DBCONFIG_LEGACY_FILE_FORMAT = CApi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT; + public static final int DBCONFIG_TRUSTED_SCHEMA = CApi.SQLITE_DBCONFIG_TRUSTED_SCHEMA; + public static final int DBCONFIG_STMT_SCANSTATUS = CApi.SQLITE_DBCONFIG_STMT_SCANSTATUS; + public static final int DBCONFIG_REVERSE_SCANORDER = CApi.SQLITE_DBCONFIG_REVERSE_SCANORDER; + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; @@ -375,6 +394,19 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_db_filename(thisDb(), dbName); } + /** + Analog to sqlite3_db_config() for the call forms which take one + of the boolean-type db configuration flags (namely the + DBCONFIG_... constants defined in this class). On success it + returns the result of that underlying call. Throws on error. + */ + public boolean dbConfig(int op, boolean on){ + org.sqlite.jni.capi.OutputPointer.Int32 pOut = + new org.sqlite.jni.capi.OutputPointer.Int32(); + checkRc( CApi.sqlite3_db_config(thisDb(), op, on ? 1 : 0, pOut) ); + return pOut.get()!=0; + } + /** Analog to the variant of sqlite3_db_config() for configuring the SQLITE_DBCONFIG_MAINDBNAME option. Throws on error. diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index b6f1011489..38bcb54c66 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -211,15 +211,24 @@ public class Tester2 implements Runnable { void testOpenDb1(){ Sqlite db = openDb(); affirm( 0!=db.nativeHandle().getNativePointer() ); + affirm( "main".equals( db.dbName(0) ) ); + db.setMainDbName("foo"); + affirm( "foo".equals( db.dbName(0) ) ); + affirm( db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, true) + /* The underlying function has different mangled names in jdk8 + vs jdk19, and this call is here to ensure that the build + fails if it cannot find both names. */ ); + affirm( !db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, false) ); + SqliteException ex = null; + try{ db.dbConfig(0, false); } + catch(SqliteException e){ ex = e; } + affirm( null!=ex ); + ex = null; db.close(); affirm( null==db.nativeHandle() ); - SqliteException ex = null; - try { - db = openDb("/no/such/dir/.../probably"); - }catch(SqliteException e){ - ex = e; - } + try{ db = openDb("/no/such/dir/.../probably"); } + catch(SqliteException e){ ex = e; } affirm( ex!=null ); affirm( ex.errcode() != 0 ); affirm( ex.extendedErrcode() != 0 ); diff --git a/manifest b/manifest index ae3068b124..cbf52bf2f6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\sthe\strace\sAPI\sto\sthe\sJNI\swrapper1\sAPI\sand\sadd\sa\sway\sto\smap\sthe\snative-level\sdb/stmt\stypes\sto\stheir\shigh-level\scounterparts\s(required\sfor\stranslating\scallbacks\ssuch\sas\stracers). -D 2023-11-04T12:53:00.737 +C Bind\sthe\sbool-flag\ssqlite3_db_config()\svariants\sto\sthe\sJNI\swrapper1\sAPI. +D 2023-11-04T13:16:49.628 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c afe9c25b82279a28fe2c81f869070fa0d434b0a8ccd7f8aca0e8173db410d14a +F ext/jni/src/c/sqlite3-jni.c 53493819418048bfdc8e6f505954c7e692d4666b64c3ae732ea8319c91aac747 F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 24aba7b14b11da52cd47083608d37c186122c2e2072e75b2ff923d1f15bfb9e5 +F ext/jni/src/org/sqlite/jni/capi/CApi.java d21e6c1c4557ae18bbc2eefb0882efdb36fdaecdc58823c142def994327a365b F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java fba87e2c39ba186bb7add972d9e84b7f817f656452cf4f317679575bd5a738e7 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 41e2b910a11dfdd4cc39ab608492d7c12f3791e85ac7f9d75d5445f7645a5e57 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12a9323a74e38e7c6229dc73c5b62bf50088a65310100f383469308549381907 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0033898c318eea50489817957ef6078064f2b70b2838733462663ca3d6b09127 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 83cfe6583c8df226eda985eed059f47efaefaca3951c618c286ffc8c63210ee8 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 00c697a57af398d6c49b2c32c10314201a5d0fa0862dc496f9d4f2139087b76b F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 -R cb275d62547ff275bdfb4b29d97a5241 +P 702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3 +R 86eeaee301f846f2f1ea6341b6a33699 U stephan -Z 2824e678eb263c5ade76d9ba9fb58525 +Z e6b3bd265a1978cd37eafbdc1dc5ccf4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0e828aa55c..3c3ee0a4df 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3 \ No newline at end of file +b5cdcb9279d9276f24b67083839f463beecd731f46f2e8bf68fff716df0a3921 \ No newline at end of file From 348e192ea896293e704afb1b8cc323fa85dba293 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 13:37:42 +0000 Subject: [PATCH 107/347] Rework the JNI wrapper1 variants of status() and db_status() to be more Java-esque. FossilOrigin-Name: 40ad3920673561a06edf0b70a50a40be6cd20817fe22b87b63a9ac80cb2c9df8 --- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 70 +++++++++++++++++-- .../src/org/sqlite/jni/wrapper1/Tester2.java | 15 ++++ manifest | 14 ++-- manifest.uuid | 2 +- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 3e24f52f33..0ea2c38768 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -44,6 +44,20 @@ public final class Sqlite implements AutoCloseable { public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE; public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT; + public static final int DBSTATUS_LOOKASIDE_USED = CApi.SQLITE_DBSTATUS_LOOKASIDE_USED; + public static final int DBSTATUS_CACHE_USED = CApi.SQLITE_DBSTATUS_CACHE_USED; + public static final int DBSTATUS_SCHEMA_USED = CApi.SQLITE_DBSTATUS_SCHEMA_USED; + public static final int DBSTATUS_STMT_USED = CApi.SQLITE_DBSTATUS_STMT_USED; + public static final int DBSTATUS_LOOKASIDE_HIT = CApi.SQLITE_DBSTATUS_LOOKASIDE_HIT; + public static final int DBSTATUS_LOOKASIDE_MISS_SIZE = CApi.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE; + public static final int DBSTATUS_LOOKASIDE_MISS_FULL = CApi.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL; + public static final int DBSTATUS_CACHE_HIT = CApi.SQLITE_DBSTATUS_CACHE_HIT; + public static final int DBSTATUS_CACHE_MISS = CApi.SQLITE_DBSTATUS_CACHE_MISS; + public static final int DBSTATUS_CACHE_WRITE = CApi.SQLITE_DBSTATUS_CACHE_WRITE; + public static final int DBSTATUS_DEFERRED_FKS = CApi.SQLITE_DBSTATUS_DEFERRED_FKS; + public static final int DBSTATUS_CACHE_USED_SHARED = CApi.SQLITE_DBSTATUS_CACHE_USED_SHARED; + public static final int DBSTATUS_CACHE_SPILL = CApi.SQLITE_DBSTATUS_CACHE_SPILL; + public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH; public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH; public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN; @@ -150,19 +164,49 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_sourceid(); } + + /** + Output object for use with status() and libStatus(). + */ + public static final class Status { + /** The current value for the requested status() or libStatus() metric. */ + long current; + /** The peak value for the requested status() or libStatus() metric. */ + long peak; + }; + /** As per sqlite3_status64(), but returns its current and high-water - results as a two-element array. Throws if the first argument is + results as a Status object. Throws if the first argument is not one of the STATUS_... constants. */ - public long[] libStatus(int op, boolean resetStats){ + public static Status libStatus(int op, boolean resetStats){ org.sqlite.jni.capi.OutputPointer.Int64 pCurrent = new org.sqlite.jni.capi.OutputPointer.Int64(); org.sqlite.jni.capi.OutputPointer.Int64 pHighwater = new org.sqlite.jni.capi.OutputPointer.Int64(); - final int rc = CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats); - checkRc(rc); - return new long[] {pCurrent.value, pHighwater.value}; + checkRc2( CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats) ); + final Status s = new Status(); + s.current = pCurrent.value; + s.peak = pHighwater.value; + return s; + } + + /** + As per sqlite3_status64(), but returns its current and high-water + results as a Status object. Throws if the first argument is + not one of the DBSTATUS_... constants or on any other misuse. + */ + public Status status(int op, boolean resetStats){ + org.sqlite.jni.capi.OutputPointer.Int32 pCurrent = + new org.sqlite.jni.capi.OutputPointer.Int32(); + org.sqlite.jni.capi.OutputPointer.Int32 pHighwater = + new org.sqlite.jni.capi.OutputPointer.Int32(); + checkRc( CApi.sqlite3_db_status(thisDb(), op, pCurrent, pHighwater, resetStats) ); + final Status s = new Status(); + s.current = pCurrent.value; + s.peak = pHighwater.value; + return s; } @Override public void close(){ @@ -272,6 +316,20 @@ public final class Sqlite implements AutoCloseable { } } + /** + Like checkRc() but behaves as if that function were + called with a null db object. + */ + private static void checkRc2(int rc){ + if( 0!=rc ){ + if( CApi.SQLITE_NOMEM==rc ){ + throw new OutOfMemoryError(); + }else{ + throw new SqliteException(rc); + } + } + } + /** prepFlags must be 0 or a bitmask of the PREPARE_... constants. @@ -440,7 +498,7 @@ public final class Sqlite implements AutoCloseable { /** Analog to sqlite3_release_memory(). */ - public static int releaseMemory(int n){ + public static int libReleaseMemory(int n){ return CApi.sqlite3_release_memory(n); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 38bcb54c66..b83cd44601 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -501,6 +501,21 @@ public class Tester2 implements Runnable { affirm( 7 == counter.value ); } + private void testStatus(){ + final Sqlite db = openDb(); + execSql(db, "create table t(a); insert into t values(1),(2),(3)"); + + Sqlite.Status s = Sqlite.libStatus(Sqlite.STATUS_MEMORY_USED, false); + affirm( s.current > 0 ); + affirm( s.peak >= s.current ); + + s = db.status(Sqlite.DBSTATUS_SCHEMA_USED, false); + affirm( s.current > 0 ); + affirm( s.peak == 0 /* always 0 for SCHEMA_USED */ ); + + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index cbf52bf2f6..5ee7dad393 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\sthe\sbool-flag\ssqlite3_db_config()\svariants\sto\sthe\sJNI\swrapper1\sAPI. -D 2023-11-04T13:16:49.628 +C Rework\sthe\sJNI\swrapper1\svariants\sof\sstatus()\sand\sdb_status()\sto\sbe\smore\sJava-esque. +D 2023-11-04T13:37:42.589 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0033898c318eea50489817957ef6078064f2b70b2838733462663ca3d6b09127 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java ab6bee53895e5f7345b57d32ef30e9cc9c9c09979a31211f63e60dcdea9a7ab1 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 00c697a57af398d6c49b2c32c10314201a5d0fa0862dc496f9d4f2139087b76b +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java cbd087d3fcbfca384656fc2189a9b017e4afe70f0f17d8266d28c628ab97cada F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3 -R 86eeaee301f846f2f1ea6341b6a33699 +P b5cdcb9279d9276f24b67083839f463beecd731f46f2e8bf68fff716df0a3921 +R 9ee74b8e162e4e8a23028ccec92c6373 U stephan -Z e6b3bd265a1978cd37eafbdc1dc5ccf4 +Z ce3f2fb1a954c596b6cdbc560de71d23 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3c3ee0a4df..18ae81abac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b5cdcb9279d9276f24b67083839f463beecd731f46f2e8bf68fff716df0a3921 \ No newline at end of file +40ad3920673561a06edf0b70a50a40be6cd20817fe22b87b63a9ac80cb2c9df8 \ No newline at end of file From 1b199243cbff71e3e60caf9d7cb2be0f76eb894f Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 16:01:13 +0000 Subject: [PATCH 108/347] Expose context_db_handle() to the JNI wrapper1 API and clean up some related tests. FossilOrigin-Name: c23123af7d40dea24a0848dff987fd58a6703ce04165060533544db85983d566 --- .../jni/wrapper1/AggregateFunction.java | 4 ---- .../org/sqlite/jni/wrapper1/SqlFunction.java | 12 +++++++++- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 2 +- .../sqlite/jni/wrapper1/SqliteException.java | 2 +- .../src/org/sqlite/jni/wrapper1/Tester2.java | 18 +++++++++++---- .../sqlite/jni/wrapper1/WindowFunction.java | 4 ---- manifest | 22 +++++++++---------- manifest.uuid | 2 +- 8 files changed, 39 insertions(+), 27 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java index 6a38d4b530..fc63b53542 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java @@ -12,10 +12,6 @@ ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; -import org.sqlite.jni.capi.CApi; -import org.sqlite.jni.annotation.*; -import org.sqlite.jni.capi.sqlite3_context; -import org.sqlite.jni.capi.sqlite3_value; /** EXPERIMENTAL/INCOMPLETE/UNTESTED diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 5bcb3bd5fa..941800513d 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -56,7 +56,7 @@ public interface SqlFunction { */ Arguments(sqlite3_context cx, sqlite3_value args[]){ this.cx = cx; - this.args = args==null ? new sqlite3_value[0] : args;; + this.args = args==null ? new sqlite3_value[0] : args; this.length = this.args.length; } @@ -76,6 +76,16 @@ public interface SqlFunction { //! Returns the underlying sqlite3_context for these arguments. sqlite3_context getContext(){return cx;} + /** + Returns the Sqlite (db) object associated with this UDF call, + or null if the UDF is somehow called without such an object or + the db has been closed in an untimely manner (e.g. closed by a + UDF call). + */ + public Sqlite getDb(){ + return Sqlite.fromNative( CApi.sqlite3_context_db_handle(cx) ); + } + public int getArgCount(){ return args.length; } public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));} diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 0ea2c38768..4f14253a3e 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -781,7 +781,7 @@ public final class Sqlite implements AutoCloseable { Returns the Sqlite which prepared this statement, or null if this statement has been finalized. */ - public Sqlite db(){ return this._db; } + public Sqlite getDb(){ return this._db; } /** Works like sqlite3_reset() but throws on error. diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java index 27cfc0e6bb..09fa02a2ca 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java @@ -74,7 +74,7 @@ public final class SqliteException extends java.lang.RuntimeException { } public SqliteException(Sqlite.Stmt stmt){ - this(stmt.db()); + this(stmt.getDb()); } public int errcode(){ return errCode; } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index b83cd44601..5f991da4d3 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -241,6 +241,7 @@ public class Tester2 implements Runnable { Sqlite.Stmt stmt = db.prepare("SELECT ?1"); Exception e = null; affirm( null!=stmt.nativeHandle() ); + affirm( db == stmt.getDb() ); affirm( 1==stmt.bindParameterCount() ); affirm( "?1".equals(stmt.bindParameterName(1)) ); affirm( null==stmt.bindParameterName(2) ); @@ -294,21 +295,30 @@ public class Tester2 implements Runnable { final ValueHolder vh = new ValueHolder<>(0); final ScalarFunction f = new ScalarFunction(){ public void xFunc(SqlFunction.Arguments args){ + affirm( db == args.getDb() ); for( SqlFunction.Arguments.Arg arg : args ){ vh.value += arg.getInt(); } + args.resultInt(vh.value); } public void xDestroy(){ ++xDestroyCalled.value; } }; db.createFunction("myfunc", -1, f); - execSql(db, "select myfunc(1,2,3)"); + Sqlite.Stmt q = db.prepare("select myfunc(1,2,3)"); + affirm( q.step() ); affirm( 6 == vh.value ); - vh.value = 0; - execSql(db, "select myfunc(-1,-2,-3)"); - affirm( -6 == vh.value ); + affirm( 6 == q.columnInt(0) ); + q.finalizeStmt(); affirm( 0 == xDestroyCalled.value ); + vh.value = 0; + q = db.prepare("select myfunc(-1,-2,-3)"); + affirm( q.step() ); + affirm( -6 == vh.value ); + affirm( -6 == q.columnInt(0) ); + affirm( 0 == xDestroyCalled.value ); + q.finalizeStmt(); } affirm( 1 == xDestroyCalled.value ); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java index 479fc74d7f..a3905567d4 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java @@ -12,10 +12,6 @@ ** This file is part of the wrapper1 interface for sqlite3. */ package org.sqlite.jni.wrapper1; -import org.sqlite.jni.capi.CApi; -import org.sqlite.jni.annotation.*; -import org.sqlite.jni.capi.sqlite3_context; -import org.sqlite.jni.capi.sqlite3_value; /** A SqlFunction implementation for window functions. The T type diff --git a/manifest b/manifest index 5ee7dad393..661bf798a5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rework\sthe\sJNI\swrapper1\svariants\sof\sstatus()\sand\sdb_status()\sto\sbe\smore\sJava-esque. -D 2023-11-04T13:37:42.589 +C Expose\scontext_db_handle()\sto\sthe\sJNI\swrapper1\sAPI\sand\sclean\sup\ssome\srelated\stests. +D 2023-11-04T16:01:13.004 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -293,14 +293,14 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java a8e88c3783d21cec51b0748568a96653 F ext/jni/src/org/sqlite/jni/fts5/fts5_extension_function.java 9e2b954d210d572552b28aca523b272fae14bd41e318921b22f65b728d5bf978 F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ade23d329843f809cd0d0f4f1a2856da6e6b4d90 F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e -F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f +F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java ab6bee53895e5f7345b57d32ef30e9cc9c9c09979a31211f63e60dcdea9a7ab1 -F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java cbd087d3fcbfca384656fc2189a9b017e4afe70f0f17d8266d28c628ab97cada +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java e787f5f36d5832fe3c7a000a8609eb0629fb160b95f8f25566df13e72e6f5470 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e813aa4a680948a1885a5df1537c9245b3b7362aaf6aa31f679640e81da020e +F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 96d7908da8bad591aff8f192cb83e038fd5861ef4601726eeda24905422718c9 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af -F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4 +F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0 @@ -2142,8 +2142,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 b5cdcb9279d9276f24b67083839f463beecd731f46f2e8bf68fff716df0a3921 -R 9ee74b8e162e4e8a23028ccec92c6373 +P 40ad3920673561a06edf0b70a50a40be6cd20817fe22b87b63a9ac80cb2c9df8 +R f79739ffe6d4cd3ba58087c1ffb0d1f9 U stephan -Z ce3f2fb1a954c596b6cdbc560de71d23 +Z 05734d404abe66e6e03def34a1d0d490 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 18ae81abac..c78490991b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -40ad3920673561a06edf0b70a50a40be6cd20817fe22b87b63a9ac80cb2c9df8 \ No newline at end of file +c23123af7d40dea24a0848dff987fd58a6703ce04165060533544db85983d566 \ No newline at end of file From d4677f192ff63f5e670dc18afa9c78ab12e78c54 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 4 Nov 2023 21:44:00 +0000 Subject: [PATCH 109/347] Ensure that the YYYY-MM-DD input to date and time functions has been normalized prior to returning a result. [forum:/forumpost/6bb476897e|Forum post 6bb476897e]. FossilOrigin-Name: b692eb8ccb2d0645599ad73a8bdacf5df499114244aadeb38aabc580fc4dc7c5 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/date.c | 6 ++++++ test/date.test | 5 +++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 661bf798a5..eacc52b68c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expose\scontext_db_handle()\sto\sthe\sJNI\swrapper1\sAPI\sand\sclean\sup\ssome\srelated\stests. -D 2023-11-04T16:01:13.004 +C Ensure\sthat\sthe\sYYYY-MM-DD\sinput\sto\sdate\sand\stime\sfunctions\shas\sbeen\snormalized\nprior\sto\sreturning\sa\sresult.\n[forum:/forumpost/6bb476897e|Forum\spost\s6bb476897e]. +D 2023-11-04T21:44:00.659 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -670,7 +670,7 @@ F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b -F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 +F src/date.c 3b8d02977d160e128469de38493b4085f7c5cf4073193459909a6af3cf6d7c91 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 @@ -1016,7 +1016,7 @@ F test/ctime.test 340f362f41f92972bbd71f44e10569a5cc694062b692231bd08aa6fe6c1c47 F test/cursorhint.test 05cf0febe5c5f8a31f199401fd1c9322249e753950d55f26f9d5aca61408a270 F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee9ae9c42f F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8 -F test/date.test c0d17cdfd89395bc78087b131e3538d96f864b5029c335318011accc7c0d0934 +F test/date.test ff2341a1ef71b9a27979494d299222f9a293aa22cb9ff6e9c38d88a895317ebf F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1 F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5 F test/date4.test 8aeb3de5b5e9fda968baa9357e4c0fae573724b7904943410195a19e96e31b6a @@ -2142,8 +2142,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 40ad3920673561a06edf0b70a50a40be6cd20817fe22b87b63a9ac80cb2c9df8 -R f79739ffe6d4cd3ba58087c1ffb0d1f9 -U stephan -Z 05734d404abe66e6e03def34a1d0d490 +P c23123af7d40dea24a0848dff987fd58a6703ce04165060533544db85983d566 +R 0a52dbd298d991529b412673e9dc7eb5 +U drh +Z 33b6056e932d878d8676b163d8b94db2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c78490991b..a2ff9c7a2a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c23123af7d40dea24a0848dff987fd58a6703ce04165060533544db85983d566 \ No newline at end of file +b692eb8ccb2d0645599ad73a8bdacf5df499114244aadeb38aabc580fc4dc7c5 \ No newline at end of file diff --git a/src/date.c b/src/date.c index f163085445..e493542db9 100644 --- a/src/date.c +++ b/src/date.c @@ -1043,6 +1043,12 @@ static int isDate( } computeJD(p); if( p->isError || !validJulianDay(p->iJD) ) return 1; + if( argc==1 && p->validYMD && p->D>28 ){ + /* Make sure a YYYY-MM-DD is normalized. + ** Example: 2023-02-31 -> 2023-03-03 */ + assert( p->validJD ); + p->validYMD = 0; + } return 0; } diff --git a/test/date.test b/test/date.test index fb76dac8ac..19cecc2db3 100644 --- a/test/date.test +++ b/test/date.test @@ -146,6 +146,8 @@ datetest 2.49 {datetime('2003-10-22 12:24','0000 second')} {2003-10-22 12:24:00} datetest 2.50 {datetime('2003-10-22 12:24','0001 second')} {2003-10-22 12:24:01} datetest 2.51 {datetime('2003-10-22 12:24','nonsense')} NULL +datetest 2.60 {datetime('2023-02-31')} {2023-03-03 00:00:00} + datetest 3.1 {strftime('%d','2003-10-31 12:34:56.432')} 31 datetest 3.2.1 {strftime('pre%fpost','2003-10-31 12:34:56.432')} pre56.432post datetest 3.2.2 {strftime('%f','2003-10-31 12:34:59.9999999')} 59.999 @@ -452,6 +454,9 @@ datetest 13.31 {date('2001-01-01','+1.5 years')} {2002-07-02} datetest 13.32 {date('2002-01-01','+1.5 years')} {2003-07-02} datetest 13.33 {date('2002-01-01','-1.5 years')} {2000-07-02} datetest 13.34 {date('2001-01-01','-1.5 years')} {1999-07-02} +datetest 13.35 {date('2023-02-28')} {2023-02-28} +datetest 13.36 {date('2023-02-29')} {2023-03-01} +datetest 13.37 {date('2023-04-31')} {2023-05-01} # Test for issues reported by BareFeet (list.sql at tandb.com.au) # on mailing list on 2008-06-12. From ffdb479e7c45c8870cae7be57173ed1d55234302 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 21:51:34 +0000 Subject: [PATCH 110/347] Reimplement auto-extensions in Java for use with the JNI wrapper1 API. FossilOrigin-Name: 14ed4c64533622e5faf1aaa59c24885885aad43f1c0d4717773e79440e8e1468 --- ext/jni/src/org/sqlite/jni/capi/CApi.java | 2 +- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 2 +- .../org/sqlite/jni/wrapper1/SqlFunction.java | 4 + .../src/org/sqlite/jni/wrapper1/Sqlite.java | 109 ++++++++++++++++-- .../src/org/sqlite/jni/wrapper1/Tester2.java | 77 +++++++++++++ manifest | 22 ++-- manifest.uuid | 2 +- 7 files changed, 194 insertions(+), 24 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index ef1ca17b22..79568e74ad 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -891,7 +891,7 @@ public final class CApi { } public static native boolean sqlite3_extended_result_codes( - @NotNull sqlite3 db, boolean onoff + @NotNull sqlite3 db, boolean on ); static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb); diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index fdadb3e29d..479b5f72bc 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1419,7 +1419,7 @@ public class Tester1 implements Runnable { val.value = 0; final AutoExtensionCallback ax2 = new AutoExtensionCallback(){ - @Override public synchronized int call(sqlite3 db){ + @Override public int call(sqlite3 db){ ++val.value; return 0; } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 941800513d..b3317029c7 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -117,6 +117,10 @@ public interface SqlFunction { public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);} public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);} public void resultNull(){CApi.sqlite3_result_null(cx);} + /** + Analog to sqlite3_result_value(), using the Value object at the + given argument index. + */ public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));} public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);} public void resultZeroBlob(long n){ diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 4f14253a3e..298b6ae55e 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -13,7 +13,6 @@ */ package org.sqlite.jni.wrapper1; import java.nio.charset.StandardCharsets; -import static org.sqlite.jni.capi.CApi.*; import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; import org.sqlite.jni.capi.sqlite3_stmt; @@ -129,7 +128,7 @@ public final class Sqlite implements AutoCloseable { */ public static Sqlite open(String filename, int flags, String vfsName){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); - final int rc = sqlite3_open_v2(filename, out, flags, vfsName); + final int rc = CApi.sqlite3_open_v2(filename, out, flags, vfsName); final sqlite3 n = out.take(); if( 0!=rc ){ if( null==n ) throw new SqliteException(rc); @@ -137,10 +136,11 @@ public final class Sqlite implements AutoCloseable { n.close(); throw ex; } - Sqlite rv = new Sqlite(n); + final Sqlite rv = new Sqlite(n); synchronized(nativeToWrapper){ nativeToWrapper.put(n, rv); } + runAutoExtensions(rv); return rv; } @@ -149,7 +149,7 @@ public final class Sqlite implements AutoCloseable { } public static Sqlite open(String filename){ - return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null); + return open(filename, OPEN_READWRITE|OPEN_CREATE, null); } public static String libVersion(){ @@ -308,7 +308,7 @@ public final class Sqlite implements AutoCloseable { if( 0!=rc ){ if( CApi.SQLITE_NOMEM==rc ){ throw new OutOfMemoryError(); - }else if( null==db || 0==sqlite3_errcode(db)){ + }else if( null==db || 0==CApi.sqlite3_errcode(db)){ throw new SqliteException(rc); }else{ throw new SqliteException(db); @@ -343,7 +343,7 @@ public final class Sqlite implements AutoCloseable { */ public Stmt prepare(String sql, int prepFlags){ final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); - final int rc = sqlite3_prepare_v3(thisDb(), sql, prepFlags, out); + final int rc = CApi.sqlite3_prepare_v3(thisDb(), sql, prepFlags, out); checkRc(rc); final sqlite3_stmt q = out.take(); if( null==q ){ @@ -724,7 +724,7 @@ public final class Sqlite implements AutoCloseable { synchronized(nativeToWrapper){ nativeToWrapper.remove(this.stmt); } - sqlite3_finalize(stmt); + CApi.sqlite3_finalize(stmt); stmt = null; _db = null; resultColCount = 0; @@ -745,8 +745,8 @@ public final class Sqlite implements AutoCloseable { private int checkRc(int rc){ switch(rc){ case 0: - case SQLITE_ROW: - case SQLITE_DONE: return rc; + case CApi.SQLITE_ROW: + case CApi.SQLITE_DONE: return rc; default: if( null==stmt ) throw new SqliteException(rc); else throw new SqliteException(this); @@ -759,7 +759,7 @@ public final class Sqlite implements AutoCloseable { result. */ public boolean step(){ - switch(checkRc(sqlite3_step(thisStmt()))){ + switch(checkRc(CApi.sqlite3_step(thisStmt()))){ case CApi.SQLITE_ROW: return true; case CApi.SQLITE_DONE: return false; default: @@ -930,4 +930,93 @@ public final class Sqlite implements AutoCloseable { } } /* Stmt class */ + /** + Interface for auto-extensions, as per the + sqlite3_auto_extension() API. + + Design note: the chicken/egg timing of auto-extension execution + requires that this feature be entirely re-implemented in Java + because the C-level API has no access to the Sqlite type so + cannot pass on an object of that type while the database is being + opened. One side effect of this reimplementation is that this + class's list of auto-extensions is 100% independent of the + C-level list so, e.g., clearAutoExtensions() will have no effect + on auto-extensions added via the C-level API and databases opened + from that level of API will not be passed to this level's + AutoExtension instances. + */ + public interface AutoExtension { + public void call(Sqlite db); + } + + private static final java.util.Set autoExtensions = + new java.util.LinkedHashSet<>(); + + /** + Passes db to all auto-extensions. If any one of them throws, + db.close() is called before the exception is propagated. + */ + private static void runAutoExtensions(Sqlite db){ + AutoExtension list[]; + synchronized(autoExtensions){ + /* Avoid that modifications to the AutoExtension list from within + auto-extensions affect this execution of this list. */ + list = autoExtensions.toArray(new AutoExtension[0]); + } + try { + for( AutoExtension ax : list ) ax.call(db); + }catch(Exception e){ + db.close(); + throw e; + } + } + + /** + Analog to sqlite3_auto_extension(), adds the given object to the + list of auto-extensions if it is not already in that list. The + given object will be run as part of Sqlite.open(), and passed the + being-opened database. If the extension throws then open() will + fail. + + This API does not guaranty whether or not manipulations made to + the auto-extension list from within auto-extension callbacks will + affect the current traversal of the auto-extension list. Whether + or not they do is unspecified and subject to change between + versions. e.g. if an AutoExtension calls addAutoExtension(), + whether or not the new extension will be run on the being-opened + database is undefined. + + Note that calling Sqlite.open() from an auto-extension will + necessarily result in recursion loop and (eventually) a stack + overflow. + */ + public static void addAutoExtension( AutoExtension e ){ + if( null==e ){ + throw new IllegalArgumentException("AutoExtension may not be null."); + } + synchronized(autoExtensions){ + autoExtensions.add(e); + } + } + + /** + Removes the given object from the auto-extension list if it is in + that list, otherwise this has no side-effects beyond briefly + locking that list. + */ + public static void removeAutoExtension( AutoExtension e ){ + synchronized(autoExtensions){ + autoExtensions.remove(e); + } + } + + /** + Removes all auto-extensions which were added via addAutoExtension(). + */ + public static void clearAutoExtensions(){ + synchronized(autoExtensions){ + autoExtensions.clear(); + } + } + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 5f991da4d3..4402ea6b3a 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -526,6 +526,83 @@ public class Tester2 implements Runnable { db.close(); } + @SingleThreadOnly /* because multiple threads legitimately make these + results unpredictable */ + private synchronized void testAutoExtension(){ + final ValueHolder val = new ValueHolder<>(0); + final ValueHolder toss = new ValueHolder<>(null); + final Sqlite.AutoExtension ax = new Sqlite.AutoExtension(){ + @Override public void call(Sqlite db){ + ++val.value; + if( null!=toss.value ){ + throw new RuntimeException(toss.value); + } + } + }; + Sqlite.addAutoExtension(ax); + openDb().close(); + affirm( 1==val.value ); + openDb().close(); + affirm( 2==val.value ); + Sqlite.clearAutoExtensions(); + openDb().close(); + affirm( 2==val.value ); + + Sqlite.addAutoExtension( ax ); + Sqlite.addAutoExtension( ax ); // Must not add a second entry + Sqlite.addAutoExtension( ax ); // or a third one + openDb().close(); + affirm( 3==val.value ); + + Sqlite db = openDb(); + affirm( 4==val.value ); + execSql(db, "ATTACH ':memory:' as foo"); + affirm( 4==val.value, "ATTACH uses the same connection, not sub-connections." ); + db.close(); + db = null; + + Sqlite.removeAutoExtension(ax); + openDb().close(); + affirm( 4==val.value ); + Sqlite.addAutoExtension(ax); + Exception err = null; + toss.value = "Throwing from auto_extension."; + try{ + openDb(); + }catch(Exception e){ + err = e; + } + affirm( err!=null ); + affirm( err.getMessage().indexOf(toss.value)>=0 ); + toss.value = null; + + val.value = 0; + final Sqlite.AutoExtension ax2 = new Sqlite.AutoExtension(){ + @Override public void call(Sqlite db){ + ++val.value; + } + }; + Sqlite.addAutoExtension(ax2); + openDb().close(); + affirm( 2 == val.value ); + Sqlite.removeAutoExtension(ax); + openDb().close(); + affirm( 3 == val.value ); + Sqlite.addAutoExtension(ax); + openDb().close(); + affirm( 5 == val.value ); + Sqlite.removeAutoExtension(ax2); + openDb().close(); + affirm( 6 == val.value ); + Sqlite.addAutoExtension(ax2); + openDb().close(); + affirm( 8 == val.value ); + + Sqlite.clearAutoExtensions(); + openDb().close(); + affirm( 8 == val.value ); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index eacc52b68c..23488e42ad 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sthat\sthe\sYYYY-MM-DD\sinput\sto\sdate\sand\stime\sfunctions\shas\sbeen\snormalized\nprior\sto\sreturning\sa\sresult.\n[forum:/forumpost/6bb476897e|Forum\spost\s6bb476897e]. -D 2023-11-04T21:44:00.659 +C Reimplement\sauto-extensions\sin\sJava\sfor\suse\swith\sthe\sJNI\swrapper1\sAPI. +D 2023-11-04T21:51:34.589 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java d21e6c1c4557ae18bbc2eefb0882efdb36fdaecdc58823c142def994327a365b +F ext/jni/src/org/sqlite/jni/capi/CApi.java 4043d709626079cce6d524ef49122b934c043022bd88bc1e72eb697ac8df86e7 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 41e2b910a11dfdd4cc39ab608492d7c12f3791e85ac7f9d75d5445f7645a5e57 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 96c27ae10ec44ce5f6a150e8bc6525d86ab2d9118da18649943a0bf4d8d206ce F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -295,10 +295,10 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java e787f5f36d5832fe3c7a000a8609eb0629fb160b95f8f25566df13e72e6f5470 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3e813aa4a680948a1885a5df1537c9245b3b7362aaf6aa31f679640e81da020e +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3da22cb18d8544fff1c7184aeaa2516c20d63e8a31db848eb7470ce285b284dc F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 96d7908da8bad591aff8f192cb83e038fd5861ef4601726eeda24905422718c9 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 03638a1774a95bcc7b5de440a5f1398720460e30fc480032a2e8be24e997d30c F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 c23123af7d40dea24a0848dff987fd58a6703ce04165060533544db85983d566 -R 0a52dbd298d991529b412673e9dc7eb5 -U drh -Z 33b6056e932d878d8676b163d8b94db2 +P b692eb8ccb2d0645599ad73a8bdacf5df499114244aadeb38aabc580fc4dc7c5 +R 99817a8518af3f567a07823f0172a1fb +U stephan +Z 276f4a4bca9307a54b6144f3c8e4f323 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a2ff9c7a2a..89c3bcfb2b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b692eb8ccb2d0645599ad73a8bdacf5df499114244aadeb38aabc580fc4dc7c5 \ No newline at end of file +14ed4c64533622e5faf1aaa59c24885885aad43f1c0d4717773e79440e8e1468 \ No newline at end of file From dc8a684c11113b9e2ce8fa5c0372f57fcd8c7869 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 22:47:40 +0000 Subject: [PATCH 111/347] Wrap the sqlite3_backup API in the JNI wrapper1 API. FossilOrigin-Name: 3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 --- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 119 ++++++++++++++++-- .../src/org/sqlite/jni/wrapper1/Tester2.java | 35 ++++++ manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 154 insertions(+), 16 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 298b6ae55e..85b5460f34 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -16,6 +16,7 @@ import java.nio.charset.StandardCharsets; import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; import org.sqlite.jni.capi.sqlite3_stmt; +import org.sqlite.jni.capi.sqlite3_backup; import org.sqlite.jni.capi.OutputPointer; /** @@ -767,14 +768,6 @@ public final class Sqlite implements AutoCloseable { "This \"cannot happen\": all possible result codes were checked already." ); } - /* - Potential signature change TODO: - - boolean step() - - Returning true for SQLITE_ROW and false for anything else. - Those semantics have proven useful in the WASM/JS bindings. - */ } /** @@ -1019,4 +1012,114 @@ public final class Sqlite implements AutoCloseable { } } + /** + Encapsulates state related to the sqlite3 backup API. Use + Sqlite.initBackup() to create new instances. + */ + public static final class Backup implements AutoCloseable { + private sqlite3_backup b = null; + private Sqlite dbTo = null; + private Sqlite dbFrom = null; + + + public static final int DONE = CApi.SQLITE_DONE; + public static final int BUSY = CApi.SQLITE_BUSY; + public static final int LOCKED = CApi.SQLITE_LOCKED; + + Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){ + this.dbTo = dbDest; + this.dbFrom = dbSrc; + b = CApi.sqlite3_backup_init(dbDest.nativeHandle(), schemaDest, + dbSrc.nativeHandle(), schemaSrc); + if(null==b) toss(); + } + + private void toss(){ + int rc = CApi.sqlite3_errcode(dbTo.nativeHandle()); + if(0!=rc) throw new SqliteException(dbTo); + rc = CApi.sqlite3_errcode(dbFrom.nativeHandle()); + if(0!=rc) throw new SqliteException(dbFrom); + throw new SqliteException(CApi.SQLITE_ERROR); + } + + private sqlite3_backup getNative(){ + if( null==b ) throw new IllegalStateException("This Backup is already closed."); + return b; + } + /** + If this backup is still active, this completes the backup and + frees its native resources, otherwise it this is a no-op. + */ + public void finish(){ + if( null!=b ){ + CApi.sqlite3_backup_finish(b); + b = null; + dbTo = null; + dbFrom = null; + } + } + + /** Equivalent to finish(). */ + @Override public void close(){ + this.finish(); + } + + /** + Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds + or, DONE if the end is reached, BUSY if one of the databases is + busy, LOCKED if one of the databases is locked, and throws for + any other result code or if this object has been closed. Note + that BUSY and LOCKED are not necessarily permanent errors, so + do not trigger an exception. + */ + public int step(int pageCount){ + final int rc = CApi.sqlite3_backup_step(getNative(), pageCount); + switch(rc){ + case 0: + case DONE: + case BUSY: + case LOCKED: + return rc; + default: + toss(); + return CApi.SQLITE_ERROR/*not reached*/; + } + } + + /** + Analog to sqlite3_backup_pagecount(). + */ + public int pageCount(){ + return CApi.sqlite3_backup_pagecount(getNative()); + } + + /** + Analog to sqlite3_backup_remaining(). + */ + public int remaining(){ + return CApi.sqlite3_backup_remaining(getNative()); + } + } + + /** + Analog to sqlite3_backup_init(). If schemaSrc is null, "main" is + assumed. Throws if either this db or dbSrc (the source db) are + not opened, if either of schemaDest or schemaSrc are null, or if + the underlying call to sqlite3_backup_init() fails. + + The returned object must eventually be cleaned up by either + arranging for it to be auto-closed (e.g. using + try-with-resources) or by calling its finish() method. + */ + public Backup initBackup(String schemaDest, Sqlite dbSrc, String schemaSrc){ + thisDb(); + dbSrc.thisDb(); + if( null==schemaSrc || null==schemaDest ){ + throw new IllegalArgumentException( + "Neither the source nor destination schema name may be null." + ); + } + return new Backup(this, schemaDest, dbSrc, schemaSrc); + } + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 4402ea6b3a..d305d6b719 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -603,6 +603,41 @@ public class Tester2 implements Runnable { affirm( 8 == val.value ); } + private void testBackup(){ + final Sqlite dbDest = openDb(); + + try (Sqlite dbSrc = openDb()) { + execSql(dbSrc, new String[]{ + "pragma page_size=512; VACUUM;", + "create table t(a);", + "insert into t(a) values(1),(2),(3);" + }); + Exception e = null; + try { + dbSrc.initBackup("main",dbSrc,"main"); + }catch(Exception x){ + e = x; + } + affirm( e instanceof SqliteException ); + e = null; + try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) { + affirm( null!=b ); + int rc; + while( Sqlite.Backup.DONE!=(rc = b.step(1)) ){ + affirm( 0==rc ); + } + affirm( b.pageCount() > 0 ); + b.finish(); + } + } + + try (Sqlite.Stmt q = dbDest.prepare("SELECT sum(a) from t")) { + q.step(); + affirm( q.columnInt(0) == 6 ); + } + dbDest.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 23488e42ad..234186ce10 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reimplement\sauto-extensions\sin\sJava\sfor\suse\swith\sthe\sJNI\swrapper1\sAPI. -D 2023-11-04T21:51:34.589 +C Wrap\sthe\ssqlite3_backup\sAPI\sin\sthe\sJNI\swrapper1\sAPI. +D 2023-11-04T22:47:40.514 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 3da22cb18d8544fff1c7184aeaa2516c20d63e8a31db848eb7470ce285b284dc +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 8dd4cce0f0a42542af768a73c3c9e7bebd1c77207a35ba93de86c97d4c572847 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 03638a1774a95bcc7b5de440a5f1398720460e30fc480032a2e8be24e997d30c +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 3c2eda2efe45a051e371ba98abee34f51ceec3bb7d28dfde866646b650fcb426 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 b692eb8ccb2d0645599ad73a8bdacf5df499114244aadeb38aabc580fc4dc7c5 -R 99817a8518af3f567a07823f0172a1fb +P 14ed4c64533622e5faf1aaa59c24885885aad43f1c0d4717773e79440e8e1468 +R 631890601bc1abeba3be592d27b2aeb0 U stephan -Z 276f4a4bca9307a54b6144f3c8e4f323 +Z 39a1f06f24f0b71c35deac1228d090c6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 89c3bcfb2b..5235b01fd9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14ed4c64533622e5faf1aaa59c24885885aad43f1c0d4717773e79440e8e1468 \ No newline at end of file +3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 \ No newline at end of file From 15d38c0ddeb98168e0bca78a6a15d99f7092e225 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 4 Nov 2023 23:37:11 +0000 Subject: [PATCH 112/347] Bind collation and collation-needed to JNI wrapper1 and correct the callback return type for collation-needed callbacks in the lower-level JNI binding. FossilOrigin-Name: 0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe --- ext/jni/src/c/sqlite3-jni.c | 2 +- .../jni/capi/CollationNeededCallback.java | 7 +- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 4 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 97 +++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 61 ++++++++++++ manifest | 20 ++-- manifest.uuid | 2 +- 7 files changed, 176 insertions(+), 17 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index e6b0750cbd..bf4e73b7c3 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2817,7 +2817,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); jmethodID const xCallback = (*env)->GetMethodID( - env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)I" + env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)V" ); S3JniUnrefLocal(klazz); S3JniIfThrew { diff --git a/ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java b/ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java index fe61fe5065..ffd7fa94ab 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java @@ -21,8 +21,9 @@ public interface CollationNeededCallback extends CallbackProxy { Has the same semantics as the C-level sqlite3_create_collation() callback. -

    If it throws, the exception message is passed on to the db and - the exception is suppressed. +

    Because the C API has no mechanism for reporting errors + from this callbacks, any exceptions thrown by this callback + are suppressed. */ - int call(sqlite3 db, int eTextRep, String collationName); + void call(sqlite3 db, int eTextRep, String collationName); } diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 479b5f72bc..b97d568de8 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -593,9 +593,9 @@ public class Tester1 implements Runnable { }; final CollationNeededCallback collLoader = new CollationNeededCallback(){ @Override - public int call(sqlite3 dbArg, int eTextRep, String collationName){ + public void call(sqlite3 dbArg, int eTextRep, String collationName){ affirm(dbArg == db/* as opposed to a temporary object*/); - return sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation); + sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation); } }; int rc = sqlite3_collation_needed(db, collLoader); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 85b5460f34..80122cd350 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -100,6 +100,13 @@ public final class Sqlite implements AutoCloseable { public static final int DBCONFIG_STMT_SCANSTATUS = CApi.SQLITE_DBCONFIG_STMT_SCANSTATUS; public static final int DBCONFIG_REVERSE_SCANORDER = CApi.SQLITE_DBCONFIG_REVERSE_SCANORDER; + public static final int UTF8 = CApi.SQLITE_UTF8; + public static final int UTF16 = CApi.SQLITE_UTF16; + public static final int UTF16LE = CApi.SQLITE_UTF16LE; + public static final int UTF16BE = CApi.SQLITE_UTF16BE; + /* We elide the UTF16_ALIGNED from this interface because it + is irrelevant for the Java interface. */ + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; @@ -1122,4 +1129,94 @@ public final class Sqlite implements AutoCloseable { return new Backup(this, schemaDest, dbSrc, schemaSrc); } + + /** + Callback type for use with createCollation(). + */ + public interface Collation { + /** + Called by the SQLite core to compare inputs. Implementations + must compare its two arguments using memcmp(3) semantics. + + Warning: the SQLite core has no mechanism for reporting errors + from custom collations and its workflow does not accommodate + propagation of exceptions from callbacks. Any exceptions thrown + from collations will be silently supressed and sorting results + will be unpredictable. + */ + int call(byte[] lhs, byte[] rhs); + } + + /** + Analog to sqlite3_create_collation(). + + Throws if name is null or empty, c is null, or the encoding flag + is invalid. The encoding must be one of the UTF8, UTF16, UTF16LE, + or UTF16BE constants. + */ + public void createCollation(String name, int encoding, Collation c){ + thisDb(); + if( null==name || 0==name.length()){ + throw new IllegalArgumentException("Collation name may not be null or empty."); + } + if( null==c ){ + throw new IllegalArgumentException("Collation may not be null."); + } + switch(encoding){ + case UTF8: + case UTF16: + case UTF16LE: + case UTF16BE: + break; + default: + throw new IllegalArgumentException("Invalid Collation encoding."); + } + checkRc( + CApi.sqlite3_create_collation( + thisDb(), name, encoding, new org.sqlite.jni.capi.CollationCallback(){ + @Override public int call(byte[] lhs, byte[] rhs){ + try{return c.call(lhs, rhs);} + catch(Exception e){return 0;} + } + @Override public void xDestroy(){} + } + ) + ); + } + + /** + Callback for use with onCollationNeeded(). + */ + public interface CollationNeeded { + /** + Must behave as documented for the callback for + sqlite3_collation_needed(). + + Warning: the C API has no mechanism for reporting or + propagating errors from this callback, so any exceptions it + throws are suppressed. + */ + void call(Sqlite db, int encoding, String collationName); + } + + /** + Sets up the given object to be called by the SQLite core when it + encounters a collation name which it does not know. Pass a null + object to disconnect the object from the core. This replaces any + existing collation-needed loader, or is a no-op if the given + object is already registered. Throws if registering the loader + fails. + */ + public void onCollationNeeded( CollationNeeded cn ){ + org.sqlite.jni.capi.CollationNeededCallback cnc = null; + if( null!=cn ){ + cnc = new org.sqlite.jni.capi.CollationNeededCallback(){ + @Override public void call(sqlite3 db, int encoding, String collationName){ + final Sqlite xdb = Sqlite.fromNative(db); + if(null!=xdb) cn.call(xdb, encoding, collationName); + } + }; + } + checkRc( CApi.sqlite3_collation_needed(thisDb(), cnc) ); + } } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index d305d6b719..6ca0be5d17 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -638,6 +638,67 @@ public class Tester2 implements Runnable { dbDest.close(); } + private void testCollation(){ + final Sqlite db = openDb(); + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + final Sqlite.Collation myCollation = new Sqlite.Collation() { + private String myState = + "this is local state. There is much like it, but this is mine."; + @Override + // Reverse-sorts its inputs... + public int call(byte[] lhs, byte[] rhs){ + int len = lhs.length > rhs.length ? rhs.length : lhs.length; + int c = 0, i = 0; + for(i = 0; i < len; ++i){ + c = lhs[i] - rhs[i]; + if(0 != c) break; + } + if(0==c){ + if(i < lhs.length) c = 1; + else if(i < rhs.length) c = -1; + } + return -c; + } + }; + final Sqlite.CollationNeeded collLoader = new Sqlite.CollationNeeded(){ + @Override + public void call(Sqlite dbArg, int eTextRep, String collationName){ + affirm(dbArg == db); + db.createCollation("reversi", eTextRep, myCollation); + } + }; + db.onCollationNeeded(collLoader); + Sqlite.Stmt stmt = db.prepare("SELECT a FROM t ORDER BY a COLLATE reversi"); + int counter = 0; + while( stmt.step() ){ + final String val = stmt.columnText16(0); + ++counter; + switch(counter){ + case 1: affirm("c".equals(val)); break; + case 2: affirm("b".equals(val)); break; + case 3: affirm("a".equals(val)); break; + } + } + affirm(3 == counter); + stmt.finalizeStmt(); + stmt = db.prepare("SELECT a FROM t ORDER BY a"); + counter = 0; + while( stmt.step() ){ + final String val = stmt.columnText16(0); + ++counter; + //outln("Non-REVERSI'd row#"+counter+": "+val); + switch(counter){ + case 3: affirm("c".equals(val)); break; + case 2: affirm("b".equals(val)); break; + case 1: affirm("a".equals(val)); break; + } + } + affirm(3 == counter); + stmt.finalizeStmt(); + db.onCollationNeeded(null); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 234186ce10..2453b24fde 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Wrap\sthe\ssqlite3_backup\sAPI\sin\sthe\sJNI\swrapper1\sAPI. -D 2023-11-04T22:47:40.514 +C Bind\scollation\sand\scollation-needed\sto\sJNI\swrapper1\sand\scorrect\sthe\scallback\sreturn\stype\sfor\scollation-needed\scallbacks\sin\sthe\slower-level\sJNI\sbinding. +D 2023-11-04T23:37:11.738 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 53493819418048bfdc8e6f505954c7e692d4666b64c3ae732ea8319c91aac747 +F ext/jni/src/c/sqlite3-jni.c 931a7320f5b5745034b4fd61027ea7cc29559856e6da613e4fdcf01ef102e710 F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04 F ext/jni/src/org/sqlite/jni/capi/CApi.java 4043d709626079cce6d524ef49122b934c043022bd88bc1e72eb697ac8df86e7 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a -F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95 +F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 29c002f3c638cc80f7db1594564a262d1beb32637824c3dca2d60a224d1f71d7 F ext/jni/src/org/sqlite/jni/capi/ConfigLogCallback.java b995ca412f59b631803b93aa5b3684fce62e335d1e123207084c054abfd488d4 F ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java 701f2e4d8bdeb27cfbeeb56315d15b13d8752b0fdbca705f31bd4366c58d8a33 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 96c27ae10ec44ce5f6a150e8bc6525d86ab2d9118da18649943a0bf4d8d206ce +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 8823d962f283aa7af5878d1a87b759ca03e1c9519ae692077f785eab81c86f3f F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 8dd4cce0f0a42542af768a73c3c9e7bebd1c77207a35ba93de86c97d4c572847 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 6a861cfc8b3284c07cf2fa88916deab27f98e9e4234fae1bed1917c933c64083 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 3c2eda2efe45a051e371ba98abee34f51ceec3bb7d28dfde866646b650fcb426 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java a4e4b0b8ee0d56a383fd57b24244c6f93f8a0fe2e2ba5faacc0a3331f8d3fc84 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 14ed4c64533622e5faf1aaa59c24885885aad43f1c0d4717773e79440e8e1468 -R 631890601bc1abeba3be592d27b2aeb0 +P 3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 +R ef3119e103f71f255ed5e129f7bdb70b U stephan -Z 39a1f06f24f0b71c35deac1228d090c6 +Z 99a7727c5c06597d0eb0c0df96988bd8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5235b01fd9..48f7c632aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 \ No newline at end of file +0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe \ No newline at end of file From b02ca781ad2e8e375ccce4c1259a9b466eac0f68 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 00:02:47 +0000 Subject: [PATCH 113/347] Add busy-handler support to JNI wrapper1. FossilOrigin-Name: dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20 --- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 83 +++++++++---------- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 68 +++++++++++---- .../src/org/sqlite/jni/wrapper1/Tester2.java | 40 ++++++++- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 140 insertions(+), 69 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index b97d568de8..48ab031466 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1031,49 +1031,48 @@ public class Tester1 implements Runnable { @SingleThreadOnly /* because threads inherently break this test */ private static void testBusy(){ final String dbName = "_busy-handler.db"; - final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); - final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); - - int rc = sqlite3_open(dbName, outDb); - ++metrics.dbOpen; - affirm( 0 == rc ); - final sqlite3 db1 = outDb.get(); - execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); - rc = sqlite3_open(dbName, outDb); - ++metrics.dbOpen; - affirm( 0 == rc ); - affirm( outDb.get() != db1 ); - final sqlite3 db2 = outDb.get(); - - affirm( "main".equals( sqlite3_db_name(db1, 0) ) ); - rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); - affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); - affirm( "foo".equals( sqlite3_db_name(db1, 0) ) ); - affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) ); - - final ValueHolder xBusyCalled = new ValueHolder<>(0); - BusyHandlerCallback handler = new BusyHandlerCallback(){ - @Override public int call(int n){ - //outln("busy handler #"+n); - return n > 2 ? 0 : ++xBusyCalled.value; - } - }; - rc = sqlite3_busy_handler(db2, handler); - affirm(0 == rc); - - // Force a locked condition... - execSql(db1, "BEGIN EXCLUSIVE"); - rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt); - affirm( SQLITE_BUSY == rc); - affirm( null == outStmt.get() ); - affirm( 3 == xBusyCalled.value ); - sqlite3_close_v2(db1); - sqlite3_close_v2(db2); try{ - final java.io.File f = new java.io.File(dbName); - f.delete(); - }catch(Exception e){ - /* ignore */ + final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); + + int rc = sqlite3_open(dbName, outDb); + ++metrics.dbOpen; + affirm( 0 == rc ); + final sqlite3 db1 = outDb.get(); + execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); + rc = sqlite3_open(dbName, outDb); + ++metrics.dbOpen; + affirm( 0 == rc ); + affirm( outDb.get() != db1 ); + final sqlite3 db2 = outDb.get(); + + affirm( "main".equals( sqlite3_db_name(db1, 0) ) ); + rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); + affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); + affirm( "foo".equals( sqlite3_db_name(db1, 0) ) ); + affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) ); + + final ValueHolder xBusyCalled = new ValueHolder<>(0); + BusyHandlerCallback handler = new BusyHandlerCallback(){ + @Override public int call(int n){ + //outln("busy handler #"+n); + return n > 2 ? 0 : ++xBusyCalled.value; + } + }; + rc = sqlite3_busy_handler(db2, handler); + affirm(0 == rc); + + // Force a locked condition... + execSql(db1, "BEGIN EXCLUSIVE"); + rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt); + affirm( SQLITE_BUSY == rc); + affirm( null == outStmt.get() ); + affirm( 3 == xBusyCalled.value ); + sqlite3_close_v2(db1); + sqlite3_close_v2(db2); + }finally{ + try{(new java.io.File(dbName)).delete();} + catch(Exception e){/* ignore */} } } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 80122cd350..ffbc695242 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -32,6 +32,7 @@ public final class Sqlite implements AutoCloseable { public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; + public static final int TXN_NONE = CApi.SQLITE_TXN_NONE; public static final int TXN_READ = CApi.SQLITE_TXN_READ; public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE; @@ -107,6 +108,10 @@ public final class Sqlite implements AutoCloseable { /* We elide the UTF16_ALIGNED from this interface because it is irrelevant for the Java interface. */ + public static final int DONE = CApi.SQLITE_DONE; + public static final int BUSY = CApi.SQLITE_BUSY; + public static final int LOCKED = CApi.SQLITE_LOCKED; + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; @@ -430,10 +435,6 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_get_autocommit(thisDb()); } - public void setBusyTimeout(int ms){ - checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms)); - } - /** Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ, or TXN_WRITE to denote this database's current transaction state @@ -1028,11 +1029,6 @@ public final class Sqlite implements AutoCloseable { private Sqlite dbTo = null; private Sqlite dbFrom = null; - - public static final int DONE = CApi.SQLITE_DONE; - public static final int BUSY = CApi.SQLITE_BUSY; - public static final int LOCKED = CApi.SQLITE_LOCKED; - Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){ this.dbTo = dbDest; this.dbFrom = dbSrc; @@ -1073,19 +1069,19 @@ public final class Sqlite implements AutoCloseable { /** Analog to sqlite3_backup_step(). Returns 0 if stepping succeeds - or, DONE if the end is reached, BUSY if one of the databases is - busy, LOCKED if one of the databases is locked, and throws for - any other result code or if this object has been closed. Note - that BUSY and LOCKED are not necessarily permanent errors, so - do not trigger an exception. + or, Sqlite.DONE if the end is reached, Sqlite.BUSY if one of + the databases is busy, Sqlite.LOCKED if one of the databases is + locked, and throws for any other result code or if this object + has been closed. Note that BUSY and LOCKED are not necessarily + permanent errors, so do not trigger an exception. */ public int step(int pageCount){ final int rc = CApi.sqlite3_backup_step(getNative(), pageCount); switch(rc){ case 0: - case DONE: - case BUSY: - case LOCKED: + case Sqlite.DONE: + case Sqlite.BUSY: + case Sqlite.LOCKED: return rc; default: toss(); @@ -1219,4 +1215,42 @@ public final class Sqlite implements AutoCloseable { } checkRc( CApi.sqlite3_collation_needed(thisDb(), cnc) ); } + + /** + Callback for use with busyHandler(). + */ + public interface BusyHandler { + /** + Must function as documented for the C-level + sqlite3_busy_handler() callback argument, minus the (void*) + argument the C-level function requires. + + If this function throws, it is translated to a database-level + error. + */ + int call(int n); + } + + /** + Analog to sqlite3_busy_timeout(). + */ + public void setBusyTimeout(int ms){ + checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms)); + } + + /** + Analog to sqlite3_busy_handler(). If b is null then any + current handler is cleared. + */ + void setBusyHandler( BusyHandler b ){ + org.sqlite.jni.capi.BusyHandlerCallback bhc = null; + if( null!=b ){ + bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){ + @Override public int call(int n){ + return b.call(n); + } + }; + } + checkRc( CApi.sqlite3_busy_handler(thisDb(), bhc) ); + } } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 6ca0be5d17..3a00c0953f 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -623,7 +623,7 @@ public class Tester2 implements Runnable { try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) { affirm( null!=b ); int rc; - while( Sqlite.Backup.DONE!=(rc = b.step(1)) ){ + while( Sqlite.DONE!=(rc = b.step(1)) ){ affirm( 0==rc ); } affirm( b.pageCount() > 0 ); @@ -699,6 +699,44 @@ public class Tester2 implements Runnable { db.close(); } + @SingleThreadOnly /* because threads inherently break this test */ + private void testBusy(){ + final String dbName = "_busy-handler.db"; + try{ + Sqlite db1 = openDb(dbName); + ++metrics.dbOpen; + execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); + Sqlite db2 = openDb(dbName); + ++metrics.dbOpen; + + final ValueHolder xBusyCalled = new ValueHolder<>(0); + Sqlite.BusyHandler handler = new Sqlite.BusyHandler(){ + @Override public int call(int n){ + return n > 2 ? 0 : ++xBusyCalled.value; + } + }; + db2.setBusyHandler(handler); + + // Force a locked condition... + execSql(db1, "BEGIN EXCLUSIVE"); + int rc = 0; + SqliteException ex = null; + try{ + db2.prepare("SELECT * from t"); + }catch(SqliteException x){ + ex = x; + } + affirm( null!=ex ); + affirm( Sqlite.BUSY == ex.errcode() ); + affirm( 3 == xBusyCalled.value ); + db1.close(); + db2.close(); + }finally{ + try{(new java.io.File(dbName)).delete();} + catch(Exception e){/* ignore */} + } + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 2453b24fde..0cdcd1cfcd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\scollation\sand\scollation-needed\sto\sJNI\swrapper1\sand\scorrect\sthe\scallback\sreturn\stype\sfor\scollation-needed\scallbacks\sin\sthe\slower-level\sJNI\sbinding. -D 2023-11-04T23:37:11.738 +C Add\sbusy-handler\ssupport\sto\sJNI\swrapper1. +D 2023-11-05T00:02:47.384 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 8823d962f283aa7af5878d1a87b759ca03e1c9519ae692077f785eab81c86f3f +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4bb5e62907a422a80a0fccbcb83085e9163c2c245451312a62c7550a45d16683 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 6a861cfc8b3284c07cf2fa88916deab27f98e9e4234fae1bed1917c933c64083 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java c930ca964f605ba8f175d3b0c85099d7f93069b59bf825929c9eef9e68ac96c5 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java a4e4b0b8ee0d56a383fd57b24244c6f93f8a0fe2e2ba5faacc0a3331f8d3fc84 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 6f5fae3c3827ca42ef124c319b24907483aadda69b7453173f7807e0a94f33dd F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 3ee6cc29d2111e7ad90860827c0ea808fdf07bc71defdade7e6794ec4a2a3ce2 -R ef3119e103f71f255ed5e129f7bdb70b +P 0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe +R 2dabe33af17a981e8c8323ecd84b4487 U stephan -Z 99a7727c5c06597d0eb0c0df96988bd8 +Z c2d53a448e9f555de7ff26068d4eba40 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 48f7c632aa..97de8be89b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe \ No newline at end of file +dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20 \ No newline at end of file From 2c930e3b86028a108cab9008b75665d32cdd11c5 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 00:48:43 +0000 Subject: [PATCH 114/347] Add commit/rollback hook support to JNI wrapper1. FossilOrigin-Name: ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8 --- ext/jni/src/c/sqlite3-jni.c | 5 +- .../sqlite/jni/capi/CommitHookCallback.java | 3 +- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 3 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 101 ++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 81 ++++++++++++++ manifest | 20 ++-- manifest.uuid | 2 +- 7 files changed, 201 insertions(+), 14 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index bf4e73b7c3..0d60e49d13 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2942,7 +2942,10 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) : (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0); S3JniIfThrew{ - rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, "hook callback threw"); + rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, + isCommit + ? "Commit hook callback threw" + : "Rollback hook callback threw"); } S3JniHook_localundup(hook); } diff --git a/ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java b/ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java index 24373bdf2b..e1e55c78d2 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java @@ -19,7 +19,8 @@ package org.sqlite.jni.capi; public interface CommitHookCallback extends CallbackProxy { /** Works as documented for the C-level sqlite3_commit_hook() - callback. Must not throw. + callback. If it throws, the exception is translated into + a db-level error. */ int call(); } diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 48ab031466..4a8ceb1812 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1096,6 +1096,7 @@ public class Tester1 implements Runnable { private void testCommitHook(){ final sqlite3 db = createNewDb(); + sqlite3_extended_result_codes(db, true); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder hookResult = new ValueHolder<>(0); final CommitHookCallback theHook = new CommitHookCallback(){ @@ -1138,7 +1139,7 @@ public class Tester1 implements Runnable { affirm( 5 == counter.value ); hookResult.value = SQLITE_ERROR; int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;"); - affirm( SQLITE_CONSTRAINT == rc ); + affirm( SQLITE_CONSTRAINT_COMMITHOOK == rc ); affirm( 6 == counter.value ); sqlite3_close_v2(db); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index ffbc695242..dcd475ea68 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -1253,4 +1253,105 @@ public final class Sqlite implements AutoCloseable { } checkRc( CApi.sqlite3_busy_handler(thisDb(), bhc) ); } + + public interface CommitHook { + /** + Must behave as documented for the C-level sqlite3_commit_hook() + callback. If it throws, the exception is translated into + a db-level error. + */ + int call(); + } + + /** + A level of indirection to permit setCommitHook() to have similar + semantics as the C API, returning the previous hook. The caveat + is that if the low-level API is used to install a hook, it will + have a different hook type than Sqlite.CommitHook so + setCommitHook() will return null instead of that object. + */ + private static class CommitHookProxy + implements org.sqlite.jni.capi.CommitHookCallback { + final CommitHook commitHook; + CommitHookProxy(CommitHook ch){ + this.commitHook = ch; + } + @Override public int call(){ + return commitHook.call(); + } + } + + /** + Analog to sqlite3_commit_hook(). Returns the previous hook, if + any (else null). Throws if this db is closed. + + Minor caveat: if a commit hook is set on this object's underlying + db handle using the lower-level SQLite API, this function may + return null when replacing it, despite there being a hook, + because it will have a different callback type. So long as the + handle is only manipulated via the high-level API, this caveat + does not apply. + */ + CommitHook setCommitHook( CommitHook c ){ + CommitHookProxy chp = null; + if( null!=c ){ + chp = new CommitHookProxy(c); + } + final org.sqlite.jni.capi.CommitHookCallback rv = + CApi.sqlite3_commit_hook(thisDb(), chp); + return (rv instanceof CommitHookProxy) + ? ((CommitHookProxy)rv).commitHook + : null; + } + + + public interface RollbackHook { + /** + Must behave as documented for the C-level sqlite3_rollback_hook() + callback. If it throws, the exception is translated into + a db-level error. + */ + void call(); + } + + /** + A level of indirection to permit setRollbackHook() to have similar + semantics as the C API, returning the previous hook. The caveat + is that if the low-level API is used to install a hook, it will + have a different hook type than Sqlite.RollbackHook so + setRollbackHook() will return null instead of that object. + */ + private static class RollbackHookProxy + implements org.sqlite.jni.capi.RollbackHookCallback { + final RollbackHook rollbackHook; + RollbackHookProxy(RollbackHook ch){ + this.rollbackHook = ch; + } + @Override public void call(){rollbackHook.call();} + } + + /** + Analog to sqlite3_rollback_hook(). Returns the previous hook, if + any (else null). Throws if this db is closed. + + Minor caveat: if a rollback hook is set on this object's underlying + db handle using the lower-level SQLite API, this function may + return null when replacing it, despite there being a hook, + because it will have a different callback type. So long as the + handle is only manipulated via the high-level API, this caveat + does not apply. + */ + RollbackHook setRollbackHook( RollbackHook c ){ + RollbackHookProxy chp = null; + if( null!=c ){ + chp = new RollbackHookProxy(c); + } + final org.sqlite.jni.capi.RollbackHookCallback rv = + CApi.sqlite3_rollback_hook(thisDb(), chp); + return (rv instanceof RollbackHookProxy) + ? ((RollbackHookProxy)rv).rollbackHook + : null; + } + + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 3a00c0953f..56ea35ef1f 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -737,6 +737,87 @@ public class Tester2 implements Runnable { } } + private void testCommitHook(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + final ValueHolder hookResult = new ValueHolder<>(0); + final Sqlite.CommitHook theHook = new Sqlite.CommitHook(){ + @Override public int call(){ + ++counter.value; + return hookResult.value; + } + }; + Sqlite.CommitHook oldHook = db.setCommitHook(theHook); + affirm( null == oldHook ); + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + affirm( 2 == counter.value ); + execSql(db, "BEGIN; SELECT 1; SELECT 2; COMMIT;"); + affirm( 2 == counter.value /* NOT invoked if no changes are made */ ); + execSql(db, "BEGIN; update t set a='d' where a='c'; COMMIT;"); + affirm( 3 == counter.value ); + oldHook = db.setCommitHook(theHook); + affirm( theHook == oldHook ); + execSql(db, "BEGIN; update t set a='e' where a='d'; COMMIT;"); + affirm( 4 == counter.value ); + oldHook = db.setCommitHook(null); + affirm( theHook == oldHook ); + execSql(db, "BEGIN; update t set a='f' where a='e'; COMMIT;"); + affirm( 4 == counter.value ); + oldHook = db.setCommitHook(null); + affirm( null == oldHook ); + execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;"); + affirm( 4 == counter.value ); + + final Sqlite.CommitHook newHook = new Sqlite.CommitHook(){ + @Override public int call(){return 0;} + }; + oldHook = db.setCommitHook(newHook); + affirm( null == oldHook ); + execSql(db, "BEGIN; update t set a='h' where a='g'; COMMIT;"); + affirm( 4 == counter.value ); + oldHook = db.setCommitHook(theHook); + affirm( newHook == oldHook ); + execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;"); + affirm( 5 == counter.value ); + hookResult.value = CApi.SQLITE_ERROR; + int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;"); + affirm( CApi.SQLITE_CONSTRAINT_COMMITHOOK == rc ); + affirm( 6 == counter.value ); + db.close(); + } + + private void testRollbackHook(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + final Sqlite.RollbackHook theHook = new Sqlite.RollbackHook(){ + @Override public void call(){ + ++counter.value; + } + }; + Sqlite.RollbackHook oldHook = db.setRollbackHook(theHook); + affirm( null == oldHook ); + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + affirm( 0 == counter.value ); + execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;"); + affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ ); + + final Sqlite.RollbackHook newHook = new Sqlite.RollbackHook(){ + @Override public void call(){} + }; + oldHook = db.setRollbackHook(newHook); + affirm( theHook == oldHook ); + execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 1 == counter.value ); + oldHook = db.setRollbackHook(theHook); + affirm( newHook == oldHook ); + execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 2 == counter.value ); + int rc = execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 0 == rc ); + affirm( 3 == counter.value ); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 0cdcd1cfcd..68dcbace48 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sbusy-handler\ssupport\sto\sJNI\swrapper1. -D 2023-11-05T00:02:47.384 +C Add\scommit/rollback\shook\ssupport\sto\sJNI\swrapper1. +D 2023-11-05T00:48:43.424 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 931a7320f5b5745034b4fd61027ea7cc29559856e6da613e4fdcf01ef102e710 +F ext/jni/src/c/sqlite3-jni.c e24804e86759c0680064aacc46ab901d9b2b1a44eba312bcc9a387f15f044d12 F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/capi/CApi.java 4043d709626079cce6d524ef49122b934c04 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab -F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 29c002f3c638cc80f7db1594564a262d1beb32637824c3dca2d60a224d1f71d7 +F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 482f53dfec9e3ac2a9070d3fceebd56250932aaaf7c4f5bc8de29fc011416e0c F ext/jni/src/org/sqlite/jni/capi/ConfigLogCallback.java b995ca412f59b631803b93aa5b3684fce62e335d1e123207084c054abfd488d4 F ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java 701f2e4d8bdeb27cfbeeb56315d15b13d8752b0fdbca705f31bd4366c58d8a33 F ext/jni/src/org/sqlite/jni/capi/NativePointerHolder.java b7036dcb1ef1b39f1f36ac605dde0ff1a24a9a01ade6aa1a605039443e089a61 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4bb5e62907a422a80a0fccbcb83085e9163c2c245451312a62c7550a45d16683 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 909ebd23111762c878116ebacf73a848c8323fb46e8128eb3d99a85d48905444 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java c930ca964f605ba8f175d3b0c85099d7f93069b59bf825929c9eef9e68ac96c5 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 782bb185ffc629cdabbd624565a52ed9b4b1b2d773b8b1d84476d91cbf94827d F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 6f5fae3c3827ca42ef124c319b24907483aadda69b7453173f7807e0a94f33dd +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java ab1236dd65b4f90db729c88c71382b14aa179095f8b8e4b50835125bd0072f9e F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 0f673140681685ab390ecd7326a8b80d060b7ab23c31a2cfc28ba76fd5096afe -R 2dabe33af17a981e8c8323ecd84b4487 +P dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20 +R f0002b14be80bf0bcecc7fa19a1e63af U stephan -Z c2d53a448e9f555de7ff26068d4eba40 +Z 507dddf857173269602bfa9033ce1fa7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 97de8be89b..ba526bfc7b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20 \ No newline at end of file +ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8 \ No newline at end of file From 0ee7ae72a1c7993878bb46df377db6583e2c4ecc Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 01:14:07 +0000 Subject: [PATCH 115/347] Add update-hook support to JNI wrapper1. FossilOrigin-Name: 6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618 --- .../sqlite/jni/capi/RollbackHookCallback.java | 5 +- .../sqlite/jni/capi/UpdateHookCallback.java | 3 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 84 +++++++++++++++++++ .../src/org/sqlite/jni/wrapper1/Tester2.java | 56 ++++++++++++- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 153 insertions(+), 15 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java b/ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java index 5ce17e718a..cf9c4b6e7a 100644 --- a/ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java @@ -18,8 +18,9 @@ package org.sqlite.jni.capi; */ public interface RollbackHookCallback extends CallbackProxy { /** - Works as documented for the C-level sqlite3_rollback_hook() - callback. + Must function as documented for the C-level sqlite3_rollback_hook() + callback. If it throws, the exception is translated into + a db-level error. */ void call(); } diff --git a/ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java b/ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java index 33d72a5dd2..e3d491f67e 100644 --- a/ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java @@ -19,7 +19,8 @@ package org.sqlite.jni.capi; public interface UpdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_update_hook() - callback. + callback. If it throws, the exception is translated into + a db-level error. */ void call(int opId, String dbName, String tableName, long rowId); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index dcd475ea68..324f65ae29 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -112,6 +112,42 @@ public final class Sqlite implements AutoCloseable { public static final int BUSY = CApi.SQLITE_BUSY; public static final int LOCKED = CApi.SQLITE_LOCKED; + public static final int DENY = CApi.SQLITE_DENY; + public static final int IGNORE = CApi.SQLITE_IGNORE; + public static final int CREATE_INDEX = CApi.SQLITE_CREATE_INDEX; + public static final int CREATE_TABLE = CApi.SQLITE_CREATE_TABLE; + public static final int CREATE_TEMP_INDEX = CApi.SQLITE_CREATE_TEMP_INDEX; + public static final int CREATE_TEMP_TABLE = CApi.SQLITE_CREATE_TEMP_TABLE; + public static final int CREATE_TEMP_TRIGGER = CApi.SQLITE_CREATE_TEMP_TRIGGER; + public static final int CREATE_TEMP_VIEW = CApi.SQLITE_CREATE_TEMP_VIEW; + public static final int CREATE_TRIGGER = CApi.SQLITE_CREATE_TRIGGER; + public static final int CREATE_VIEW = CApi.SQLITE_CREATE_VIEW; + public static final int DELETE = CApi.SQLITE_DELETE; + public static final int DROP_INDEX = CApi.SQLITE_DROP_INDEX; + public static final int DROP_TABLE = CApi.SQLITE_DROP_TABLE; + public static final int DROP_TEMP_INDEX = CApi.SQLITE_DROP_TEMP_INDEX; + public static final int DROP_TEMP_TABLE = CApi.SQLITE_DROP_TEMP_TABLE; + public static final int DROP_TEMP_TRIGGER = CApi.SQLITE_DROP_TEMP_TRIGGER; + public static final int DROP_TEMP_VIEW = CApi.SQLITE_DROP_TEMP_VIEW; + public static final int DROP_TRIGGER = CApi.SQLITE_DROP_TRIGGER; + public static final int DROP_VIEW = CApi.SQLITE_DROP_VIEW; + public static final int INSERT = CApi.SQLITE_INSERT; + public static final int PRAGMA = CApi.SQLITE_PRAGMA; + public static final int READ = CApi.SQLITE_READ; + public static final int SELECT = CApi.SQLITE_SELECT; + public static final int TRANSACTION = CApi.SQLITE_TRANSACTION; + public static final int UPDATE = CApi.SQLITE_UPDATE; + public static final int ATTACH = CApi.SQLITE_ATTACH; + public static final int DETACH = CApi.SQLITE_DETACH; + public static final int ALTER_TABLE = CApi.SQLITE_ALTER_TABLE; + public static final int REINDEX = CApi.SQLITE_REINDEX; + public static final int ANALYZE = CApi.SQLITE_ANALYZE; + public static final int CREATE_VTABLE = CApi.SQLITE_CREATE_VTABLE; + public static final int DROP_VTABLE = CApi.SQLITE_DROP_VTABLE; + public static final int FUNCTION = CApi.SQLITE_FUNCTION; + public static final int SAVEPOINT = CApi.SQLITE_SAVEPOINT; + public static final int RECURSIVE = CApi.SQLITE_RECURSIVE; + //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ this.db = db; @@ -1353,5 +1389,53 @@ public final class Sqlite implements AutoCloseable { : null; } + public interface UpdateHook { + /** + Must function as described for the C-level sqlite3_update_hook() + callback. + */ + void call(int opId, String dbName, String tableName, long rowId); + } + + /** + A level of indirection to permit setUpdateHook() to have similar + semantics as the C API, returning the previous hook. The caveat + is that if the low-level API is used to install a hook, it will + have a different hook type than Sqlite.UpdateHook so + setUpdateHook() will return null instead of that object. + */ + private static class UpdateHookProxy + implements org.sqlite.jni.capi.UpdateHookCallback { + final UpdateHook updateHook; + UpdateHookProxy(UpdateHook ch){ + this.updateHook = ch; + } + @Override public void call(int opId, String dbName, String tableName, long rowId){ + updateHook.call(opId, dbName, tableName, rowId); + } + } + + /** + Analog to sqlite3_update_hook(). Returns the previous hook, if + any (else null). Throws if this db is closed. + + Minor caveat: if a update hook is set on this object's underlying + db handle using the lower-level SQLite API, this function may + return null when replacing it, despite there being a hook, + because it will have a different callback type. So long as the + handle is only manipulated via the high-level API, this caveat + does not apply. + */ + UpdateHook setUpdateHook( UpdateHook c ){ + UpdateHookProxy chp = null; + if( null!=c ){ + chp = new UpdateHookProxy(c); + } + final org.sqlite.jni.capi.UpdateHookCallback rv = + CApi.sqlite3_update_hook(thisDb(), chp); + return (rv instanceof UpdateHookProxy) + ? ((UpdateHookProxy)rv).updateHook + : null; + } } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 56ea35ef1f..fd8fb69b7a 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -133,6 +133,9 @@ public class Tester2 implements Runnable { Executes all SQL statements in the given string. If throwOnError is true then it will throw for any prepare/step errors, else it will return the corresponding non-0 result code. + + TODO: reimplement this in the high-level API once it has the + multi-prepare capability. */ public static int execSql(Sqlite dbw, boolean throwOnError, String sql){ final sqlite3 db = dbw.nativeHandle(); @@ -163,7 +166,7 @@ public class Tester2 implements Runnable { } CApi.sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer()); - if(CApi.SQLITE_DONE!=rc){ + if(Sqlite.DONE!=rc){ break; } } @@ -181,7 +184,7 @@ public class Tester2 implements Runnable { @SingleThreadOnly /* because it's thread-agnostic */ private void test1(){ - affirm(CApi.sqlite3_libversion_number() == CApi.SQLITE_VERSION_NUMBER); + affirm(Sqlite.libVersionNumber() == CApi.SQLITE_VERSION_NUMBER); } /* Copy/paste/rename this to add new tests. */ @@ -818,6 +821,55 @@ public class Tester2 implements Runnable { db.close(); } + private void testUpdateHook(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + final ValueHolder expectedOp = new ValueHolder<>(0); + final Sqlite.UpdateHook theHook = new Sqlite.UpdateHook(){ + @Override + public void call(int opId, String dbName, String tableName, long rowId){ + ++counter.value; + if( 0!=expectedOp.value ){ + affirm( expectedOp.value == opId ); + } + } + }; + Sqlite.UpdateHook oldHook = db.setUpdateHook(theHook); + affirm( null == oldHook ); + expectedOp.value = Sqlite.INSERT; + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + affirm( 3 == counter.value ); + expectedOp.value = Sqlite.UPDATE; + execSql(db, "update t set a='d' where a='c';"); + affirm( 4 == counter.value ); + oldHook = db.setUpdateHook(theHook); + affirm( theHook == oldHook ); + expectedOp.value = Sqlite.DELETE; + execSql(db, "DELETE FROM t where a='d'"); + affirm( 5 == counter.value ); + oldHook = db.setUpdateHook(null); + affirm( theHook == oldHook ); + execSql(db, "update t set a='e' where a='b';"); + affirm( 5 == counter.value ); + oldHook = db.setUpdateHook(null); + affirm( null == oldHook ); + + final Sqlite.UpdateHook newHook = new Sqlite.UpdateHook(){ + @Override public void call(int opId, String dbName, String tableName, long rowId){ + } + }; + oldHook = db.setUpdateHook(newHook); + affirm( null == oldHook ); + execSql(db, "update t set a='h' where a='a'"); + affirm( 5 == counter.value ); + oldHook = db.setUpdateHook(theHook); + affirm( newHook == oldHook ); + expectedOp.value = Sqlite.UPDATE; + execSql(db, "update t set a='i' where a='h'"); + affirm( 6 == counter.value ); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 68dcbace48..3ee436df87 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\scommit/rollback\shook\ssupport\sto\sJNI\swrapper1. -D 2023-11-05T00:48:43.424 +C Add\supdate-hook\ssupport\sto\sJNI\swrapper1. +D 2023-11-05T01:14:07.152 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -264,14 +264,14 @@ F ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java aca8f9fa72e3b6602bc9 F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17ca4b7ddde1d8cd6915b6ab7b708249a9787beca6bd6b6 F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f -F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java 105e324d09c207100485e7667ad172e64322c62426bb49b547e9b0dc9c33f5f0 +F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java e172210a2080e851ebb694c70e9f0bf89284237795e38710a7f5f1b61e3f6787 F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385155fa3b8011a5cca0bb3c28468c7131c1a5 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f F ext/jni/src/org/sqlite/jni/capi/Tester1.java 909ebd23111762c878116ebacf73a848c8323fb46e8128eb3d99a85d48905444 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 -F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4 +F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 782bb185ffc629cdabbd624565a52ed9b4b1b2d773b8b1d84476d91cbf94827d +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12f55248d500c0cf4148757adb442303831632edaa738a41d82708643ba42418 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java ab1236dd65b4f90db729c88c71382b14aa179095f8b8e4b50835125bd0072f9e +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 4cb3bebcd44f4289fa8075477583b6c508832288f7b18d6101d2ec23309ee4cf F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20 -R f0002b14be80bf0bcecc7fa19a1e63af +P ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8 +R 100ace4e28cdce95fe3bed6faf77a591 U stephan -Z 507dddf857173269602bfa9033ce1fa7 +Z dbd53c8956790cde47b0f616fa6677f5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ba526bfc7b..e37ae0477c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8 \ No newline at end of file +6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618 \ No newline at end of file From 557297ae771aeffb3dbc2ce2e2cdb69f13304003 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 5 Nov 2023 01:21:14 +0000 Subject: [PATCH 116/347] Setup, takedown, mode set and output working. No input yet. (WIP) FossilOrigin-Name: dfea85be1fb927ea446c9d98bae42ba1197bdab098aa6d95aa512a37d07a1e52 --- manifest | 16 ++- manifest.uuid | 2 +- src/console_io.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++ src/console_io.h | 59 +++++++---- 4 files changed, 307 insertions(+), 28 deletions(-) create mode 100755 src/console_io.c diff --git a/manifest b/manifest index ea6e754aed..914526c556 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Define\sinterface\sbetween\sproject\scommand-line\sapps\sand\sa\sconsole\sI/O\s"library". -D 2023-11-04T02:22:04.015 +C Setup,\stakedown,\smode\sset\sand\soutput\sworking.\sNo\sinput\syet.\s(WIP) +D 2023-11-05T01:21:14.743 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,7 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.h 14057195f16cace1f669f723510190f90300ee6c1ef0a6f417a54344bc57bdd0 +F src/console_io.c 484a4ad56c18ddc62b845494f8ac6e06df9f9b0bc2fa60ac2dcba74635bdf8da x +F src/console_io.h 6b7c86a5778445e507a089b9808803e4523e2989f33c04692e008eae3f0f4d8a F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -2143,11 +2144,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 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913 -R 44e2680e2cc755f86742bfd0211316ba -T *branch * console-io-lib -T *sym-console-io-lib * -T -sym-trunk * +P 64abef8314b8544fdc7b71317d61a4641dc607a1ae42b8ff21543226fd338ba2 +R 9aec452b903f28bc0cbf638b7c11370e U larrybr -Z e3b2358cc83000c61d02936c7cb34f1b +Z 37ecbe5d111f6f842b9b1e97c1e0e0d1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 66b2569455..5e27b835d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64abef8314b8544fdc7b71317d61a4641dc607a1ae42b8ff21543226fd338ba2 \ No newline at end of file +dfea85be1fb927ea446c9d98bae42ba1197bdab098aa6d95aa512a37d07a1e52 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c new file mode 100755 index 0000000000..8c4db84396 --- /dev/null +++ b/src/console_io.c @@ -0,0 +1,258 @@ +/* +** 2023 November 4 +** +** 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 various interfaces used for console I/O by the +** SQLite project command-line tools, as explained in console_io.h . +*/ + +#ifndef SQLITE_CDECL +# define SQLITE_CDECL +#endif + +#include +#include +#include "console_io.h" +#include "sqlite3.h" + +#if defined(_WIN32) || defined(WIN32) +# include +# include +# ifdef SHELL_LEGACY_CONSOLE_IO +# define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */ +# else +# define SHELL_CON_TRANSLATE 1 /* Use wchar APIs for console I/O */ +# endif +#else +# include +# define SHELL_CON_TRANSLATE 0 /* Use plain C library stream I/O at console */ +#endif + +#if SHELL_CON_TRANSLATE +static HANDLE handleOfFile(FILE *pf){ + int fileDesc = _fileno(pf); + union { intptr_t osfh; HANDLE fh; } fid = { + (fileDesc!=-2)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE + }; + return fid.fh; +} +#endif + +static short fileOfConsole(FILE *pf){ +#if SHELL_CON_TRANSLATE + DWORD dwj; + HANDLE fh = handleOfFile(pf); + if( INVALID_HANDLE_VALUE != fh ){ + return (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwj)); + }else return 0; +#else + return (short)isatty(fileno(pf)); +#endif +} + +#define SHELL_INVALID_FILE_PTR ((FILE *)sizeof(FILE*)) + +typedef struct ConsoleInfo { + /* int iDefaultFmode; */ + ConsoleStdConsStreams cscs; +#if SHELL_CON_TRANSLATE + HANDLE hIn; HANDLE hOut; HANDLE hErr; + HANDLE hLowest; +#endif + FILE *pfIn; FILE *pfOut; FILE *pfErr; +} ConsoleInfo; + +static ConsoleInfo consoleInfo = { + /* 0, iDefaultFmode */ + CSCS_NoConsole, +#if SHELL_CON_TRANSLATE + INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, +#endif + SHELL_INVALID_FILE_PTR, SHELL_INVALID_FILE_PTR, SHELL_INVALID_FILE_PTR +}; +#undef SHELL_INVALID_FILE_PTR + +#if SHELL_CON_TRANSLATE == 1 +# define SHELL_CON_MODE_CSZ _O_U16TEXT +#elif SHELL_CON_TRANSLATE == 2 +# define SHELL_CON_MODE_CSZ _O_U8TEXT +#endif + +INT_LINKAGE ConsoleStdConsStreams +consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ + ConsoleStdConsStreams rv = CSCS_NoConsole; + if( fileOfConsole(pfErr) ){ + rv |= CSCS_ErrConsole; + consoleInfo.pfErr = pfErr; +#if SHELL_CON_TRANSLATE + fflush(pfErr); +# if SHELL_CON_TRANSLATE == 1 + _setmode(_fileno(pfErr), _O_U16TEXT); + _setmode(_fileno(pfErr), _O_BINARY); +# elif SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(pfErr), _O_U8TEXT); + _setmode(_fileno(pfErr), _O_TEXT); +# endif + consoleInfo.hLowest = consoleInfo.hErr = handleOfFile(pfErr); +#endif + } + if( fileOfConsole(pfOut) ){ + rv |= CSCS_OutConsole; + consoleInfo.pfOut = pfOut; +#if SHELL_CON_TRANSLATE + fflush(pfOut); +# if SHELL_CON_TRANSLATE == 1 + _setmode(_fileno(pfOut), _O_U16TEXT); + _setmode(_fileno(pfOut), _O_BINARY); +# elif SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(pfOut), _O_U8TEXT); + _setmode(_fileno(pfOut), _O_TEXT); +# endif + consoleInfo.hLowest = consoleInfo.hOut = handleOfFile(pfOut); +#endif + } + if( fileOfConsole(pfIn) ){ + rv |= CSCS_InConsole; + consoleInfo.pfIn = pfIn; +#if SHELL_CON_TRANSLATE == 1 + _setmode(_fileno(pfIn), _O_U16TEXT); + _setmode(_fileno(pfIn), _O_BINARY); + consoleInfo.hLowest = consoleInfo.hIn = handleOfFile(pfIn); +#elif SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(pfIn), _O_U8TEXT); + _setmode(_fileno(pfIn), _O_TEXT); + consoleInfo.hLowest = consoleInfo.hIn = handleOfFile(pfIn); +#endif + } + consoleInfo.cscs = rv; + return rv; +} + +INT_LINKAGE void SQLITE_CDECL consoleRestore( void ){ +#if SHELL_CON_TRANSLATE + if( consoleInfo.cscs ){ + /* ToDo: Read these modes in consoleClassifySetup somehow. + ** A _get_fmode() call almost works. But not with gcc, yet. + ** This has to be done to make the CLI a callable function. + */ + int tmode = _O_TEXT, xmode = _O_U8TEXT; + if( consoleInfo.cscs & CSCS_InConsole ){ + _setmode(_fileno(consoleInfo.pfIn), tmode); + _setmode(_fileno(consoleInfo.pfIn), xmode); + } + if( consoleInfo.cscs & CSCS_OutConsole ){ + _setmode(_fileno(consoleInfo.pfOut), tmode); + _setmode(_fileno(consoleInfo.pfOut), xmode); + } + if( consoleInfo.cscs & CSCS_ErrConsole ){ + _setmode(_fileno(consoleInfo.pfErr), tmode); + _setmode(_fileno(consoleInfo.pfErr), xmode); + } + } +#endif +#ifdef TEST_CIO +#endif +} + +static short isConOut(FILE *pf){ + if( pf==consoleInfo.pfOut ) return 1; + else if( pf==consoleInfo.pfErr ) return 2; + else return 0; +} + +INT_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ + short ico = isConOut(pf); + if( ico || bFlush ) fflush(pf); +#if SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(pf), _O_BINARY); +#elif SHELL_CON_TRANSLATE == 1 + /* Never change between text/binary on UTF-16 console streamss. */ + if( !ico && !(consoleInfo.pfIn==pf)) _setmode(_fileno(pf), _O_BINARY); +#endif +} +INT_LINKAGE void setTextMode(FILE *pf, short bFlush){ + short ico = isConOut(pf); + if( ico || bFlush ) fflush(pf); +#if SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(pf), _O_TEXT); +#elif SHELL_CON_TRANSLATE == 1 + /* Never change between text/binary on UTF-16 console streamss. */ + if( !ico && !(consoleInfo.pfIn==pf)) _setmode(_fileno(pf), _O_TEXT); +#endif +} +/* Later: Factor common code out of above 2 procs. */ + +INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ + va_list ap; + int rv = 0; + short on = isConOut(pfO); + va_start(ap, zFormat); + if( on > 0 ){ +#if SHELL_CON_TRANSLATE + char *z1 = sqlite3_vmprintf(zFormat, ap); +# if SHELL_CON_TRANSLATE == 2 + /* Legacy translation to active code page, then MBCS chars out. */ + char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); + if( z2!=NULL ){ + rv = strlen(z2); + vfprintf(pfO, "%s", z2); + sqlite3_free(z2); + } +# else + /* Translation from UTF-8 to UTF-16, then WCHAR characters out. */ + if( z1!=NULL ){ + int nwc; + WCHAR *zw2 = 0; + rv = strlen(z1); + nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,0,0); + if( nwc>0 ){ + zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); + if( zw2!=NULL ){ + HANDLE ho = (on==1)? consoleInfo.hOut : consoleInfo.hErr; + nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,zw2,nwc); + zw2[nwc] = 0; + WriteConsoleW(ho, zw2, nwc, 0, NULL); + sqlite3_free(zw2); + }else rv = 0; + } + } +# endif + sqlite3_free(z1); +#else +#endif + }else{ + rv = vfprintf(pfO, zFormat, ap); + } + va_end(ap); + return rv; +} + +INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn){ + return 0; +} + +#ifdef TEST_CIO +// cl -Zi -I. -DWIN32 -DTEST_CIO sqlite3.c src/console_io.c -Fecio.exe +// gcc -I. -DWIN32 -DTEST_CIO -o cio sqlite3.c src/console_io.c -o cio.exe +const char *prompts[] = { "main", "cont" }; +Prompts goofy = { 2, prompts }; + +int main(int na, char *av[]){ + ConsoleStdConsStreams cc = consoleClassifySetup(stdin, stdout, stderr); + setTextMode(stdout, 1); + setTextMode(stderr, 1); + fprintfUtf8(stderr, "%d\n", cc); + fprintfUtf8(stdout, "%s=%d\n", "∑(1st 7 primes)", 42); + fprintfUtf8(stderr, "%s\n", "∫ (1/x) dx ≡ ln(x)"); + consoleRestore(); + return 0; +} +#endif /* defined(TEST_CIO) */ diff --git a/src/console_io.h b/src/console_io.h index b8c1ea982d..444c3e0e65 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -24,19 +24,33 @@ ** This code may change in tandem with other project code as needed. */ +#ifndef INT_LINKAGE +# define INT_LINKAGE /* Linkage will be external to translation unit. */ +# include +# include +# include +# if defined(_WIN32) || defined(WIN32) +# undef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# endif +#endif + /* Define enum for use with following function. */ -enum ConsoleStdStreams { - CSC_NoConsole = 0, - CSC_InConsole = 1, CSC_OutConsole = 2, CSC_ErrConsole = 4, - CSC_AnyConsole = 0x7 -}; +typedef enum ConsoleStdConsStreams { + CSCS_NoConsole = 0, + CSCS_InConsole = 1, CSCS_OutConsole = 2, CSCS_ErrConsole = 4, + CSCS_AnyConsole = 0x7 +} ConsoleStdConsStreams; /* ** Classify the three standard I/O streams according to whether ** they are connected to a console attached to the process. ** -** Returns the bit-wise OR of CSC_{In,Out,Err}Console values, -** or CSC_NoConsole if none of the streams reaches a console. +** Returns the bit-wise OR of CSCS_{In,Out,Err}Console values, +** or CSCS_NoConsole if none of the streams reaches a console. ** ** This function should be called before any I/O is done with ** the given streams. As a side-effect, the given inputs are @@ -47,8 +61,8 @@ enum ConsoleStdStreams { ** On some platforms, stream or console mode alteration (aka ** "Setup") may be made which is undone by consoleRestore(). */ -INT_LINKAGE ConsoleStdStreams -consoleClassifySetup( FILE *pfIn, FILE *pfOut,FILE *pfErr ); +INT_LINKAGE ConsoleStdConsStreams +consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); /* ** Undo any side-effects left by consoleClassifySetup(...). @@ -64,10 +78,10 @@ INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); ** Render output like fprintf(). If the output is going to the ** console and translation from UTF-8 is necessary, perform ** the needed translation. Otherwise, write formatted output -** to the provided stream almost as-is, with possibly with -** newline translation as set by set{Binary,Text}Mode(). +** to the provided stream almost as-is, possibly with newline +** translation as specified by set{Binary,Text}Mode(). */ -INT_LINKAGE int fprintfUtf8(FILE *, const char *zFmt, ...); +INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); /* ** Collect input like fgets(...) with special provisions for input @@ -79,14 +93,23 @@ INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn); /* ** Set given stream for binary mode, where newline translation is -** not done, or to text mode where, for some platforms, newlines +** not done, or for text mode where, for some platforms, newlines ** are translated to the platform's conventional char sequence. +** If bFlush true, flush the stream. ** ** An additional side-effect is that if the stream is one passed -** to consoleClassifySetup() as an output, it is flushed. +** to consoleClassifySetup() as an output, it is flushed first. +** +** Note that binary/text mode has no effect on console output +** translation. Newline chars start a new line on all platforms. */ -INT_LINKAGE void setBinaryMode(File *); -INT_LINKAGE void setTextMode(File *); +INT_LINKAGE void setBinaryMode(FILE *, short bFlush); +INT_LINKAGE void setTextMode(FILE *, short bFlush); + +typedef struct Prompts { + int numPrompts; + const char **azPrompts; +} Prompts; /* ** Macros for use of a line editor. @@ -128,8 +151,8 @@ INT_LINKAGE void setTextMode(File *); ** library to interactively collect line edited input. */ INT_LINKAGE char * -shellGetLine(File *pfIn, char *zBufPrior, int nLen, - short isContinuation, const char *azPrompt[2]); +shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, + short isContinuation, Prompts azPrompt); /* ** TBD: Define an interface for application(s) to generate From 9a265899409da5ecc374f97fc3cd67706da69c4b Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 01:39:29 +0000 Subject: [PATCH 117/347] Add progress-handler support to JNI wrapper1. Correct the return type of the extended_result_codes() JNI binding and expose it to wrapper1. FossilOrigin-Name: 6c0acfdce2160d8db261a59677cec571b6abc333481525b1ec975d98e88bec88 --- ext/jni/src/c/sqlite3-jni.c | 8 +- ext/jni/src/c/sqlite3-jni.h | 4 +- ext/jni/src/org/sqlite/jni/capi/CApi.java | 2 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 162 +++++++++++------- .../sqlite/jni/wrapper1/SqliteException.java | 2 +- .../src/org/sqlite/jni/wrapper1/Tester2.java | 18 ++ manifest | 22 +-- manifest.uuid | 2 +- 8 files changed, 142 insertions(+), 78 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 0d60e49d13..c530651cd7 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -3596,12 +3596,14 @@ S3JniApi(sqlite3_normalized_sql(),jstring,1normalized_1sql)( #endif } -S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)( +S3JniApi(sqlite3_extended_result_codes(),jint,1extended_1result_1codes)( JniArgsEnvClass, jobject jpDb, jboolean onoff ){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - int const rc = pDb ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) : 0; - return rc ? JNI_TRUE : JNI_FALSE; + int const rc = pDb + ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) + : SQLITE_MISUSE; + return rc; } S3JniApi(sqlite3_finalize(),jint,1finalize)( diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index e4393ddd8b..a1097fe57a 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1402,9 +1402,9 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1errcode /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_extended_result_codes - * Signature: (Lorg/sqlite/jni/capi/sqlite3;Z)Z + * Signature: (Lorg/sqlite/jni/capi/sqlite3;Z)I */ -JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1result_1codes +JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1result_1codes (JNIEnv *, jclass, jobject, jboolean); /* diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 79568e74ad..f1d4def500 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -890,7 +890,7 @@ public final class CApi { return sqlite3_extended_errcode(db.getNativePointer()); } - public static native boolean sqlite3_extended_result_codes( + public static native int sqlite3_extended_result_codes( @NotNull sqlite3 db, boolean on ); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 324f65ae29..d9b5a17c20 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -213,61 +213,6 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_sourceid(); } - - /** - Output object for use with status() and libStatus(). - */ - public static final class Status { - /** The current value for the requested status() or libStatus() metric. */ - long current; - /** The peak value for the requested status() or libStatus() metric. */ - long peak; - }; - - /** - As per sqlite3_status64(), but returns its current and high-water - results as a Status object. Throws if the first argument is - not one of the STATUS_... constants. - */ - public static Status libStatus(int op, boolean resetStats){ - org.sqlite.jni.capi.OutputPointer.Int64 pCurrent = - new org.sqlite.jni.capi.OutputPointer.Int64(); - org.sqlite.jni.capi.OutputPointer.Int64 pHighwater = - new org.sqlite.jni.capi.OutputPointer.Int64(); - checkRc2( CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats) ); - final Status s = new Status(); - s.current = pCurrent.value; - s.peak = pHighwater.value; - return s; - } - - /** - As per sqlite3_status64(), but returns its current and high-water - results as a Status object. Throws if the first argument is - not one of the DBSTATUS_... constants or on any other misuse. - */ - public Status status(int op, boolean resetStats){ - org.sqlite.jni.capi.OutputPointer.Int32 pCurrent = - new org.sqlite.jni.capi.OutputPointer.Int32(); - org.sqlite.jni.capi.OutputPointer.Int32 pHighwater = - new org.sqlite.jni.capi.OutputPointer.Int32(); - checkRc( CApi.sqlite3_db_status(thisDb(), op, pCurrent, pHighwater, resetStats) ); - final Status s = new Status(); - s.current = pCurrent.value; - s.peak = pHighwater.value; - return s; - } - - @Override public void close(){ - if(null!=this.db){ - synchronized(nativeToWrapper){ - nativeToWrapper.remove(this.db); - } - this.db.close(); - this.db = null; - } - } - /** Returns the value of the native library's build-time value of the SQLITE_THREADSAFE build option. @@ -325,6 +270,61 @@ public final class Sqlite implements AutoCloseable { return 0==CApi.sqlite3_strlike(glob, txt, escChar); } + /** + Output object for use with status() and libStatus(). + */ + public static final class Status { + /** The current value for the requested status() or libStatus() metric. */ + long current; + /** The peak value for the requested status() or libStatus() metric. */ + long peak; + }; + + /** + As per sqlite3_status64(), but returns its current and high-water + results as a Status object. Throws if the first argument is + not one of the STATUS_... constants. + */ + public static Status libStatus(int op, boolean resetStats){ + org.sqlite.jni.capi.OutputPointer.Int64 pCurrent = + new org.sqlite.jni.capi.OutputPointer.Int64(); + org.sqlite.jni.capi.OutputPointer.Int64 pHighwater = + new org.sqlite.jni.capi.OutputPointer.Int64(); + checkRc2( CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats) ); + final Status s = new Status(); + s.current = pCurrent.value; + s.peak = pHighwater.value; + return s; + } + + /** + As per sqlite3_db_status(), but returns its current and + high-water results as a Status object. Throws if the first + argument is not one of the DBSTATUS_... constants or on any other + misuse. + */ + public Status status(int op, boolean resetStats){ + org.sqlite.jni.capi.OutputPointer.Int32 pCurrent = + new org.sqlite.jni.capi.OutputPointer.Int32(); + org.sqlite.jni.capi.OutputPointer.Int32 pHighwater = + new org.sqlite.jni.capi.OutputPointer.Int32(); + checkRc( CApi.sqlite3_db_status(thisDb(), op, pCurrent, pHighwater, resetStats) ); + final Status s = new Status(); + s.current = pCurrent.value; + s.peak = pHighwater.value; + return s; + } + + @Override public void close(){ + if(null!=this.db){ + synchronized(nativeToWrapper){ + nativeToWrapper.remove(this.db); + } + this.db.close(); + this.db = null; + } + } + /** Returns this object's underlying native db handle, or null if this instance has been closed. This is very specifically not @@ -379,6 +379,19 @@ public final class Sqlite implements AutoCloseable { } } + /** + Toggles the use of extended result codes on or off. By default + they are turned off, but they can be enabled by default by + including the OPEN_EXRESCODE flag when opening a database. + + Because this API reports db-side errors using exceptions, + enabling this may change the values returned by + SqliteException.errcode(). + */ + public void useExtendedResultCodes(boolean on){ + checkRc( CApi.sqlite3_extended_result_codes(thisDb(), on) ); + } + /** prepFlags must be 0 or a bitmask of the PREPARE_... constants. @@ -1278,7 +1291,7 @@ public final class Sqlite implements AutoCloseable { Analog to sqlite3_busy_handler(). If b is null then any current handler is cleared. */ - void setBusyHandler( BusyHandler b ){ + public void setBusyHandler( BusyHandler b ){ org.sqlite.jni.capi.BusyHandlerCallback bhc = null; if( null!=b ){ bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){ @@ -1328,7 +1341,7 @@ public final class Sqlite implements AutoCloseable { handle is only manipulated via the high-level API, this caveat does not apply. */ - CommitHook setCommitHook( CommitHook c ){ + public CommitHook setCommitHook( CommitHook c ){ CommitHookProxy chp = null; if( null!=c ){ chp = new CommitHookProxy(c); @@ -1377,7 +1390,7 @@ public final class Sqlite implements AutoCloseable { handle is only manipulated via the high-level API, this caveat does not apply. */ - RollbackHook setRollbackHook( RollbackHook c ){ + public RollbackHook setRollbackHook( RollbackHook c ){ RollbackHookProxy chp = null; if( null!=c ){ chp = new RollbackHookProxy(c); @@ -1426,7 +1439,7 @@ public final class Sqlite implements AutoCloseable { handle is only manipulated via the high-level API, this caveat does not apply. */ - UpdateHook setUpdateHook( UpdateHook c ){ + public UpdateHook setUpdateHook( UpdateHook c ){ UpdateHookProxy chp = null; if( null!=c ){ chp = new UpdateHookProxy(c); @@ -1438,4 +1451,35 @@ public final class Sqlite implements AutoCloseable { : null; } + + /** + Callback interface for use with setProgressHandler(). + */ + public interface ProgressHandler { + /** + Must behave as documented for the C-level sqlite3_progress_handler() + callback. If it throws, the exception is translated into + a db-level error. + */ + int call(); + } + + /** + Analog to sqlite3_progress_handler(), sets the current progress + handler or clears it if p is null. + + Note that this API, in contrast to setUpdateHook(), + setRollbackHook(), and setCommitHook(), cannot return the + previous handler. That inconsistency is part of the lower-level C + API. + */ + public void setProgressHandler( int n, ProgressHandler p ){ + org.sqlite.jni.capi.ProgressHandlerCallback phc = null; + if( null!=p ){ + phc = new org.sqlite.jni.capi.ProgressHandlerCallback(){ + @Override public int call(){ return p.call(); } + }; + } + CApi.sqlite3_progress_handler( thisDb(), n, phc ); + } } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java index 09fa02a2ca..9b4440f190 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java @@ -50,7 +50,7 @@ public final class SqliteException extends java.lang.RuntimeException { /** Records the current error state of db (which must not be null and - must refer to an opened db object). Note that this does NOT close + must refer to an opened db object). Note that this does not close the db. Design note: closing the db on error is really only useful during diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index fd8fb69b7a..1af2c8c5d1 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -870,6 +870,24 @@ public class Tester2 implements Runnable { db.close(); } + private void testProgress(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + db.setProgressHandler(1, new Sqlite.ProgressHandler(){ + @Override public int call(){ + ++counter.value; + return 0; + } + }); + execSql(db, "SELECT 1; SELECT 2;"); + affirm( counter.value > 0 ); + int nOld = counter.value; + db.setProgressHandler(0, null); + execSql(db, "SELECT 1; SELECT 2;"); + affirm( nOld == counter.value ); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 3ee436df87..82936f69b9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\supdate-hook\ssupport\sto\sJNI\swrapper1. -D 2023-11-05T01:14:07.152 +C Add\sprogress-handler\ssupport\sto\sJNI\swrapper1.\sCorrect\sthe\sreturn\stype\sof\sthe\sextended_result_codes()\sJNI\sbinding\sand\sexpose\sit\sto\swrapper1. +D 2023-11-05T01:39:29.450 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,8 +241,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c e24804e86759c0680064aacc46ab901d9b2b1a44eba312bcc9a387f15f044d12 -F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015 +F ext/jni/src/c/sqlite3-jni.c b98d822a35ef4438023c3c14816b5ac17bdbd23bc838ff80b6463c3146a75d14 +F ext/jni/src/c/sqlite3-jni.h 03f66d3b43359dacd7c2c9407d78274dbdb027bc7610985011bf2b462222414e F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 4043d709626079cce6d524ef49122b934c043022bd88bc1e72eb697ac8df86e7 +F ext/jni/src/org/sqlite/jni/capi/CApi.java a6d4fdd35e4fcfe95c61e343eb389cfaab3d5166e9670730aef14240a982b97b F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12f55248d500c0cf4148757adb442303831632edaa738a41d82708643ba42418 -F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 4cb3bebcd44f4289fa8075477583b6c508832288f7b18d6101d2ec23309ee4cf +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 923c885609ca9e6793636fc17787d0058c80ce1d7ba5a69eb40e68cc299892d1 +F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 822cf3d66beb1efcac1792c8652cbebb80ae998275a33337de46fde1670e0008 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8 -R 100ace4e28cdce95fe3bed6faf77a591 +P 6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618 +R dafa52fdba2c999e04b851e5ec6214ab U stephan -Z dbd53c8956790cde47b0f616fa6677f5 +Z 824d9a7ed24ad4091f85bc7e5264c033 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e37ae0477c..7f82e1d85b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618 \ No newline at end of file +6c0acfdce2160d8db261a59677cec571b6abc333481525b1ec975d98e88bec88 \ No newline at end of file From 88c90a793ef80560b90a37802c06135c5bef3617 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 01:55:20 +0000 Subject: [PATCH 118/347] Add authorizer support to JNI wrapper1. FossilOrigin-Name: 773f9873865b5277a6a682c4695f216bfe1ec05ed5e5a2a70aaa451934ba2dc0 --- .../sqlite/jni/capi/AuthorizerCallback.java | 3 +- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 3 + .../src/org/sqlite/jni/wrapper1/Sqlite.java | 139 +++++++++++++++++- .../src/org/sqlite/jni/wrapper1/Tester2.java | 24 +++ manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 174 insertions(+), 15 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java b/ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java index ce7c6fca6d..298e3a5900 100644 --- a/ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java @@ -20,7 +20,8 @@ import org.sqlite.jni.annotation.*; public interface AuthorizerCallback extends CallbackProxy { /** Must function as described for the C-level - sqlite3_set_authorizer() callback. + sqlite3_set_authorizer() callback. If it throws, the error is + converted to a db-level error and the exception is suppressed. */ int call(int opId, @Nullable String s1, @Nullable String s2, @Nullable String s3, @Nullable String s4); diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 4a8ceb1812..fb53196d26 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1358,6 +1358,9 @@ public class Tester1 implements Runnable { authRc.value = SQLITE_DENY; int rc = execSql(db, false, "UPDATE t SET a=2"); affirm( SQLITE_AUTH==rc ); + sqlite3_set_authorizer(db, null); + rc = execSql(db, false, "UPDATE t SET a=2"); + affirm( 0==rc ); // TODO: expand these tests considerably sqlite3_close(db); } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index d9b5a17c20..cf18277329 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -29,6 +29,111 @@ import org.sqlite.jni.capi.OutputPointer; public final class Sqlite implements AutoCloseable { private sqlite3 db; + public static final int OK = CApi.SQLITE_OK; + public static final int ERROR = CApi.SQLITE_ERROR; + public static final int INTERNAL = CApi.SQLITE_INTERNAL; + public static final int PERM = CApi.SQLITE_PERM; + public static final int ABORT = CApi.SQLITE_ABORT; + public static final int BUSY = CApi.SQLITE_BUSY; + public static final int LOCKED = CApi.SQLITE_LOCKED; + public static final int NOMEM = CApi.SQLITE_NOMEM; + public static final int READONLY = CApi.SQLITE_READONLY; + public static final int INTERRUPT = CApi.SQLITE_INTERRUPT; + public static final int IOERR = CApi.SQLITE_IOERR; + public static final int CORRUPT = CApi.SQLITE_CORRUPT; + public static final int NOTFOUND = CApi.SQLITE_NOTFOUND; + public static final int FULL = CApi.SQLITE_FULL; + public static final int CANTOPEN = CApi.SQLITE_CANTOPEN; + public static final int PROTOCOL = CApi.SQLITE_PROTOCOL; + public static final int EMPTY = CApi.SQLITE_EMPTY; + public static final int SCHEMA = CApi.SQLITE_SCHEMA; + public static final int TOOBIG = CApi.SQLITE_TOOBIG; + public static final int CONSTRAINT = CApi. SQLITE_CONSTRAINT; + public static final int MISMATCH = CApi.SQLITE_MISMATCH; + public static final int MISUSE = CApi.SQLITE_MISUSE; + public static final int NOLFS = CApi.SQLITE_NOLFS; + public static final int AUTH = CApi.SQLITE_AUTH; + public static final int FORMAT = CApi.SQLITE_FORMAT; + public static final int RANGE = CApi.SQLITE_RANGE; + public static final int NOTADB = CApi.SQLITE_NOTADB; + public static final int NOTICE = CApi.SQLITE_NOTICE; + public static final int WARNING = CApi.SQLITE_WARNING; + public static final int ROW = CApi.SQLITE_ROW; + public static final int DONE = CApi.SQLITE_DONE; + public static final int ERROR_MISSING_COLLSEQ = CApi.SQLITE_ERROR_MISSING_COLLSEQ; + public static final int ERROR_RETRY = CApi.SQLITE_ERROR_RETRY; + public static final int ERROR_SNAPSHOT = CApi.SQLITE_ERROR_SNAPSHOT; + public static final int IOERR_READ = CApi.SQLITE_IOERR_READ; + public static final int IOERR_SHORT_READ = CApi.SQLITE_IOERR_SHORT_READ; + public static final int IOERR_WRITE = CApi.SQLITE_IOERR_WRITE; + public static final int IOERR_FSYNC = CApi.SQLITE_IOERR_FSYNC; + public static final int IOERR_DIR_FSYNC = CApi.SQLITE_IOERR_DIR_FSYNC; + public static final int IOERR_TRUNCATE = CApi.SQLITE_IOERR_TRUNCATE; + public static final int IOERR_FSTAT = CApi.SQLITE_IOERR_FSTAT; + public static final int IOERR_UNLOCK = CApi.SQLITE_IOERR_UNLOCK; + public static final int IOERR_RDLOCK = CApi.SQLITE_IOERR_RDLOCK; + public static final int IOERR_DELETE = CApi.SQLITE_IOERR_DELETE; + public static final int IOERR_BLOCKED = CApi.SQLITE_IOERR_BLOCKED; + public static final int IOERR_NOMEM = CApi.SQLITE_IOERR_NOMEM; + public static final int IOERR_ACCESS = CApi.SQLITE_IOERR_ACCESS; + public static final int IOERR_CHECKRESERVEDLOCK = CApi.SQLITE_IOERR_CHECKRESERVEDLOCK; + public static final int IOERR_LOCK = CApi.SQLITE_IOERR_LOCK; + public static final int IOERR_CLOSE = CApi.SQLITE_IOERR_CLOSE; + public static final int IOERR_DIR_CLOSE = CApi.SQLITE_IOERR_DIR_CLOSE; + public static final int IOERR_SHMOPEN = CApi.SQLITE_IOERR_SHMOPEN; + public static final int IOERR_SHMSIZE = CApi.SQLITE_IOERR_SHMSIZE; + public static final int IOERR_SHMLOCK = CApi.SQLITE_IOERR_SHMLOCK; + public static final int IOERR_SHMMAP = CApi.SQLITE_IOERR_SHMMAP; + public static final int IOERR_SEEK = CApi.SQLITE_IOERR_SEEK; + public static final int IOERR_DELETE_NOENT = CApi.SQLITE_IOERR_DELETE_NOENT; + public static final int IOERR_MMAP = CApi.SQLITE_IOERR_MMAP; + public static final int IOERR_GETTEMPPATH = CApi.SQLITE_IOERR_GETTEMPPATH; + public static final int IOERR_CONVPATH = CApi.SQLITE_IOERR_CONVPATH; + public static final int IOERR_VNODE = CApi.SQLITE_IOERR_VNODE; + public static final int IOERR_AUTH = CApi.SQLITE_IOERR_AUTH; + public static final int IOERR_BEGIN_ATOMIC = CApi.SQLITE_IOERR_BEGIN_ATOMIC; + public static final int IOERR_COMMIT_ATOMIC = CApi.SQLITE_IOERR_COMMIT_ATOMIC; + public static final int IOERR_ROLLBACK_ATOMIC = CApi.SQLITE_IOERR_ROLLBACK_ATOMIC; + public static final int IOERR_DATA = CApi.SQLITE_IOERR_DATA; + public static final int IOERR_CORRUPTFS = CApi.SQLITE_IOERR_CORRUPTFS; + public static final int LOCKED_SHAREDCACHE = CApi.SQLITE_LOCKED_SHAREDCACHE; + public static final int LOCKED_VTAB = CApi.SQLITE_LOCKED_VTAB; + public static final int BUSY_RECOVERY = CApi.SQLITE_BUSY_RECOVERY; + public static final int BUSY_SNAPSHOT = CApi.SQLITE_BUSY_SNAPSHOT; + public static final int BUSY_TIMEOUT = CApi.SQLITE_BUSY_TIMEOUT; + public static final int CANTOPEN_NOTEMPDIR = CApi.SQLITE_CANTOPEN_NOTEMPDIR; + public static final int CANTOPEN_ISDIR = CApi.SQLITE_CANTOPEN_ISDIR; + public static final int CANTOPEN_FULLPATH = CApi.SQLITE_CANTOPEN_FULLPATH; + public static final int CANTOPEN_CONVPATH = CApi.SQLITE_CANTOPEN_CONVPATH; + public static final int CANTOPEN_SYMLINK = CApi.SQLITE_CANTOPEN_SYMLINK; + public static final int CORRUPT_VTAB = CApi.SQLITE_CORRUPT_VTAB; + public static final int CORRUPT_SEQUENCE = CApi.SQLITE_CORRUPT_SEQUENCE; + public static final int CORRUPT_INDEX = CApi.SQLITE_CORRUPT_INDEX; + public static final int READONLY_RECOVERY = CApi.SQLITE_READONLY_RECOVERY; + public static final int READONLY_CANTLOCK = CApi.SQLITE_READONLY_CANTLOCK; + public static final int READONLY_ROLLBACK = CApi.SQLITE_READONLY_ROLLBACK; + public static final int READONLY_DBMOVED = CApi.SQLITE_READONLY_DBMOVED; + public static final int READONLY_CANTINIT = CApi.SQLITE_READONLY_CANTINIT; + public static final int READONLY_DIRECTORY = CApi.SQLITE_READONLY_DIRECTORY; + public static final int ABORT_ROLLBACK = CApi.SQLITE_ABORT_ROLLBACK; + public static final int CONSTRAINT_CHECK = CApi.SQLITE_CONSTRAINT_CHECK; + public static final int CONSTRAINT_COMMITHOOK = CApi.SQLITE_CONSTRAINT_COMMITHOOK; + public static final int CONSTRAINT_FOREIGNKEY = CApi.SQLITE_CONSTRAINT_FOREIGNKEY; + public static final int CONSTRAINT_FUNCTION = CApi.SQLITE_CONSTRAINT_FUNCTION; + public static final int CONSTRAINT_NOTNULL = CApi.SQLITE_CONSTRAINT_NOTNULL; + public static final int CONSTRAINT_PRIMARYKEY = CApi.SQLITE_CONSTRAINT_PRIMARYKEY; + public static final int CONSTRAINT_TRIGGER = CApi.SQLITE_CONSTRAINT_TRIGGER; + public static final int CONSTRAINT_UNIQUE = CApi.SQLITE_CONSTRAINT_UNIQUE; + public static final int CONSTRAINT_VTAB = CApi.SQLITE_CONSTRAINT_VTAB; + public static final int CONSTRAINT_ROWID = CApi.SQLITE_CONSTRAINT_ROWID; + public static final int CONSTRAINT_PINNED = CApi.SQLITE_CONSTRAINT_PINNED; + public static final int CONSTRAINT_DATATYPE = CApi.SQLITE_CONSTRAINT_DATATYPE; + public static final int NOTICE_RECOVER_WAL = CApi.SQLITE_NOTICE_RECOVER_WAL; + public static final int NOTICE_RECOVER_ROLLBACK = CApi.SQLITE_NOTICE_RECOVER_ROLLBACK; + public static final int WARNING_AUTOINDEX = CApi.SQLITE_WARNING_AUTOINDEX; + public static final int AUTH_USER = CApi.SQLITE_AUTH_USER; + public static final int OK_LOAD_PERMANENTLY = CApi.SQLITE_OK_LOAD_PERMANENTLY; + public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; @@ -108,10 +213,6 @@ public final class Sqlite implements AutoCloseable { /* We elide the UTF16_ALIGNED from this interface because it is irrelevant for the Java interface. */ - public static final int DONE = CApi.SQLITE_DONE; - public static final int BUSY = CApi.SQLITE_BUSY; - public static final int LOCKED = CApi.SQLITE_LOCKED; - public static final int DENY = CApi.SQLITE_DENY; public static final int IGNORE = CApi.SQLITE_IGNORE; public static final int CREATE_INDEX = CApi.SQLITE_CREATE_INDEX; @@ -1482,4 +1583,34 @@ public final class Sqlite implements AutoCloseable { } CApi.sqlite3_progress_handler( thisDb(), n, phc ); } + + + /** + Callback for use with setAuthorizer(). + */ + public interface Authorizer { + /** + Must function as described for the C-level + sqlite3_set_authorizer() callback. If it throws, the error is + converted to a db-level error and the exception is suppressed. + */ + int call(int opId, String s1, String s2, String s3, String s4); + } + + /** + Analog to sqlite3_set_authorizer(), this sets the current + authorizer callback, or clears if it passed null. + */ + public void setAuthorizer( Authorizer a ) { + org.sqlite.jni.capi.AuthorizerCallback ac = null; + if( null!=a ){ + ac = new org.sqlite.jni.capi.AuthorizerCallback(){ + @Override public int call(int opId, String s1, String s2, String s3, String s4){ + return a.call(opId, s1, s2, s3, s4); + } + }; + } + checkRc( CApi.sqlite3_set_authorizer( thisDb(), ac ) ); + } + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 1af2c8c5d1..b03a83417f 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -888,6 +888,30 @@ public class Tester2 implements Runnable { db.close(); } + private void testAuthorizer(){ + final Sqlite db = openDb(); + final ValueHolder counter = new ValueHolder<>(0); + final ValueHolder authRc = new ValueHolder<>(0); + final Sqlite.Authorizer auth = new Sqlite.Authorizer(){ + public int call(int op, String s0, String s1, String s2, String s3){ + ++counter.value; + //outln("xAuth(): "+s0+" "+s1+" "+s2+" "+s3); + return authRc.value; + } + }; + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + db.setAuthorizer(auth); + execSql(db, "UPDATE t SET a=1"); + affirm( 1 == counter.value ); + authRc.value = Sqlite.DENY; + int rc = execSql(db, false, "UPDATE t SET a=2"); + affirm( Sqlite.AUTH==rc ); + db.setAuthorizer(null); + rc = execSql(db, false, "UPDATE t SET a=2"); + affirm( 0==rc ); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 82936f69b9..c813cb698b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sprogress-handler\ssupport\sto\sJNI\swrapper1.\sCorrect\sthe\sreturn\stype\sof\sthe\sextended_result_codes()\sJNI\sbinding\sand\sexpose\sit\sto\swrapper1. -D 2023-11-05T01:39:29.450 +C Add\sauthorizer\ssupport\sto\s\sJNI\swrapper1. +D 2023-11-05T01:55:20.977 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -248,7 +248,7 @@ F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7 F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0 -F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013 +F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca F ext/jni/src/org/sqlite/jni/capi/CApi.java a6d4fdd35e4fcfe95c61e343eb389cfaab3d5166e9670730aef14240a982b97b @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 909ebd23111762c878116ebacf73a848c8323fb46e8128eb3d99a85d48905444 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 89d3c4b2ffa9a7d3a125daae66167d29f64058702f65254b7d4a721e3868bd1b F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 923c885609ca9e6793636fc17787d0058c80ce1d7ba5a69eb40e68cc299892d1 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 2397e70955de15f8fb65a7ed421365f018c099d16b8080f81c7553e4dae3d44f F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 822cf3d66beb1efcac1792c8652cbebb80ae998275a33337de46fde1670e0008 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 199b9fb56e06f808d5856494ca8840e9004ff939f27c7c6fda9a60858144f95d F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618 -R dafa52fdba2c999e04b851e5ec6214ab +P 6c0acfdce2160d8db261a59677cec571b6abc333481525b1ec975d98e88bec88 +R d2d319f241ba72795b6add1f6b68fc9e U stephan -Z 824d9a7ed24ad4091f85bc7e5264c033 +Z a73e3653c86a5ad585af4f9360f1181a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7f82e1d85b..db8f9c9684 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6c0acfdce2160d8db261a59677cec571b6abc333481525b1ec975d98e88bec88 \ No newline at end of file +773f9873865b5277a6a682c4695f216bfe1ec05ed5e5a2a70aaa451934ba2dc0 \ No newline at end of file From 546db3f14a5722ddfdaff671430b32cf84bd8985 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 03:37:33 +0000 Subject: [PATCH 119/347] JNI wrapper1 normalizeSql() now throws UnsupportedOperationException, instead of returning null, if built without SQLITE_ENABLE_NORMALIZE. Remove SQLITE_PREPARE_NORMALIZE from the JNI interface because it's a legacy no-op. FossilOrigin-Name: d081a126697e214082f3b203f23ea63510080e5c2aac1d8badc9e6e4bfea7c95 --- ext/jni/src/c/sqlite3-jni.h | 2 -- ext/jni/src/org/sqlite/jni/capi/CApi.java | 1 - .../jni/capi/PreupdateHookCallback.java | 3 ++- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 2 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 23 ++++++++++++++++--- manifest | 20 ++++++++-------- manifest.uuid | 2 +- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index a1097fe57a..c4ca0559f2 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -427,8 +427,6 @@ extern "C" { #define org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE 33554432L #undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT #define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT 1L -#undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NORMALIZE -#define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NORMALIZE 2L #undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB #define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB 4L #undef org_sqlite_jni_capi_CApi_SQLITE_OK diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index f1d4def500..d1930e1e32 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -2279,7 +2279,6 @@ public final class CApi { // prepare flags public static final int SQLITE_PREPARE_PERSISTENT = 1; - public static final int SQLITE_PREPARE_NORMALIZE = 2; public static final int SQLITE_PREPARE_NO_VTAB = 4; // result codes diff --git a/ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java b/ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java index 99d3fb0351..38f7c5613e 100644 --- a/ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java @@ -19,7 +19,8 @@ package org.sqlite.jni.capi; public interface PreupdateHookCallback extends CallbackProxy { /** Must function as described for the C-level sqlite3_preupdate_hook() - callback. + callback. If it throws, the exception is translated to a + db-level error and the exception is suppressed. */ void call(sqlite3 db, int op, String dbName, String dbTable, long iKey1, long iKey2 ); diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index fb53196d26..5655ac7809 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -327,7 +327,7 @@ public class Tester1 implements Runnable { rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)", - SQLITE_PREPARE_NORMALIZE, outStmt); + 0, outStmt); affirm(0 == rc); stmt = outStmt.get(); affirm(0 != stmt.getNativePointer()); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index cf18277329..abf7f20f06 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -178,7 +178,6 @@ public final class Sqlite implements AutoCloseable { public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS; public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT; - public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE; public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB; public static final int TRACE_STMT = CApi.SQLITE_TRACE_STMT; @@ -336,6 +335,22 @@ public final class Sqlite implements AutoCloseable { return CApi.sqlite3_compileoption_used(optName); } + private static boolean hasNormalizeSql = + compileOptionUsed("ENABLE_NORMALIZE"); + + /** + Throws UnsupportedOperationException if check is false. + flag is expected to be the name of an SQLITE_ENABLE_... + build flag. + */ + private static void checkSupported(boolean check, String flag){ + if( !check ){ + throw new UnsupportedOperationException( + "Library was built without "+flag + ); + } + } + /** Analog to sqlite3_complete(). */ @@ -972,10 +987,12 @@ public final class Sqlite implements AutoCloseable { } /** - Analog to sqlite3_normalized_sql(), returning null if the - library is built without the SQLITE_ENABLE_NORMALIZE flag. + Analog to sqlite3_normalized_sql(), but throws + UnsupportedOperationException if the library was built without + the SQLITE_ENABLE_NORMALIZE flag. */ public String normalizedSql(){ + Sqlite.checkSupported(hasNormalizeSql, "SQLITE_ENABLE_NORMALIZE"); return CApi.sqlite3_normalized_sql(thisStmt()); } diff --git a/manifest b/manifest index c813cb698b..5467b57897 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sauthorizer\ssupport\sto\s\sJNI\swrapper1. -D 2023-11-05T01:55:20.977 +C JNI\swrapper1\snormalizeSql()\snow\sthrows\sUnsupportedOperationException,\sinstead\sof\sreturning\snull,\sif\sbuilt\swithout\sSQLITE_ENABLE_NORMALIZE.\sRemove\sSQLITE_PREPARE_NORMALIZE\sfrom\sthe\sJNI\sinterface\sbecause\sit's\sa\slegacy\sno-op. +D 2023-11-05T03:37:33.975 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -242,7 +242,7 @@ F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c b98d822a35ef4438023c3c14816b5ac17bdbd23bc838ff80b6463c3146a75d14 -F ext/jni/src/c/sqlite3-jni.h 03f66d3b43359dacd7c2c9407d78274dbdb027bc7610985011bf2b462222414e +F ext/jni/src/c/sqlite3-jni.h 9300900f7ec91fffa01445e1ad5815e35a7bece4c9ca07de464641f77195404a F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java a6d4fdd35e4fcfe95c61e343eb389cfaab3d5166e9670730aef14240a982b97b +F ext/jni/src/org/sqlite/jni/capi/CApi.java 41da9b46718b258ad2f195f3dec7265747eff37b6b350b8c427ee290eed0d83c F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -261,7 +261,7 @@ F ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java 701f2e4d8bdeb27cfbee F ext/jni/src/org/sqlite/jni/capi/NativePointerHolder.java b7036dcb1ef1b39f1f36ac605dde0ff1a24a9a01ade6aa1a605039443e089a61 F ext/jni/src/org/sqlite/jni/capi/OutputPointer.java 68f60aec7aeb5cd4e5fb83449037f668c63cb99f682ee1036cc226d0cbd909b9 F ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java aca8f9fa72e3b6602bc9a7dd3ae9f5b2808103fbbee9b2749dc96c19cdc261a1 -F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17ca4b7ddde1d8cd6915b6ab7b708249a9787beca6bd6b6 +F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java efcf57545c5e282d1dd332fa63329b3b218d98f356ef107a9dbe3979be82213a F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java e172210a2080e851ebb694c70e9f0bf89284237795e38710a7f5f1b61e3f6787 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 89d3c4b2ffa9a7d3a125daae66167d29f64058702f65254b7d4a721e3868bd1b +F ext/jni/src/org/sqlite/jni/capi/Tester1.java f931370e0beb121390c31747a019690a7cf0927120396862d2a02f75a66bf89c F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,7 +296,7 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 2397e70955de15f8fb65a7ed421365f018c099d16b8080f81c7553e4dae3d44f +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fbdb8d6643ac1f32c4ffdb76a2ee4580edd0b1935d8d13ad7f581a702635313b F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 199b9fb56e06f808d5856494ca8840e9004ff939f27c7c6fda9a60858144f95d F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af @@ -2142,8 +2142,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 6c0acfdce2160d8db261a59677cec571b6abc333481525b1ec975d98e88bec88 -R d2d319f241ba72795b6add1f6b68fc9e +P 773f9873865b5277a6a682c4695f216bfe1ec05ed5e5a2a70aaa451934ba2dc0 +R ea6cc7dea8e7673dad2d1e0c776c92af U stephan -Z a73e3653c86a5ad585af4f9360f1181a +Z d4fc1a1c9c9dbb69dd1ef9b20d8e37f3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index db8f9c9684..624838ac87 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -773f9873865b5277a6a682c4695f216bfe1ec05ed5e5a2a70aaa451934ba2dc0 \ No newline at end of file +d081a126697e214082f3b203f23ea63510080e5c2aac1d8badc9e6e4bfea7c95 \ No newline at end of file From ed99e7493aa4f3dd2c49859ca8aaa2c9a831de7a Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 5 Nov 2023 04:20:04 +0000 Subject: [PATCH 120/347] Add incremental blob I/O support to JNI wrapper1. FossilOrigin-Name: 7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0 --- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 2 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 75 ++++++++++++++++++- .../src/org/sqlite/jni/wrapper1/Tester2.java | 26 +++++++ manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 5655ac7809..b25d077408 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1633,7 +1633,7 @@ public class Tester1 implements Runnable { sqlite3_finalize(stmt); b = sqlite3_blob_open(db, "main", "t", "a", - sqlite3_last_insert_rowid(db), 1); + sqlite3_last_insert_rowid(db), 0); affirm( null!=b ); rc = sqlite3_blob_reopen(b, 2); affirm( 0==rc ); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index abf7f20f06..93c294f5de 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -17,6 +17,7 @@ import org.sqlite.jni.capi.CApi; import org.sqlite.jni.capi.sqlite3; import org.sqlite.jni.capi.sqlite3_stmt; import org.sqlite.jni.capi.sqlite3_backup; +import org.sqlite.jni.capi.sqlite3_blob; import org.sqlite.jni.capi.OutputPointer; /** @@ -473,7 +474,7 @@ public final class Sqlite implements AutoCloseable { if( 0!=rc ){ if( CApi.SQLITE_NOMEM==rc ){ throw new OutOfMemoryError(); - }else if( null==db || 0==CApi.sqlite3_errcode(db)){ + }else if( null==db || 0==CApi.sqlite3_errcode(db) ){ throw new SqliteException(rc); }else{ throw new SqliteException(db); @@ -1630,4 +1631,76 @@ public final class Sqlite implements AutoCloseable { checkRc( CApi.sqlite3_set_authorizer( thisDb(), ac ) ); } + /** + Object type for use with blobOpen() + */ + public final class Blob implements AutoCloseable { + private Sqlite db; + private sqlite3_blob b; + Blob(Sqlite db, sqlite3_blob b){ + this.db = db; + this.b = b; + } + + /** + Analog to sqlite3_blob_close(). + */ + @Override public void close(){ + if( null!=b ){ + CApi.sqlite3_blob_close(b); + b = null; + db = null; + } + } + + /** + Analog to sqlite3_blob_reopen() but throws on error. + */ + public void reopen(long newRowId){ + db.checkRc( CApi.sqlite3_blob_reopen(b, newRowId) ); + } + + /** + Analog to sqlite3_blob_write() but throws on error. + */ + public void write( byte[] bytes, int atOffset ){ + db.checkRc( CApi.sqlite3_blob_write(b, bytes, atOffset) ); + } + + /** + Analog to sqlite3_blob_read() but throws on error. + */ + public void read( byte[] dest, int atOffset ){ + db.checkRc( CApi.sqlite3_blob_read(b, dest, atOffset) ); + } + + /** + Analog to sqlite3_blob_bytes(). + */ + public int bytes(){ + return CApi.sqlite3_blob_bytes(b); + } + } + + /** + Analog to sqlite3_blob_open(). Returns a Blob object for the + given database, table, column, and rowid. The blob is opened for + read-write mode if writeable is true, else it is read-only. + + The returned object must eventually be freed, before this + database is closed, by either arranging for it to be auto-closed + or calling its close() method. + + Throws on error. + */ + public Blob blobOpen(String dbName, String tableName, String columnName, + long iRow, boolean writeable){ + final OutputPointer.sqlite3_blob out = new OutputPointer.sqlite3_blob(); + checkRc( + CApi.sqlite3_blob_open(thisDb(), dbName, tableName, columnName, + iRow, writeable ? 1 : 0, out) + ); + return new Blob(this, out.take()); + } + } diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index b03a83417f..3f7593c709 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -912,6 +912,32 @@ public class Tester2 implements Runnable { db.close(); } + private void testBlobOpen(){ + final Sqlite db = openDb(); + + execSql(db, "CREATE TABLE T(a BLOB);" + +"INSERT INTO t(rowid,a) VALUES(1, 'def'),(2, 'XYZ');" + ); + Sqlite.Blob b = db.blobOpen("main", "t", "a", + db.lastInsertRowId(), true); + affirm( 3==b.bytes() ); + b.write(new byte[] {100, 101, 102 /*"DEF"*/}, 0); + b.close(); + Sqlite.Stmt stmt = db.prepare("SELECT length(a), a FROM t ORDER BY a"); + affirm( stmt.step() ); + affirm( 3 == stmt.columnInt(0) ); + affirm( "def".equals(stmt.columnText16(1)) ); + stmt.finalizeStmt(); + + b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), false); + b.reopen(2); + final byte[] tgt = new byte[3]; + b.read( tgt, 0 ); + affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" ); + b.close(); + db.close(); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 5467b57897..f81c5b0edc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\swrapper1\snormalizeSql()\snow\sthrows\sUnsupportedOperationException,\sinstead\sof\sreturning\snull,\sif\sbuilt\swithout\sSQLITE_ENABLE_NORMALIZE.\sRemove\sSQLITE_PREPARE_NORMALIZE\sfrom\sthe\sJNI\sinterface\sbecause\sit's\sa\slegacy\sno-op. -D 2023-11-05T03:37:33.975 +C Add\sincremental\sblob\sI/O\ssupport\sto\sJNI\swrapper1. +D 2023-11-05T04:20:04.320 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java f931370e0beb121390c31747a019690a7cf0927120396862d2a02f75a66bf89c +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 2898e38cf5ee568a93e760fa9b84f448acb58138d0fca18d146e80a1da7c45fc F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fbdb8d6643ac1f32c4ffdb76a2ee4580edd0b1935d8d13ad7f581a702635313b +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 6a310fe422d0daf79f7841c9b341f64d843ca7e85ef31829530623a81ecd25fa F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 199b9fb56e06f808d5856494ca8840e9004ff939f27c7c6fda9a60858144f95d +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java a944fd0a9047a51a5074bffd707452d80cf06e715ebc78b541480ee98629fb93 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 773f9873865b5277a6a682c4695f216bfe1ec05ed5e5a2a70aaa451934ba2dc0 -R ea6cc7dea8e7673dad2d1e0c776c92af +P d081a126697e214082f3b203f23ea63510080e5c2aac1d8badc9e6e4bfea7c95 +R d10239966e7198eb47ba47ac2878d98e U stephan -Z d4fc1a1c9c9dbb69dd1ef9b20d8e37f3 +Z e8e87bde5ba89e299cdcc353d989a2b2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 624838ac87..63c307a8c4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d081a126697e214082f3b203f23ea63510080e5c2aac1d8badc9e6e4bfea7c95 \ No newline at end of file +7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0 \ No newline at end of file From ff396346256f189f1ed336f630c191c57583f337 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 5 Nov 2023 19:42:00 +0000 Subject: [PATCH 121/347] Input working. No line-editor provisions yet. (WIP, but suitable for testing.) FossilOrigin-Name: e8568b1d925c2118eb08394dd8aa50cfb521240f87668f535ec4a03e67dc9a09 --- manifest | 14 +-- manifest.uuid | 2 +- src/console_io.c | 312 ++++++++++++++++++++++++++++++----------------- src/console_io.h | 22 +++- 4 files changed, 228 insertions(+), 122 deletions(-) diff --git a/manifest b/manifest index 914526c556..da6d8f4914 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Setup,\stakedown,\smode\sset\sand\soutput\sworking.\sNo\sinput\syet.\s(WIP) -D 2023-11-05T01:21:14.743 +C Input\sworking.\sNo\sline-editor\sprovisions\syet.\s(WIP,\sbut\ssuitable\sfor\stesting.) +D 2023-11-05T19:42:00.134 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,8 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c 484a4ad56c18ddc62b845494f8ac6e06df9f9b0bc2fa60ac2dcba74635bdf8da x -F src/console_io.h 6b7c86a5778445e507a089b9808803e4523e2989f33c04692e008eae3f0f4d8a +F src/console_io.c a04f62d1930d32f0bda26b546e9b3d6455e7caaee4ea7624be4b1ef7ea2329b7 x +F src/console_io.h 49680984d2a121697ccb2620f9e8465393d61c3edc3d5720c77dfa0a1f4a7389 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -2144,8 +2144,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 64abef8314b8544fdc7b71317d61a4641dc607a1ae42b8ff21543226fd338ba2 -R 9aec452b903f28bc0cbf638b7c11370e +P dfea85be1fb927ea446c9d98bae42ba1197bdab098aa6d95aa512a37d07a1e52 +R 4898fd3a7f002f1e4e422622974a0785 U larrybr -Z 37ecbe5d111f6f842b9b1e97c1e0e0d1 +Z c9314460b6b28a6cd16041af96151d83 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5e27b835d6..c3c4b37173 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dfea85be1fb927ea446c9d98bae42ba1197bdab098aa6d95aa512a37d07a1e52 \ No newline at end of file +e8568b1d925c2118eb08394dd8aa50cfb521240f87668f535ec4a03e67dc9a09 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index 8c4db84396..991479e0f8 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -8,7 +8,7 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* +******************************************************************************** ** This file implements various interfaces used for console I/O by the ** SQLite project command-line tools, as explained in console_io.h . */ @@ -28,7 +28,7 @@ # ifdef SHELL_LEGACY_CONSOLE_IO # define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */ # else -# define SHELL_CON_TRANSLATE 1 /* Use wchar APIs for console I/O */ +# define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ # endif #else # include @@ -45,91 +45,102 @@ static HANDLE handleOfFile(FILE *pf){ } #endif -static short fileOfConsole(FILE *pf){ +typedef struct PerStreamTags { #if SHELL_CON_TRANSLATE + DWORD consMode; + HANDLE hx; +#endif + FILE *pf; +} PerStreamTags; + +static short fileOfConsole(FILE *pf, PerStreamTags *ppst){ +#if SHELL_CON_TRANSLATE + short rv = 0; DWORD dwj; HANDLE fh = handleOfFile(pf); if( INVALID_HANDLE_VALUE != fh ){ - return (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwj)); - }else return 0; + rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwj)); + if( rv ){ + ppst->hx = fh; + ppst->pf = pf; + GetConsoleMode(fh, &ppst->consMode); + } + } + return rv; #else return (short)isatty(fileno(pf)); #endif } #define SHELL_INVALID_FILE_PTR ((FILE *)sizeof(FILE*)) +#define SHELL_INVALID_CONS_MODE 0xFFFF0000 + +#if SHELL_CON_TRANSLATE +# define SHELL_CONI_MODE \ + (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ + | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) +# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ + | ENABLE_VIRTUAL_TERMINAL_PROCESSING) +#endif typedef struct ConsoleInfo { - /* int iDefaultFmode; */ - ConsoleStdConsStreams cscs; + /* int iInitialFmode[3]; + ** Above only needed for legacy console I/O for callable CLI. + ** Because that state cannot be obtained from each FILE *, + ** there will be no exact restoration of console state for + ** the CLI when built with SHELL_LEGACY_CONSOLE_IO defined. + */ + PerStreamTags pst[3]; #if SHELL_CON_TRANSLATE - HANDLE hIn; HANDLE hOut; HANDLE hErr; - HANDLE hLowest; + unsigned char haveInput; + unsigned char outputIx; + unsigned char stdinEof; #endif - FILE *pfIn; FILE *pfOut; FILE *pfErr; + ConsoleStdConsStreams cscs; } ConsoleInfo; -static ConsoleInfo consoleInfo = { - /* 0, iDefaultFmode */ - CSCS_NoConsole, #if SHELL_CON_TRANSLATE - INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, - INVALID_HANDLE_VALUE, +# define CI_INITIALIZER \ + {SHELL_INVALID_CONS_MODE, INVALID_HANDLE_VALUE, SHELL_INVALID_FILE_PTR } +#else +# define CI_INITIALIZER { SHELL_INVALID_FILE_PTR } #endif - SHELL_INVALID_FILE_PTR, SHELL_INVALID_FILE_PTR, SHELL_INVALID_FILE_PTR -}; -#undef SHELL_INVALID_FILE_PTR -#if SHELL_CON_TRANSLATE == 1 -# define SHELL_CON_MODE_CSZ _O_U16TEXT -#elif SHELL_CON_TRANSLATE == 2 -# define SHELL_CON_MODE_CSZ _O_U8TEXT +static ConsoleInfo consoleInfo = { + /* {0,0,0}, // iInitialFmode */ + { /* pst */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, +#if SHELL_CON_TRANSLATE + 0, 0, 1, /* haveInput, outputIx, stdinEof */ #endif + CSCS_NoConsole +}; +#undef CI_INITIALIZER INT_LINKAGE ConsoleStdConsStreams consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ ConsoleStdConsStreams rv = CSCS_NoConsole; - if( fileOfConsole(pfErr) ){ - rv |= CSCS_ErrConsole; - consoleInfo.pfErr = pfErr; + FILE *apf[3] = { pfIn, pfOut, pfErr }; + int ix; + for( ix = 2; ix >= 0; --ix ){ + PerStreamTags *ppst = &consoleInfo.pst[ix]; + if( fileOfConsole(apf[ix], ppst) ){ #if SHELL_CON_TRANSLATE - fflush(pfErr); -# if SHELL_CON_TRANSLATE == 1 - _setmode(_fileno(pfErr), _O_U16TEXT); - _setmode(_fileno(pfErr), _O_BINARY); -# elif SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(pfErr), _O_U8TEXT); - _setmode(_fileno(pfErr), _O_TEXT); -# endif - consoleInfo.hLowest = consoleInfo.hErr = handleOfFile(pfErr); + DWORD cm = (ix==0)? SHELL_CONI_MODE : SHELL_CONO_MODE; + if( ix==0 ){ + consoleInfo.haveInput = 1; + consoleInfo.stdinEof = 0; + }else{ + consoleInfo.outputIx |= ix; + } + SetConsoleMode(ppst->hx, cm); + fprintf(stderr, "consMode[%d]: %02x -> %02x\n", ix, ppst->consMode, cm); #endif - } - if( fileOfConsole(pfOut) ){ - rv |= CSCS_OutConsole; - consoleInfo.pfOut = pfOut; -#if SHELL_CON_TRANSLATE - fflush(pfOut); -# if SHELL_CON_TRANSLATE == 1 - _setmode(_fileno(pfOut), _O_U16TEXT); - _setmode(_fileno(pfOut), _O_BINARY); -# elif SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(pfOut), _O_U8TEXT); - _setmode(_fileno(pfOut), _O_TEXT); -# endif - consoleInfo.hLowest = consoleInfo.hOut = handleOfFile(pfOut); -#endif - } - if( fileOfConsole(pfIn) ){ - rv |= CSCS_InConsole; - consoleInfo.pfIn = pfIn; -#if SHELL_CON_TRANSLATE == 1 - _setmode(_fileno(pfIn), _O_U16TEXT); - _setmode(_fileno(pfIn), _O_BINARY); - consoleInfo.hLowest = consoleInfo.hIn = handleOfFile(pfIn); -#elif SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(pfIn), _O_U8TEXT); - _setmode(_fileno(pfIn), _O_TEXT); - consoleInfo.hLowest = consoleInfo.hIn = handleOfFile(pfIn); + rv |= (CSCS_InConsole< 0 ) fflush(apf[ix]); +#if SHELL_CON_TRANSLATE == 2 + _setmode(_fileno(apf[ix]), _O_U8TEXT); + _setmode(_fileno(apf[ix]), _O_TEXT); #endif } consoleInfo.cscs = rv; @@ -137,66 +148,69 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ } INT_LINKAGE void SQLITE_CDECL consoleRestore( void ){ -#if SHELL_CON_TRANSLATE if( consoleInfo.cscs ){ - /* ToDo: Read these modes in consoleClassifySetup somehow. - ** A _get_fmode() call almost works. But not with gcc, yet. - ** This has to be done to make the CLI a callable function. - */ - int tmode = _O_TEXT, xmode = _O_U8TEXT; - if( consoleInfo.cscs & CSCS_InConsole ){ - _setmode(_fileno(consoleInfo.pfIn), tmode); - _setmode(_fileno(consoleInfo.pfIn), xmode); - } - if( consoleInfo.cscs & CSCS_OutConsole ){ - _setmode(_fileno(consoleInfo.pfOut), tmode); - _setmode(_fileno(consoleInfo.pfOut), xmode); - } - if( consoleInfo.cscs & CSCS_ErrConsole ){ - _setmode(_fileno(consoleInfo.pfErr), tmode); - _setmode(_fileno(consoleInfo.pfErr), xmode); + int ix; + for( ix=0; ix<3; ++ix ){ + if( consoleInfo.cscs & (CSCS_InConsole<hx, ppst->consMode); + ppst->hx = INVALID_HANDLE_VALUE; +#endif + ppst->pf = SHELL_INVALID_FILE_PTR; + } + consoleInfo.cscs = CSCS_NoConsole; +#if SHELL_CON_TRANSLATE + consoleInfo.stdinEof = consoleInfo.haveInput = consoleInfo.outputIx= 0; +#endif } } -#endif -#ifdef TEST_CIO -#endif } +#undef SHELL_INVALID_FILE_PTR static short isConOut(FILE *pf){ - if( pf==consoleInfo.pfOut ) return 1; - else if( pf==consoleInfo.pfErr ) return 2; + if( pf==consoleInfo.pst[1].pf ) return 1; + else if( pf==consoleInfo.pst[2].pf ) return 2; else return 0; } -INT_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ +#if SHELL_CON_TRANSLATE +static void setModeFlushQ(FILE *pf, short bFlush, int mode){ short ico = isConOut(pf); - if( ico || bFlush ) fflush(pf); -#if SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(pf), _O_BINARY); -#elif SHELL_CON_TRANSLATE == 1 - /* Never change between text/binary on UTF-16 console streamss. */ - if( !ico && !(consoleInfo.pfIn==pf)) _setmode(_fileno(pf), _O_BINARY); + if( ico>1 || bFlush ) fflush(pf); + _setmode(_fileno(pf), mode); +} +#else +# define setModeFlushQ(f, b, m) if(isConOut(f)>0||b) fflush(f) #endif + +INT_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ + setModeFlushQ(pf, bFlush, _O_BINARY); } INT_LINKAGE void setTextMode(FILE *pf, short bFlush){ - short ico = isConOut(pf); - if( ico || bFlush ) fflush(pf); -#if SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(pf), _O_TEXT); -#elif SHELL_CON_TRANSLATE == 1 - /* Never change between text/binary on UTF-16 console streamss. */ - if( !ico && !(consoleInfo.pfIn==pf)) _setmode(_fileno(pf), _O_TEXT); -#endif + setModeFlushQ(pf, bFlush, _O_TEXT); } -/* Later: Factor common code out of above 2 procs. */ +#undef setModeFlushQ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ va_list ap; int rv = 0; - short on = isConOut(pfO); - va_start(ap, zFormat); - if( on > 0 ){ #if SHELL_CON_TRANSLATE + short on = isConOut(pfO); +#endif + va_start(ap, zFormat); +#if SHELL_CON_TRANSLATE + if( on > 0 ){ char *z1 = sqlite3_vmprintf(zFormat, ap); # if SHELL_CON_TRANSLATE == 2 /* Legacy translation to active code page, then MBCS chars out. */ @@ -216,7 +230,7 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ if( nwc>0 ){ zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); if( zw2!=NULL ){ - HANDLE ho = (on==1)? consoleInfo.hOut : consoleInfo.hErr; + HANDLE ho = (on==1)? consoleInfo.pst[1].hx : consoleInfo.pst[2].hx; nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,zw2,nwc); zw2[nwc] = 0; WriteConsoleW(ho, zw2, nwc, 0, NULL); @@ -226,32 +240,112 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ } # endif sqlite3_free(z1); -#else -#endif }else{ +#endif rv = vfprintf(pfO, zFormat, ap); +#if SHELL_CON_TRANSLATE } +#endif va_end(ap); return rv; } -INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn){ - return 0; +INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ + if( pfIn==0 ) pfIn = stdin; +#if SHELL_CON_TRANSLATE + if( pfIn == consoleInfo.pst[0].pf ){ + static const int nwcLen = 150; + WCHAR wcBuf[nwcLen+1]; + int lend = 0, noc = 0; + if( consoleInfo.stdinEof ) return 0; + if( ncMax > 0 ) cBuf[0] = 0; + while( noc < ncMax-8-1 && !lend ){ + /* There is room for at least 2 more characters and a 0-terminator. */ + int na = (ncMax > nwcLen*4+1 + noc)? nwcLen : (ncMax-1 - noc)/4; + DWORD nbr = 0; + BOOL bRC = ReadConsoleW(consoleInfo.pst[0].hx, wcBuf, na, &nbr, 0); + if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ + /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */ + DWORD nbrx; + bRC &= ReadConsoleW(consoleInfo.pst[0].hx, wcBuf+nbr, 1, &nbrx, 0); + if( bRC ) nbr += nbrx; + } + hd(wcBuf,nbr); + if( !bRC || (noc==0 && nbr==0) ) return 0; + if( nbr > 0 ){ + int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); + if( nmb != 0 && noc+nmb <= ncMax ){ + int iseg = noc; + nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0); + noc += nmb; + /* Fixup line-ends as coded by Windows for CR (or "Enter".) + ** Note that this is done without regard for any setModeText() + ** call that might have been done on the interactive input. + */ + if( noc > 0 ){ + if( cBuf[noc-1]=='\n' ){ + lend = 1; + if( noc > 1 && cBuf[noc-2]=='\r' ){ + cBuf[noc-2] = '\n'; + --noc; + } + } + } + /* Check for ^Z (anywhere in line) too. */ + while( iseg < noc ){ + if( cBuf[iseg]==0x1a ){ + consoleInfo.stdinEof = 1; + noc = iseg; /* Chop ^Z and anything following. */ + break; + } + ++iseg; + } + }else break; /* Drop apparent garbage in. (Could assert.) */ + }else break; + } + /* If got nothing, (after ^Z chop), must be at end-of-file. */ + if( noc == 0 ) return 0; + cBuf[noc] = 0; + return cBuf; + }else{ +#endif + return fgets(cBuf, ncMax, pfIn); +#if SHELL_CON_TRANSLATE + } +#endif } #ifdef TEST_CIO // cl -Zi -I. -DWIN32 -DTEST_CIO sqlite3.c src/console_io.c -Fecio.exe -// gcc -I. -DWIN32 -DTEST_CIO -o cio sqlite3.c src/console_io.c -o cio.exe +// gcc -I. -DWIN32 -DTEST_CIO sqlite3.c src/console_io.c -o cio.exe const char *prompts[] = { "main", "cont" }; Prompts goofy = { 2, prompts }; int main(int na, char *av[]){ ConsoleStdConsStreams cc = consoleClassifySetup(stdin, stdout, stderr); + const char *zt = "Math: ±×÷∂∆∙√∞∩∫≈≠≡≤≥\n" + "Hiragana: 亜唖娃阿哀愛挨姶逢葵茜穐悪握渥旭葦芦鯵梓圧斡扱" + "宛姐虻飴絢綾鮎或粟袷安庵按暗案闇鞍杏\n" + "Simplified Chinese: 餐参蚕残惭惨灿掺孱骖璨粲黪\n" + "Geometric Shapes: ■□▪▫▲△▼▽◆◇◊○◌◎●◢◣◤◥◦\n" + "Boxes single: ─━│┃┄┅┆┇┈┉┊┋ ┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳" + "┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋\n" + "Boxes double: ═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬\n" + "Rounded corners and diagonals: ╭╮╯╰╱╲╳\n" + ; + char inBuf[150]; setTextMode(stdout, 1); setTextMode(stderr, 1); - fprintfUtf8(stderr, "%d\n", cc); + fprintfUtf8(stderr, "Console streams: %d, CP_UTF8 valid: %d\n", cc, + IsValidCodePage(CP_UTF8)); fprintfUtf8(stdout, "%s=%d\n", "∑(1st 7 primes)", 42); fprintfUtf8(stderr, "%s\n", "∫ (1/x) dx ≡ ln(x)"); + fprintfUtf8(stdout, "%s", zt); + fprintfUtf8(stderr, "Entering input/echo loop." + " Type or copy/paste, or EOF to exit.\n"); + while( fprintfUtf8(stdout,"? ") && fgetsUtf8(inBuf, sizeof(inBuf), stdin) ){ + fprintfUtf8(stdout, "! %s", inBuf); + } consoleRestore(); return 0; } diff --git a/src/console_io.h b/src/console_io.h index 444c3e0e65..6f838427de 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -60,6 +60,10 @@ typedef enum ConsoleStdConsStreams { ** ** On some platforms, stream or console mode alteration (aka ** "Setup") may be made which is undone by consoleRestore(). +** +** Applications which run an inferior (child) process which +** inherits the same I/O streams may call this function after +** such a process exits to guard against console mode changes. */ INT_LINKAGE ConsoleStdConsStreams consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); @@ -70,7 +74,13 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); ** This should be called after consoleClassifySetup() and ** before the process terminates normally. It is suitable ** for use with the atexit() C library procedure. After -** this call, no I/O should be done with the console. +** this call, no I/O should be done with the console +** until consoleClassifySetup(...) is called again. +** +** Applications which run an inferior (child) process that +** inherits the same I/O streams might call this procedure +** before so that said process will have a console setup +** however users have configured it or come to expect. */ INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); @@ -87,9 +97,10 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); ** Collect input like fgets(...) with special provisions for input ** from the console on platforms that require same. Defers to the ** C library fgets() when input is not from the console. Newline -** translation may be done as set by set{Binary,Text}Mode(). +** translation may be done as set by set{Binary,Text}Mode(). As a +** convenience, pfIn==NULL is treated as stdin. */ -INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn); +INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn); /* ** Set given stream for binary mode, where newline translation is @@ -100,8 +111,9 @@ INT_LINKAGE int fgetsUtf8(char *buf, int ncMax, FILE *pfIn); ** An additional side-effect is that if the stream is one passed ** to consoleClassifySetup() as an output, it is flushed first. ** -** Note that binary/text mode has no effect on console output -** translation. Newline chars start a new line on all platforms. +** Note that binary/text mode has no effect on console I/O +** translation. On all platforms, newline to the console starts +** a new line and CR,LF chars from the console become a newline. */ INT_LINKAGE void setBinaryMode(FILE *, short bFlush); INT_LINKAGE void setTextMode(FILE *, short bFlush); From 8dd4697e48c04a405c9c8da4a68b948e45b1f159 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 5 Nov 2023 23:55:41 +0000 Subject: [PATCH 122/347] Make it buildable. Pull test program from testee. Zap stray fprintf(). FossilOrigin-Name: 2b850aca1e76805a0358064318a765fa66ce394d015936fd47683d74ca4c187e --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/console_io.c | 38 -------------------------------------- src/console_io.h | 6 +++++- 4 files changed, 13 insertions(+), 47 deletions(-) diff --git a/manifest b/manifest index da6d8f4914..ffa06de45f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Input\sworking.\sNo\sline-editor\sprovisions\syet.\s(WIP,\sbut\ssuitable\sfor\stesting.) -D 2023-11-05T19:42:00.134 +C Make\sit\sbuildable.\sPull\stest\sprogram\sfrom\stestee.\sZap\sstray\sfprintf(). +D 2023-11-05T23:55:41.214 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,8 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c a04f62d1930d32f0bda26b546e9b3d6455e7caaee4ea7624be4b1ef7ea2329b7 x -F src/console_io.h 49680984d2a121697ccb2620f9e8465393d61c3edc3d5720c77dfa0a1f4a7389 +F src/console_io.c 5ff9be9911d73bdf89d33b57db282e1777d2fc906c3e1c573d2e90adfb85393b x +F src/console_io.h 88b5376f02550b03a0d0f44e71fe6b8dccc025b33f7822780fed9cfe58961fe3 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -2144,8 +2144,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 dfea85be1fb927ea446c9d98bae42ba1197bdab098aa6d95aa512a37d07a1e52 -R 4898fd3a7f002f1e4e422622974a0785 +P e8568b1d925c2118eb08394dd8aa50cfb521240f87668f535ec4a03e67dc9a09 +R 35673b0955f1c798661d8793bf63d1c3 U larrybr -Z c9314460b6b28a6cd16041af96151d83 +Z beec5bac02182bb7b20a99bcb3d7ba4e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c3c4b37173..58ba99cb96 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e8568b1d925c2118eb08394dd8aa50cfb521240f87668f535ec4a03e67dc9a09 \ No newline at end of file +2b850aca1e76805a0358064318a765fa66ce394d015936fd47683d74ca4c187e \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index 991479e0f8..3352395ee1 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -133,7 +133,6 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ consoleInfo.outputIx |= ix; } SetConsoleMode(ppst->hx, cm); - fprintf(stderr, "consMode[%d]: %02x -> %02x\n", ix, ppst->consMode, cm); #endif rv |= (CSCS_InConsole< 0 ){ int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); @@ -314,39 +312,3 @@ INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ } #endif } - -#ifdef TEST_CIO -// cl -Zi -I. -DWIN32 -DTEST_CIO sqlite3.c src/console_io.c -Fecio.exe -// gcc -I. -DWIN32 -DTEST_CIO sqlite3.c src/console_io.c -o cio.exe -const char *prompts[] = { "main", "cont" }; -Prompts goofy = { 2, prompts }; - -int main(int na, char *av[]){ - ConsoleStdConsStreams cc = consoleClassifySetup(stdin, stdout, stderr); - const char *zt = "Math: ±×÷∂∆∙√∞∩∫≈≠≡≤≥\n" - "Hiragana: 亜唖娃阿哀愛挨姶逢葵茜穐悪握渥旭葦芦鯵梓圧斡扱" - "宛姐虻飴絢綾鮎或粟袷安庵按暗案闇鞍杏\n" - "Simplified Chinese: 餐参蚕残惭惨灿掺孱骖璨粲黪\n" - "Geometric Shapes: ■□▪▫▲△▼▽◆◇◊○◌◎●◢◣◤◥◦\n" - "Boxes single: ─━│┃┄┅┆┇┈┉┊┋ ┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳" - "┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋\n" - "Boxes double: ═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬\n" - "Rounded corners and diagonals: ╭╮╯╰╱╲╳\n" - ; - char inBuf[150]; - setTextMode(stdout, 1); - setTextMode(stderr, 1); - fprintfUtf8(stderr, "Console streams: %d, CP_UTF8 valid: %d\n", cc, - IsValidCodePage(CP_UTF8)); - fprintfUtf8(stdout, "%s=%d\n", "∑(1st 7 primes)", 42); - fprintfUtf8(stderr, "%s\n", "∫ (1/x) dx ≡ ln(x)"); - fprintfUtf8(stdout, "%s", zt); - fprintfUtf8(stderr, "Entering input/echo loop." - " Type or copy/paste, or EOF to exit.\n"); - while( fprintfUtf8(stdout,"? ") && fgetsUtf8(inBuf, sizeof(inBuf), stdin) ){ - fprintfUtf8(stdout, "! %s", inBuf); - } - consoleRestore(); - return 0; -} -#endif /* defined(TEST_CIO) */ diff --git a/src/console_io.h b/src/console_io.h index 6f838427de..2178eee539 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -25,7 +25,7 @@ */ #ifndef INT_LINKAGE -# define INT_LINKAGE /* Linkage will be external to translation unit. */ +# define INT_LINKAGE extern /* Linkage external to translation unit. */ # include # include # include @@ -38,6 +38,10 @@ # endif #endif +#ifndef SQLITE3_H +# include "sqlite3.h" +#endif + /* Define enum for use with following function. */ typedef enum ConsoleStdConsStreams { CSCS_NoConsole = 0, From a0cd392979418e277401c88fcb4493283a4224c2 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 6 Nov 2023 00:15:14 +0000 Subject: [PATCH 123/347] Make MSVC accept it, too. (Cannot use static const int as part of a constant expression?) FossilOrigin-Name: bb278d2496b27d2e2ee3cedd6fc54394e71ab2ba5d3d51593f97e897b8b306a4 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/console_io.c | 8 +++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index ffa06de45f..c7f8c4dc5e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sit\sbuildable.\sPull\stest\sprogram\sfrom\stestee.\sZap\sstray\sfprintf(). -D 2023-11-05T23:55:41.214 +C Make\sMSVC\saccept\sit,\stoo.\s(Cannot\suse\sstatic\sconst\sint\sas\spart\sof\sa\sconstant\sexpression?) +D 2023-11-06T00:15:14.079 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,7 +669,7 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c 5ff9be9911d73bdf89d33b57db282e1777d2fc906c3e1c573d2e90adfb85393b x +F src/console_io.c f1d63f8fd12236fb2e7d57dee3fd4f068c881649b267e716804da84ae1672b31 x F src/console_io.h 88b5376f02550b03a0d0f44e71fe6b8dccc025b33f7822780fed9cfe58961fe3 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 @@ -2144,8 +2144,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 e8568b1d925c2118eb08394dd8aa50cfb521240f87668f535ec4a03e67dc9a09 -R 35673b0955f1c798661d8793bf63d1c3 +P 2b850aca1e76805a0358064318a765fa66ce394d015936fd47683d74ca4c187e +R 93fbedf6374d1b71d34353b4143eaca1 U larrybr -Z beec5bac02182bb7b20a99bcb3d7ba4e +Z f691c4a528e1fad9be92f83414b324ea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 58ba99cb96..9470d0e102 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2b850aca1e76805a0358064318a765fa66ce394d015936fd47683d74ca4c187e \ No newline at end of file +bb278d2496b27d2e2ee3cedd6fc54394e71ab2ba5d3d51593f97e897b8b306a4 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index 3352395ee1..c137fea245 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -249,18 +249,20 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ return rv; } + INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; #if SHELL_CON_TRANSLATE if( pfIn == consoleInfo.pst[0].pf ){ - static const int nwcLen = 150; - WCHAR wcBuf[nwcLen+1]; +#define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ + WCHAR wcBuf[SHELL_GULP+1]; int lend = 0, noc = 0; if( consoleInfo.stdinEof ) return 0; if( ncMax > 0 ) cBuf[0] = 0; while( noc < ncMax-8-1 && !lend ){ /* There is room for at least 2 more characters and a 0-terminator. */ - int na = (ncMax > nwcLen*4+1 + noc)? nwcLen : (ncMax-1 - noc)/4; + int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; +#undef SHELL_GULP DWORD nbr = 0; BOOL bRC = ReadConsoleW(consoleInfo.pst[0].hx, wcBuf, na, &nbr, 0); if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ From e3a6a60901ade052547e483a6002e2d626fc8877 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 6 Nov 2023 03:09:10 +0000 Subject: [PATCH 124/347] Cause CLI to use console_io library. FossilOrigin-Name: bf66a7c1d330b04fd3c3e50f43ebe1e41307db59d7189798acafb3de77e0c8b2 --- manifest | 16 +- manifest.uuid | 2 +- src/console_io.c | 16 +- src/console_io.h | 2 + src/shell.c.in | 762 +++++++++++++++-------------------------------- 5 files changed, 254 insertions(+), 544 deletions(-) diff --git a/manifest b/manifest index c7f8c4dc5e..f339a104d0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sMSVC\saccept\sit,\stoo.\s(Cannot\suse\sstatic\sconst\sint\sas\spart\sof\sa\sconstant\sexpression?) -D 2023-11-06T00:15:14.079 +C Cause\sCLI\sto\suse\sconsole_io\slibrary. +D 2023-11-06T03:09:10.820 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,8 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c f1d63f8fd12236fb2e7d57dee3fd4f068c881649b267e716804da84ae1672b31 x -F src/console_io.h 88b5376f02550b03a0d0f44e71fe6b8dccc025b33f7822780fed9cfe58961fe3 +F src/console_io.c 54dfcb0c76e946a1b96d819e4f4b64b16e7c5e6a12b002206531b487ec0af5a0 x +F src/console_io.h df286a858032fb626a7eb339ce0ab5d50b5387c2075536f602f9625d73215e7e F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 +F src/shell.c.in 86d63996bc534872d20978b1a85d1f8bb20c84d4087c4abae2e7a35f840b93a9 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 2b850aca1e76805a0358064318a765fa66ce394d015936fd47683d74ca4c187e -R 93fbedf6374d1b71d34353b4143eaca1 +P bb278d2496b27d2e2ee3cedd6fc54394e71ab2ba5d3d51593f97e897b8b306a4 +R 1fc68e9eabc68f8f7161c78d8df5cb64 U larrybr -Z f691c4a528e1fad9be92f83414b324ea +Z ceef29f2dca26d535bb75e96dff32841 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9470d0e102..9e8e1296e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb278d2496b27d2e2ee3cedd6fc54394e71ab2ba5d3d51593f97e897b8b306a4 \ No newline at end of file +bf66a7c1d330b04fd3c3e50f43ebe1e41307db59d7189798acafb3de77e0c8b2 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index c137fea245..fed9142cfb 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -17,21 +17,27 @@ # define SQLITE_CDECL #endif +#ifndef SHELL_NO_SYSINC #include #include #include "console_io.h" #include "sqlite3.h" +#endif #if defined(_WIN32) || defined(WIN32) -# include -# include +# ifndef SHELL_NO_SYSINC +# include +# include +# endif # ifdef SHELL_LEGACY_CONSOLE_IO # define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */ # else # define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ # endif #else -# include +# ifndef SHELL_NO_SYSINC +# include +# endif # define SHELL_CON_TRANSLATE 0 /* Use plain C library stream I/O at console */ #endif @@ -215,7 +221,7 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ /* Legacy translation to active code page, then MBCS chars out. */ char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); if( z2!=NULL ){ - rv = strlen(z2); + rv = (int)strlen(z2); vfprintf(pfO, "%s", z2); sqlite3_free(z2); } @@ -224,7 +230,7 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ if( z1!=NULL ){ int nwc; WCHAR *zw2 = 0; - rv = strlen(z1); + rv = (int)strlen(z1); nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,0,0); if( nwc>0 ){ zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); diff --git a/src/console_io.h b/src/console_io.h index 2178eee539..6c3e16cd75 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -36,6 +36,8 @@ # include # include # endif +#else +# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ #endif #ifndef SQLITE3_H diff --git a/src/shell.c.in b/src/shell.c.in index 3cfe1a94a4..f05234b3ef 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -234,30 +234,13 @@ typedef unsigned char u8; /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); -extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); -extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif -/* On Windows, we normally run with output mode of TEXT so that \n characters -** are automatically translated into \r\n. However, this behavior needs -** to be disabled in some cases (ex: when generating CSV output and when -** rendering quoted strings that contain \n characters). The following -** routines take care of that. -*/ -#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT -static void setBinaryMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_BINARY); -} -static void setTextMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_TEXT); -} -#else -# define setBinaryMode(X,Y) -# define setTextMode(X,Y) -#endif +#define INT_LINKAGE static + +INCLUDE console_io.h +INCLUDE console_io.c /* True if the timer is enabled */ static int enableTimer = 0; @@ -449,25 +432,6 @@ static int bail_on_error = 0; */ static int stdin_is_interactive = 1; -/* -** If build is for non-RT Windows, without 3rd-party line editing, -** console input and output may be done in a UTF-8 compatible way, -** if the OS is capable of it and the --no-utf8 option is not seen. -*/ -#if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \ - && !defined(SHELL_OMIT_WIN_UTF8) && !SQLITE_OS_WINRT -# define SHELL_WIN_UTF8_OPT 1 -/* Record whether to do UTF-8 console I/O translation per stream. */ - static int console_utf8_in = 0; - static int console_utf8_out = 0; -/* Record whether can do UTF-8 or --no-utf8 seen in invocation. */ - static int mbcs_opted = 1; /* Assume cannot do until shown otherwise. */ -#else -# define console_utf8_in 0 -# define console_utf8_out 0 -# define SHELL_WIN_UTF8_OPT 0 -#endif - /* ** On Windows systems we have to know if standard output is a console ** in order to translate UTF-8 into MBCS. The following variable is @@ -600,243 +564,14 @@ static char *dynamicContinuePrompt(void){ } #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ -#if SHELL_WIN_UTF8_OPT -/* Following struct is used for UTF-8 console I/O. */ -static struct ConsoleState { - int stdinEof; /* EOF has been seen on console input */ - int infsMode; /* Input file stream mode upon shell start */ - UINT inCodePage; /* Input code page upon shell start */ - UINT outCodePage; /* Output code page upon shell start */ - HANDLE hConsole; /* Console input or output handle */ - DWORD consoleMode; /* Console mode upon shell start */ -} conState = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 }; - -#ifndef _O_U16TEXT /* For build environments lacking this constant: */ -# define _O_U16TEXT 0x20000 -#endif - -/* -** If given stream number is a console, return 1 and get some attributes, -** else return 0 and set the output attributes to invalid values. -*/ -static short console_attrs(unsigned stnum, HANDLE *pH, DWORD *pConsMode){ - static int stid[3] = { STD_INPUT_HANDLE,STD_OUTPUT_HANDLE,STD_ERROR_HANDLE }; - HANDLE h; - *pH = INVALID_HANDLE_VALUE; - *pConsMode = 0; - if( stnum > 2 ) return 0; - h = GetStdHandle(stid[stnum]); - if( h!=*pH && GetFileType(h)==FILE_TYPE_CHAR && GetConsoleMode(h,pConsMode) ){ - *pH = h; - return 1; - } - return 0; -} - -/* -** Perform a runtime test of Windows console to determine if it can -** do char-stream I/O correctly when the code page is set to CP_UTF8. -** Returns are: 1 => yes it can, 0 => no it cannot -** -** The console's output code page is momentarily set, then restored. -** So this should only be run when the process is given use of the -** console for either input or output. -*/ -static short ConsoleDoesUTF8(void){ - UINT ocp = GetConsoleOutputCP(); - const char TrialUtf8[] = { '\xC8', '\xAB' }; /* "ȫ" or 2 MBCS characters */ - WCHAR aReadBack[1] = { 0 }; /* Read back as 0x022B when decoded as UTF-8. */ - CONSOLE_SCREEN_BUFFER_INFO csbInfo = {0}; - /* Create an inactive screen buffer with which to do the experiment. */ - HANDLE hCSB = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE, 0, 0, - CONSOLE_TEXTMODE_BUFFER, NULL); - if( hCSB!=INVALID_HANDLE_VALUE ){ - COORD cpos = {0,0}; - DWORD rbc; - SetConsoleCursorPosition(hCSB, cpos); - SetConsoleOutputCP(CP_UTF8); - /* Write 2 chars which are a single character in UTF-8 but more in MBCS. */ - WriteConsoleA(hCSB, TrialUtf8, sizeof(TrialUtf8), NULL, NULL); - ReadConsoleOutputCharacterW(hCSB, &aReadBack[0], 1, cpos, &rbc); - GetConsoleScreenBufferInfo(hCSB, &csbInfo); - SetConsoleOutputCP(ocp); - CloseHandle(hCSB); - } - /* Return 1 if cursor advanced by 1 position, else 0. */ - return (short)(csbInfo.dwCursorPosition.X == 1 && aReadBack[0] == 0x022B); -} - -static short in_console = 0; -static short out_console = 0; - -/* -** Determine whether either normal I/O stream is the console, -** and whether it can do UTF-8 translation, setting globals -** in_console, out_console and mbcs_opted accordingly. -*/ -static void probe_console(void){ - HANDLE h; - DWORD cMode; - in_console = console_attrs(0, &h, &cMode); - out_console = console_attrs(1, &h, &cMode); - if( in_console || out_console ) mbcs_opted = !ConsoleDoesUTF8(); -} - -/* -** If console is used for normal I/O, absent a --no-utf8 option, -** prepare console for UTF-8 input (from either typing or suitable -** paste operations) and/or for UTF-8 output rendering. -** -** The console state upon entry is preserved, in conState, so that -** console_restore() can later restore the same console state. -** -** The globals console_utf8_in and console_utf8_out are set, for -** later use in selecting UTF-8 or MBCS console I/O translations. -** This routine depends upon globals set by probe_console(). -*/ -static void console_prepare_utf8(void){ - struct ConsoleState csWork = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 }; - - console_utf8_in = console_utf8_out = 0; - if( (!in_console && !out_console) || mbcs_opted ) return; - console_attrs((in_console)? 0 : 1, &conState.hConsole, &conState.consoleMode); - conState.inCodePage = GetConsoleCP(); - conState.outCodePage = GetConsoleOutputCP(); - if( in_console ){ - SetConsoleCP(CP_UTF8); - SetConsoleMode(conState.hConsole, conState.consoleMode - | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); - conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT); - console_utf8_in = 1; - } - if( out_console ){ - SetConsoleOutputCP(CP_UTF8); - console_utf8_out = 1; - } -} - -/* -** Undo the effects of console_prepare_utf8(), if any. -*/ -static void SQLITE_CDECL console_restore(void){ - if( (console_utf8_in||console_utf8_out) - && conState.hConsole!=INVALID_HANDLE_VALUE ){ - if( console_utf8_in ){ - SetConsoleCP(conState.inCodePage); - _setmode(_fileno(stdin), conState.infsMode); - } - if( console_utf8_out ) SetConsoleOutputCP(conState.outCodePage); - SetConsoleMode(conState.hConsole, conState.consoleMode); - /* Avoid multiple calls. */ - conState.hConsole = INVALID_HANDLE_VALUE; - conState.consoleMode = 0; - console_utf8_in = 0; - console_utf8_out = 0; - } -} - -/* -** Collect input like fgets(...) with special provisions for input -** from the Windows console to get around its strange coding issues. -** Defers to plain fgets() when input is not interactive or when the -** UTF-8 input is unavailable or opted out. -*/ -static char* utf8_fgets(char *buf, int ncmax, FILE *fin){ - if( fin==0 ) fin = stdin; - if( fin==stdin && stdin_is_interactive && console_utf8_in ){ -# define SQLITE_IALIM 150 - wchar_t wbuf[SQLITE_IALIM]; - int lend = 0; - int noc = 0; - if( ncmax==0 || conState.stdinEof ) return 0; - buf[0] = 0; - while( noc SQLITE_IALIM*4+1 + noc) - ? SQLITE_IALIM : (ncmax-1 - noc)/4; -# undef SQLITE_IALIM - DWORD nbr = 0; - BOOL bRC = ReadConsoleW(conState.hConsole, wbuf, na, &nbr, 0); - if( !bRC || (noc==0 && nbr==0) ) return 0; - if( nbr > 0 ){ - int nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, - wbuf,nbr,0,0,0,0); - if( nmb !=0 && noc+nmb <= ncmax ){ - int iseg = noc; - nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, - wbuf,nbr,buf+noc,nmb,0,0); - noc += nmb; - /* Fixup line-ends as coded by Windows for CR (or "Enter".)*/ - if( noc > 0 ){ - if( buf[noc-1]=='\n' ){ - lend = 1; - if( noc > 1 && buf[noc-2]=='\r' ){ - buf[noc-2] = '\n'; - --noc; - } - } - } - /* Check for ^Z (anywhere in line) too. */ - while( iseg < noc ){ - if( buf[iseg]==0x1a ){ - conState.stdinEof = 1; - noc = iseg; /* Chop ^Z and anything following. */ - break; - } - ++iseg; - } - }else break; /* Drop apparent garbage in. (Could assert.) */ - }else break; - } - /* If got nothing, (after ^Z chop), must be at end-of-file. */ - if( noc == 0 ) return 0; - buf[noc] = 0; - return buf; - }else{ - return fgets(buf, ncmax, fin); - } -} - -# define fgets(b,n,f) utf8_fgets(b,n,f) -#endif /* SHELL_WIN_UTF8_OPT */ - -/* -** Render output like fprintf(). Except, if the output is going to the -** console and if this is running on a Windows machine, and if UTF-8 -** output unavailable (or available but opted out), translate the -** output from UTF-8 into MBCS for output through 8-bit stdout stream. -** (Without -no-utf8, no translation is needed and must not be done.) -*/ -#if defined(_WIN32) || defined(WIN32) -void utf8_printf(FILE *out, const char *zFormat, ...){ - va_list ap; - va_start(ap, zFormat); - if( stdout_is_console && (out==stdout || out==stderr) && !console_utf8_out ){ - char *z1 = sqlite3_vmprintf(zFormat, ap); - char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); - sqlite3_free(z1); - fputs(z2, out); - sqlite3_free(z2); - }else{ - vfprintf(out, zFormat, ap); - } - va_end(ap); -} -#elif !defined(utf8_printf) -# define utf8_printf fprintf -#endif - -/* -** Render output like fprintf(). This should not be used on anything that -** includes string formatting (e.g. "%s"). -*/ -#if !defined(raw_printf) -# define raw_printf fprintf -#endif +/* From here onward, fgets() is redirected to the console_io library. */ +#define fgets(b,n,f) fgetsUtf8(b,n,f) +/* And, (varargs) utf8_printf(f,z,...) is redirected to the same. */ +#define utf8_printf fprintfUtf8 /* Indicate out-of-memory and exit. */ static void shell_out_of_memory(void){ - raw_printf(stderr,"Error: out of memory\n"); + utf8_printf(stderr,"Error: out of memory\n"); exit(1); } @@ -1021,23 +756,6 @@ static char *local_getline(char *zLine, FILE *in){ break; } } -#if defined(_WIN32) || defined(WIN32) - /* For interactive input on Windows systems, with -no-utf8, - ** translate the multi-byte characterset characters into UTF-8. - ** This is the translation that predates console UTF-8 input. */ - if( stdin_is_interactive && in==stdin && !console_utf8_in ){ - char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); - if( zTrans ){ - i64 nTrans = strlen(zTrans)+1; - if( nTrans>nLine ){ - zLine = realloc(zLine, nTrans); - shell_check_oom(zLine); - } - memcpy(zLine, zTrans, nTrans); - sqlite3_free(zTrans); - } - } -#endif /* defined(_WIN32) || defined(WIN32) */ return zLine; } @@ -1761,7 +1479,7 @@ static void failIfSafeMode( va_start(ap, zErrMsg); zMsg = sqlite3_vmprintf(zErrMsg, ap); va_end(ap); - raw_printf(stderr, "line %d: ", p->lineno); + utf8_printf(stderr, "line %d: ", p->lineno); utf8_printf(stderr, "%s\n", zMsg); exit(1); } @@ -1947,7 +1665,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ } zStr[i*2] = '\0'; - raw_printf(out,"X'%s'", zStr); + utf8_printf(out,"X'%s'", zStr); sqlite3_free(zStr); } @@ -1986,7 +1704,7 @@ static void output_quoted_string(FILE *out, const char *z){ if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ - raw_printf(out, "'"); + utf8_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; @@ -1995,7 +1713,7 @@ static void output_quoted_string(FILE *out, const char *z){ z += i; } if( c=='\'' ){ - raw_printf(out, "'"); + utf8_printf(out, "'"); continue; } if( c==0 ){ @@ -2003,7 +1721,7 @@ static void output_quoted_string(FILE *out, const char *z){ } z++; } - raw_printf(out, "'"); + utf8_printf(out, "'"); } setTextMode(out, 1); } @@ -2035,14 +1753,14 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ if( z[i]=='\r' ) nCR++; } if( nNL ){ - raw_printf(out, "replace("); + utf8_printf(out, "replace("); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ - raw_printf(out, "replace("); + utf8_printf(out, "replace("); zCR = unused_string(z, "\\r", "\\015", zBuf2); } - raw_printf(out, "'"); + utf8_printf(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; @@ -2051,7 +1769,7 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ z += i; } if( c=='\'' ){ - raw_printf(out, "'"); + utf8_printf(out, "'"); continue; } if( c==0 ){ @@ -2059,17 +1777,17 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ } z++; if( c=='\n' ){ - raw_printf(out, "%s", zNL); + utf8_printf(out, "%s", zNL); continue; } - raw_printf(out, "%s", zCR); + utf8_printf(out, "%s", zCR); } - raw_printf(out, "'"); + utf8_printf(out, "'"); if( nCR ){ - raw_printf(out, ",'%s',char(13))", zCR); + utf8_printf(out, ",'%s',char(13))", zCR); } if( nNL ){ - raw_printf(out, ",'%s',char(10))", zNL); + utf8_printf(out, ",'%s',char(10))", zNL); } } setTextMode(out, 1); @@ -2098,7 +1816,7 @@ static void output_c_string(FILE *out, const char *z){ fputc('\\', out); fputc('r', out); }else if( !isprint(c&0xff) ){ - raw_printf(out, "\\%03o", c&0xff); + utf8_printf(out, "\\%03o", c&0xff); }else{ fputc(c, out); } @@ -2132,7 +1850,7 @@ static void output_json_string(FILE *out, const char *z, i64 n){ }else if( c=='\t' ){ fputc('t', out); }else{ - raw_printf(out, "u%04x",c); + utf8_printf(out, "u%04x",c); } }else{ fputc(c, out); @@ -2160,15 +1878,15 @@ static void output_html_string(FILE *out, const char *z){ utf8_printf(out,"%.*s",i,z); } if( z[i]=='<' ){ - raw_printf(out,"<"); + utf8_printf(out,"<"); }else if( z[i]=='&' ){ - raw_printf(out,"&"); + utf8_printf(out,"&"); }else if( z[i]=='>' ){ - raw_printf(out,">"); + utf8_printf(out,">"); }else if( z[i]=='\"' ){ - raw_printf(out,"""); + utf8_printf(out,"""); }else if( z[i]=='\'' ){ - raw_printf(out,"'"); + utf8_printf(out,"'"); }else{ break; } @@ -2337,14 +2055,14 @@ static int shellAuth( az[3] = zA4; utf8_printf(p->out, "authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - raw_printf(p->out, " "); + utf8_printf(p->out, " "); if( az[i] ){ output_c_string(p->out, az[i]); }else{ - raw_printf(p->out, "NULL"); + utf8_printf(p->out, "NULL"); } } - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } @@ -2510,13 +2228,13 @@ static int progress_handler(void *pClientData) { ShellState *p = (ShellState*)pClientData; p->nProgress++; if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); + utf8_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; return 1; } if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - raw_printf(p->out, "Progress %u\n", p->nProgress); + utf8_printf(p->out, "Progress %u\n", p->nProgress); } return 0; } @@ -2532,7 +2250,7 @@ static void print_dashes(FILE *out, int N){ fputs(zDash, out); N -= nDash; } - raw_printf(out, "%.*s", N, zDash); + utf8_printf(out, "%.*s", N, zDash); } /* @@ -2737,22 +2455,22 @@ static int shell_callback( } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ - raw_printf(p->out,""); + utf8_printf(p->out,""); for(i=0; iout,""); + utf8_printf(p->out,""); output_html_string(p->out, azCol[i]); - raw_printf(p->out,"\n"); + utf8_printf(p->out,"\n"); } - raw_printf(p->out,"\n"); + utf8_printf(p->out,"\n"); } if( azArg==0 ) break; - raw_printf(p->out,""); + utf8_printf(p->out,""); for(i=0; iout,""); + utf8_printf(p->out,""); output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - raw_printf(p->out,"\n"); + utf8_printf(p->out,"\n"); } - raw_printf(p->out,"\n"); + utf8_printf(p->out,"\n"); break; } case MODE_Tcl: { @@ -2792,23 +2510,23 @@ static int shell_callback( if( azArg==0 ) break; utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->showHeader ){ - raw_printf(p->out,"("); + utf8_printf(p->out,"("); for(i=0; i0 ) raw_printf(p->out, ","); + if( i>0 ) utf8_printf(p->out, ","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); utf8_printf(p->out, "%s", z); sqlite3_free(z); }else{ - raw_printf(p->out, "%s", azCol[i]); + utf8_printf(p->out, "%s", azCol[i]); } } - raw_printf(p->out,")"); + utf8_printf(p->out,")"); } p->cnt++; for(i=0; iout, i>0 ? "," : " VALUES("); + utf8_printf(p->out, i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ @@ -2825,9 +2543,9 @@ static int shell_callback( sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "9.0e+999"); + utf8_printf(p->out, "9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-9.0e+999"); + utf8_printf(p->out, "-9.0e+999"); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -2835,7 +2553,7 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - raw_printf(p->out, "%s", z); + utf8_printf(p->out, "%s", z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); @@ -2849,7 +2567,7 @@ static int shell_callback( output_quoted_escaped_string(p->out, azArg[i]); } } - raw_printf(p->out,");\n"); + utf8_printf(p->out,");\n"); break; } case MODE_Json: { @@ -2871,12 +2589,12 @@ static int shell_callback( sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "9.0e+999"); + utf8_printf(p->out, "9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-9.0e+999"); + utf8_printf(p->out, "-9.0e+999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); + utf8_printf(p->out, "%s", z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); @@ -2916,7 +2634,7 @@ static int shell_callback( char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); + utf8_printf(p->out, "%s", z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); @@ -3142,9 +2860,9 @@ static int run_table_dump_query( if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - raw_printf(p->out, "\n;\n"); + utf8_printf(p->out, "\n;\n"); }else{ - raw_printf(p->out, ";\n"); + utf8_printf(p->out, ";\n"); } rc = sqlite3_step(pSelect); } @@ -3242,7 +2960,7 @@ static void displayStatLine( }else{ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); } - raw_printf(p->out, "%-36s %s\n", zLabel, zLine); + utf8_printf(p->out, "%-36s %s\n", zLabel, zLine); } /* @@ -3264,7 +2982,7 @@ static int display_stats( sqlite3_stmt *pStmt = pArg->pStmt; char z[100]; nCol = sqlite3_column_count(pStmt); - raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); + utf8_printf(out, "%-36s %d\n", "Number of output columns:", nCol); for(i=0; istatsOn==3 ){ if( pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); - raw_printf(pArg->out, "VM-steps: %d\n", iCur); + utf8_printf(pArg->out, "VM-steps: %d\n", iCur); } return 0; } @@ -3315,45 +3033,45 @@ static int display_stats( iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, + utf8_printf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Successful lookaside attempts: %d\n", + utf8_printf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to size: %d\n", + utf8_printf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n", + utf8_printf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n", + utf8_printf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache hits: %d\n", iCur); + utf8_printf(pArg->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache misses: %d\n", iCur); + utf8_printf(pArg->out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache writes: %d\n", iCur); + utf8_printf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache spills: %d\n", iCur); + utf8_printf(pArg->out, "Page cache spills: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", + utf8_printf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", + utf8_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } @@ -3361,27 +3079,27 @@ static int display_stats( int iHit, iMiss; iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); - raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); + utf8_printf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - raw_printf(pArg->out, "Sort Operations: %d\n", iCur); + utf8_printf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); + utf8_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); if( iHit || iMiss ){ - raw_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n", + utf8_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); } iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); + utf8_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); + utf8_printf(pArg->out, "Reprepare operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - raw_printf(pArg->out, "Number of times run: %d\n", iCur); + utf8_printf(pArg->out, "Number of times run: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); + utf8_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ @@ -4132,7 +3850,7 @@ static void exec_prepared_stmt_columnar( }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); }else if( p->cMode==MODE_Column ){ - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); } } j = -1; @@ -4297,8 +4015,8 @@ static int expertFinish( if( bVerbose ){ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); - raw_printf(out, "-- Candidates -----------------------------\n"); - raw_printf(out, "%s\n", zCand); + utf8_printf(out, "-- Candidates -----------------------------\n"); + utf8_printf(out, "%s\n", zCand); } for(i=0; i=2 && 0==cli_strncmp(z, "-sample", n) ){ if( i==(nArg-1) ){ - raw_printf(stderr, "option requires an argument: %s\n", z); + utf8_printf(stderr, "option requires an argument: %s\n", z); rc = SQLITE_ERROR; }else{ iSample = (int)integerValue(azArg[++i]); if( iSample<0 || iSample>100 ){ - raw_printf(stderr, "value out of range: %s\n", azArg[i]); + utf8_printf(stderr, "value out of range: %s\n", azArg[i]); rc = SQLITE_ERROR; } } } else{ - raw_printf(stderr, "unknown option: %s\n", z); + utf8_printf(stderr, "unknown option: %s\n", z); rc = SQLITE_ERROR; } } @@ -4364,7 +4082,7 @@ static int expertDotCommand( if( rc==SQLITE_OK ){ pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); if( pState->expert.pExpert==0 ){ - raw_printf(stderr, "sqlite3_expert_new: %s\n", + utf8_printf(stderr, "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); rc = SQLITE_ERROR; }else{ @@ -4692,9 +4410,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); + if( !dataOnly ) utf8_printf(p->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + if( !dataOnly ) utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ @@ -4702,7 +4420,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); + utf8_printf(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( @@ -4772,7 +4490,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->mode = p->cMode = MODE_Insert; rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + utf8_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); @@ -4803,7 +4521,7 @@ static int run_schema_dump_query( if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + utf8_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); if( zErr ){ utf8_printf(p->out, "/****** %s ******/\n", zErr); sqlite3_free(zErr); @@ -5252,7 +4970,7 @@ static char *readFile(const char *zName, int *pnByte){ if( in==0 ) return 0; rc = fseek(in, 0, SEEK_END); if( rc!=0 ){ - raw_printf(stderr, "Error: '%s' not seekable\n", zName); + utf8_printf(stderr, "Error: '%s' not seekable\n", zName); fclose(in); return 0; } @@ -5260,7 +4978,7 @@ static char *readFile(const char *zName, int *pnByte){ rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ){ - raw_printf(stderr, "Error: out of memory\n"); + utf8_printf(stderr, "Error: out of memory\n"); fclose(in); return 0; } @@ -5268,7 +4986,7 @@ static char *readFile(const char *zName, int *pnByte){ fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); - raw_printf(stderr, "Error: cannot read '%s'\n", zName); + utf8_printf(stderr, "Error: cannot read '%s'\n", zName); return 0; } pBuf[nIn] = 0; @@ -6465,7 +6183,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ memcpy(aHdr, pb, 100); sqlite3_finalize(pStmt); }else{ - raw_printf(stderr, "unable to read database header\n"); + utf8_printf(stderr, "unable to read database header\n"); sqlite3_finalize(pStmt); return 1; } @@ -6481,12 +6199,12 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ utf8_printf(p->out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) raw_printf(p->out, " (utf8)"); - if( val==2 ) raw_printf(p->out, " (utf16le)"); - if( val==3 ) raw_printf(p->out, " (utf16be)"); + if( val==1 ) utf8_printf(p->out, " (utf8)"); + if( val==2 ) utf8_printf(p->out, " (utf16le)"); + if( val==3 ) utf8_printf(p->out, " (utf16be)"); } } - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -6830,7 +6548,7 @@ static int lintFkeyIndexes( zIndent = " "; } else{ - raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", + utf8_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1] ); return SQLITE_ERROR; @@ -6876,22 +6594,22 @@ static int lintFkeyIndexes( if( rc!=SQLITE_OK ) break; if( res<0 ){ - raw_printf(stderr, "Error: internal error"); + utf8_printf(stderr, "Error: internal error"); break; }else{ if( bGroupByParent && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ - raw_printf(out, "-- Parent table %s\n", zParent); + utf8_printf(out, "-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ - raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); + utf8_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ - raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", + utf8_printf(out, "%s/* no extra indexes required for %s -> %s */\n", zIndent, zFrom, zTarget ); } @@ -6900,16 +6618,16 @@ static int lintFkeyIndexes( sqlite3_free(zPrev); if( rc!=SQLITE_OK ){ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + utf8_printf(stderr, "%s\n", sqlite3_errmsg(db)); } rc2 = sqlite3_finalize(pSql); if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ rc = rc2; - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + utf8_printf(stderr, "%s\n", sqlite3_errmsg(db)); } }else{ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + utf8_printf(stderr, "%s\n", sqlite3_errmsg(db)); } return rc; @@ -6929,9 +6647,9 @@ static int lintDotCommand( return lintFkeyIndexes(pState, azArg, nArg); usage: - raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); - raw_printf(stderr, "Where sub-commands are:\n"); - raw_printf(stderr, " fkey-indexes\n"); + utf8_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); + utf8_printf(stderr, "Where sub-commands are:\n"); + utf8_printf(stderr, " fkey-indexes\n"); return SQLITE_ERROR; } @@ -6946,7 +6664,7 @@ static void shellPrepare( if( *pRc==SQLITE_OK ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ - raw_printf(stderr, "sql error: %s (%d)\n", + utf8_printf(stderr, "sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db) ); *pRc = rc; @@ -6999,7 +6717,7 @@ void shellFinalize( int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + utf8_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -7020,7 +6738,7 @@ void shellReset( if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ sqlite3 *db = sqlite3_db_handle(pStmt); - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + utf8_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -7852,7 +7570,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ const char *zErr = sqlite3_recover_errmsg(p); int errCode = sqlite3_recover_errcode(p); - raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode); + utf8_printf(stderr, "sql error: %s (%d)\n", zErr, errCode); } rc = sqlite3_recover_finish(p); return rc; @@ -8130,7 +7848,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_AUTHORIZATION if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ if( nArg!=2 ){ - raw_printf(stderr, "Usage: .auth ON|OFF\n"); + utf8_printf(stderr, "Usage: .auth ON|OFF\n"); rc = 1; goto meta_command_exit; } @@ -8186,12 +7904,12 @@ static int do_meta_command(char *zLine, ShellState *p){ zDb = zDestFile; zDestFile = azArg[j]; }else{ - raw_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); + utf8_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); return 1; } } if( zDestFile==0 ){ - raw_printf(stderr, "missing FILENAME argument on .backup\n"); + utf8_printf(stderr, "missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; @@ -8229,7 +7947,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ - raw_printf(stderr, "Usage: .bail on|off\n"); + utf8_printf(stderr, "Usage: .bail on|off\n"); rc = 1; } }else @@ -8243,9 +7961,9 @@ static int do_meta_command(char *zLine, ShellState *p){ setTextMode(p->out, 1); } }else{ - raw_printf(stderr, "The \".binary\" command is deprecated." + utf8_printf(stderr, "The \".binary\" command is deprecated." " Use \".crnl\" instead.\n"); - raw_printf(stderr, "Usage: .binary on|off\n"); + utf8_printf(stderr, "Usage: .binary on|off\n"); rc = 1; } }else @@ -8273,7 +7991,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; } }else{ - raw_printf(stderr, "Usage: .cd DIRECTORY\n"); + utf8_printf(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else @@ -8283,7 +8001,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ - raw_printf(stderr, "Usage: .changes on|off\n"); + utf8_printf(stderr, "Usage: .changes on|off\n"); rc = 1; } }else @@ -8297,7 +8015,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zRes = 0; output_reset(p); if( nArg!=2 ){ - raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); + utf8_printf(stderr, "Usage: .check GLOB-PATTERN\n"); rc = 2; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ rc = 2; @@ -8320,7 +8038,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ - raw_printf(stderr, "Usage: .clone FILENAME\n"); + utf8_printf(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else @@ -8359,7 +8077,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( i<0 || i>=ArraySize(p->aAuxDb) ){ /* No-op */ }else if( p->pAuxDb == &p->aAuxDb[i] ){ - raw_printf(stderr, "cannot close the active database connection\n"); + utf8_printf(stderr, "cannot close the active database connection\n"); rc = 1; }else if( p->aAuxDb[i].db ){ session_close_all(p, i); @@ -8367,7 +8085,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->aAuxDb[i].db = 0; } }else{ - raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n"); + utf8_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n"); rc = 1; } }else @@ -8381,9 +8099,9 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else{ #if !defined(_WIN32) && !defined(WIN32) - raw_printf(stderr, "The \".crnl\" is a no-op on non-Windows machines.\n"); + utf8_printf(stderr, "The \".crnl\" is a no-op on non-Windows machines.\n"); #endif - raw_printf(stderr, "Usage: .crnl on|off\n"); + utf8_printf(stderr, "Usage: .crnl on|off\n"); rc = 1; } }else @@ -8494,7 +8212,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[0]=='-' ) z++; if( cli_strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE - raw_printf(stderr, "The --preserve-rowids option is not compatible" + utf8_printf(stderr, "The --preserve-rowids option is not compatible" " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; sqlite3_free(zLike); @@ -8513,7 +8231,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ShellSetFlag(p, SHFLG_DumpNoSys); }else { - raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); + utf8_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; sqlite3_free(zLike); goto meta_command_exit; @@ -8547,8 +8265,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(p->out, "BEGIN TRANSACTION;\n"); + utf8_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); + utf8_printf(p->out, "BEGIN TRANSACTION;\n"); } p->writableSchema = 0; p->showHeader = 0; @@ -8579,13 +8297,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_free(zLike); if( p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); + utf8_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + utf8_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); } p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; @@ -8595,7 +8313,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ - raw_printf(stderr, "Usage: .echo on|off\n"); + utf8_printf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else @@ -8626,7 +8344,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ - raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); + utf8_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); rc = 1; } }else @@ -8665,7 +8383,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ if( p->bSafeMode ){ - raw_printf(stderr, + utf8_printf(stderr, "Cannot run experimental commands such as \"%s\" in safe mode\n", azArg[0]); rc = 1; @@ -8819,7 +8537,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( isOk==1 ){ char zBuf[100]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - raw_printf(p->out, "%s\n", zBuf); + utf8_printf(p->out, "%s\n", zBuf); } }else @@ -8834,7 +8552,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nArg = 1; } if( nArg!=1 ){ - raw_printf(stderr, "Usage: .fullschema ?--indent?\n"); + utf8_printf(stderr, "Usage: .fullschema ?--indent?\n"); rc = 1; goto meta_command_exit; } @@ -8860,15 +8578,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( doStats==0 ){ - raw_printf(p->out, "/* No STAT tables available */\n"); + utf8_printf(p->out, "/* No STAT tables available */\n"); }else{ - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); } }else @@ -8877,7 +8595,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->showHeader = booleanValue(azArg[1]); p->shellFlgs |= SHFLG_HeaderSet; }else{ - raw_printf(stderr, "Usage: .headers on|off\n"); + utf8_printf(stderr, "Usage: .headers on|off\n"); rc = 1; } }else @@ -8969,19 +8687,19 @@ static int do_meta_command(char *zLine, ShellState *p){ ** the column and row separator characters from the output mode. */ nSep = strlen30(p->colSeparator); if( nSep==0 ){ - raw_printf(stderr, + utf8_printf(stderr, "Error: non-null column separator required for import\n"); goto meta_command_exit; } if( nSep>1 ){ - raw_printf(stderr, + utf8_printf(stderr, "Error: multi-character column separators not allowed" " for import\n"); goto meta_command_exit; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ - raw_printf(stderr, + utf8_printf(stderr, "Error: non-null row separator required for import\n"); goto meta_command_exit; } @@ -8996,7 +8714,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ - raw_printf(stderr, "Error: multi-character row separators not allowed" + utf8_printf(stderr, "Error: multi-character row separators not allowed" " for import\n"); goto meta_command_exit; } @@ -9007,7 +8725,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); goto meta_command_exit; #else sCtx.in = popen(sCtx.zFile+1, "r"); @@ -9290,13 +9008,13 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); }else{ utf8_printf(stdout, "%s;\n", zSql); - raw_printf(stdout, + utf8_printf(stdout, "WARNING: writing to an imposter table will corrupt the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index" ); } }else{ - raw_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); + utf8_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); rc = 1; } sqlite3_free(zSql); @@ -9352,7 +9070,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_limit(p->db, aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ - raw_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); + utf8_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); rc = 1; goto meta_command_exit; }else{ @@ -9397,7 +9115,7 @@ static int do_meta_command(char *zLine, ShellState *p){ failIfSafeMode(p, "cannot run .load in safe mode"); if( nArg<2 || azArg[1][0]==0 ){ /* Must have a non-empty FILE. (Will not load self.) */ - raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); + utf8_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; goto meta_command_exit; } @@ -9415,7 +9133,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){ if( nArg!=2 ){ - raw_printf(stderr, "Usage: .log FILENAME\n"); + utf8_printf(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; @@ -9423,7 +9141,7 @@ static int do_meta_command(char *zLine, ShellState *p){ && cli_strcmp(zFile,"on")!=0 && cli_strcmp(zFile,"off")!=0 ){ - raw_printf(stdout, "cannot set .log to anything other " + utf8_printf(stdout, "cannot set .log to anything other " "than \"on\" or \"off\"\n"); zFile = "off"; } @@ -9482,14 +9200,14 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - raw_printf + utf8_printf (p->out, "current output mode: %s --wrap %d --wordwrap %s --%squote\n", modeDescr[p->mode], p->cmOpts.iWrap, p->cmOpts.bWordWrap ? "on" : "off", p->cmOpts.bQuote ? "" : "no"); }else{ - raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); + utf8_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); } zMode = modeDescr[p->mode]; } @@ -9548,7 +9266,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( cli_strncmp(zMode,"json",n2)==0 ){ p->mode = MODE_Json; }else{ - raw_printf(stderr, "Error: mode should be one of: " + utf8_printf(stderr, "Error: mode should be one of: " "ascii box column csv html insert json line list markdown " "qbox quote table tabs tcl\n"); rc = 1; @@ -9559,10 +9277,10 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_SHELL_FIDDLE if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ if( nArg!=2 ){ - raw_printf(stderr, "Usage: .nonce NONCE\n"); + utf8_printf(stderr, "Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ - raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", + utf8_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); exit(1); }else{ @@ -9578,7 +9296,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ - raw_printf(stderr, "Usage: .nullvalue STRING\n"); + utf8_printf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; } }else @@ -9764,7 +9482,7 @@ static int do_meta_command(char *zLine, ShellState *p){ shell_check_oom(zFile); if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else @@ -9902,10 +9620,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i1 ) raw_printf(p->out, " "); + if( i>1 ) utf8_printf(p->out, " "); utf8_printf(p->out, "%s", azArg[i]); } - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); }else #ifndef SQLITE_OMIT_PROGRESS_CALLBACK @@ -9975,13 +9693,13 @@ static int do_meta_command(char *zLine, ShellState *p){ int savedLineno = p->lineno; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ - raw_printf(stderr, "Usage: .read FILE\n"); + utf8_printf(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } if( azArg[1][0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else @@ -10022,7 +9740,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zSrcFile = azArg[2]; zDb = azArg[1]; }else{ - raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); + utf8_printf(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } @@ -10050,7 +9768,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - raw_printf(stderr, "Error: source database is busy\n"); + utf8_printf(stderr, "Error: source database is busy\n"); rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); @@ -10075,10 +9793,10 @@ static int do_meta_command(char *zLine, ShellState *p){ p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 ); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS - raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); + utf8_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif }else{ - raw_printf(stderr, "Usage: .scanstats on|off|est\n"); + utf8_printf(stderr, "Usage: .scanstats on|off|est\n"); rc = 1; } }else @@ -10113,7 +9831,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zName==0 ){ zName = azArg[ii]; }else{ - raw_printf(stderr, + utf8_printf(stderr, "Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; @@ -10220,7 +9938,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ - raw_printf(stderr,"Error: querying schema information\n"); + utf8_printf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; @@ -10266,11 +9984,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ){ session_not_open: - raw_printf(stderr, "ERROR: No sessions are open\n"); + utf8_printf(stderr, "ERROR: No sessions are open\n"); }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ - raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); + utf8_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); rc = 0; } } @@ -10305,7 +10023,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ - raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", + utf8_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); @@ -10352,7 +10070,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nByte = sizeof(pSession->azFilter[0])*(nCmd-1); pSession->azFilter = sqlite3_malloc( nByte ); if( pSession->azFilter==0 ){ - raw_printf(stderr, "Error: out or memory\n"); + utf8_printf(stderr, "Error: out or memory\n"); exit(1); } for(ii=1; iinSession>=ArraySize(pAuxDb->aSession) ){ - raw_printf(stderr, + utf8_printf(stderr, "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); goto meta_command_exit; } pSession = &pAuxDb->aSession[pAuxDb->nSession]; rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); if( rc ){ - raw_printf(stderr, "Cannot open session: error code=%d\n", rc); + utf8_printf(stderr, "Cannot open session: error code=%d\n", rc); rc = 0; goto meta_command_exit; } @@ -10484,7 +10202,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); - raw_printf(stderr, "Should be one of: --init -v\n"); + utf8_printf(stderr, "Should be one of: --init -v\n"); rc = 1; goto meta_command_exit; } @@ -10513,7 +10231,7 @@ static int do_meta_command(char *zLine, ShellState *p){ -1, &pStmt, 0); } if( rc ){ - raw_printf(stderr, "Error querying the selftest table\n"); + utf8_printf(stderr, "Error querying the selftest table\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -10570,7 +10288,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ - raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); + utf8_printf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ @@ -10620,7 +10338,7 @@ static int do_meta_command(char *zLine, ShellState *p){ goto meta_command_exit; } }else if( zLike ){ - raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); + utf8_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ @@ -10769,7 +10487,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, x; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( nArg<2 ){ - raw_printf(stderr, "Usage: .system COMMAND\n"); + utf8_printf(stderr, "Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } @@ -10780,7 +10498,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } x = zCmd!=0 ? system(zCmd) : 1; sqlite3_free(zCmd); - if( x ) raw_printf(stderr, "System command returns %d\n", x); + if( x ) utf8_printf(stderr, "System command returns %d\n", x); }else #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ @@ -10789,7 +10507,7 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zOut; int i; if( nArg!=1 ){ - raw_printf(stderr, "Usage: .show\n"); + utf8_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } @@ -10812,15 +10530,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); utf8_printf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); utf8_printf(p->out,"%12.12s: ", "colseparator"); output_c_string(p->out, p->colSeparator); - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); utf8_printf(p->out,"%12.12s: ", "rowseparator"); output_c_string(p->out, p->rowSeparator); - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); switch( p->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; @@ -10830,9 +10548,9 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "%12.12s: %s\n","stats", zOut); utf8_printf(p->out, "%12.12s: ", "width"); for (i=0;inWidth;i++) { - raw_printf(p->out, "%d ", p->colWidth[i]); + utf8_printf(p->out, "%d ", p->colWidth[i]); } - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n", "filename", p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); }else @@ -10849,7 +10567,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ - raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n"); + utf8_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n"); rc = 1; } }else @@ -10875,7 +10593,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ - raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); + utf8_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -10954,7 +10672,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - raw_printf(p->out, "\n"); + utf8_printf(p->out, "\n"); } } @@ -10968,7 +10686,7 @@ static int do_meta_command(char *zLine, ShellState *p){ output_reset(p); p->out = output_file_open("testcase-out.txt", 0); if( p->out==0 ){ - raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); + utf8_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); @@ -11220,9 +10938,9 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ - raw_printf(p->out, "%d\n", rc2); + utf8_printf(p->out, "%d\n", rc2); }else if( isOk==2 ){ - raw_printf(p->out, "0x%08x\n", rc2); + utf8_printf(p->out, "0x%08x\n", rc2); } }else #endif /* !defined(SQLITE_UNTESTABLE) */ @@ -11236,11 +10954,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ enableTimer = booleanValue(azArg[1]); if( enableTimer && !HAS_TIMER ){ - raw_printf(stderr, "Error: timer not available on this system.\n"); + utf8_printf(stderr, "Error: timer not available on this system.\n"); enableTimer = 0; } }else{ - raw_printf(stderr, "Usage: .timer on|off\n"); + utf8_printf(stderr, "Usage: .timer on|off\n"); rc = 1; } }else @@ -11277,7 +10995,7 @@ static int do_meta_command(char *zLine, ShellState *p){ mType |= SQLITE_TRACE_CLOSE; } else { - raw_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z); + utf8_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z); rc = 1; goto meta_command_exit; } @@ -11301,7 +11019,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int lenOpt; char *zOpt; if( nArg<2 ){ - raw_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); + utf8_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); rc = 1; goto meta_command_exit; } @@ -11323,14 +11041,14 @@ static int do_meta_command(char *zLine, ShellState *p){ #if SQLITE_USER_AUTHENTICATION if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ if( nArg<2 ){ - raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); + utf8_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( cli_strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ - raw_printf(stderr, "Usage: .user login USER PASSWORD\n"); + utf8_printf(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } @@ -11342,41 +11060,41 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else if( cli_strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); + utf8_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ - raw_printf(stderr, "User-Add failed: %d\n", rc); + utf8_printf(stderr, "User-Add failed: %d\n", rc); rc = 1; } }else if( cli_strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); + utf8_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ - raw_printf(stderr, "User-Edit failed: %d\n", rc); + utf8_printf(stderr, "User-Edit failed: %d\n", rc); rc = 1; } }else if( cli_strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ - raw_printf(stderr, "Usage: .user delete USER\n"); + utf8_printf(stderr, "Usage: .user delete USER\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_delete(p->db, azArg[2]); if( rc ){ - raw_printf(stderr, "User-Delete failed: %d\n", rc); + utf8_printf(stderr, "User-Delete failed: %d\n", rc); rc = 1; } }else{ - raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); + utf8_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); rc = 1; goto meta_command_exit; } @@ -11410,9 +11128,9 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + utf8_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); + utf8_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + utf8_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); } } }else @@ -11426,11 +11144,11 @@ static int do_meta_command(char *zLine, ShellState *p){ for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, pVfs==pCurrent ? " <--- CURRENT" : ""); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + utf8_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); + utf8_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + utf8_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - raw_printf(p->out, "-----------------------------------\n"); + utf8_printf(p->out, "-----------------------------------\n"); } } }else @@ -11665,7 +11383,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); - raw_printf(p->out, "%s\n", zLineBuf); + utf8_printf(p->out, "%s\n", zLineBuf); } return 0; } @@ -11951,7 +11669,7 @@ static void process_sqliterc( if( sqliterc == NULL ){ home_dir = find_home_dir(0); if( home_dir==0 ){ - raw_printf(stderr, "-- warning: cannot find home directory;" + utf8_printf(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } @@ -12036,9 +11754,6 @@ static const char zOptions[] = " -table set output mode to 'table'\n" " -tabs set output mode to 'tabs'\n" " -unsafe-testing allow unsafe commands and modes for testing\n" -#if SHELL_WIN_UTF8_OPT && 0 /* Option is accepted, but is now the default. */ - " -utf8 setup interactive console code page for UTF-8\n" -#endif " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE @@ -12056,7 +11771,7 @@ static void usage(int showDetail){ if( showDetail ){ utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); }else{ - raw_printf(stderr, "Use the -help option for additional information\n"); + utf8_printf(stderr, "Use the -help option for additional information\n"); } exit(1); } @@ -12162,6 +11877,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ # define data shellState #else ShellState data; + ConsoleStdConsStreams csStreams = CSCS_NoConsole; #endif const char *zInitFile = 0; int i; @@ -12183,12 +11899,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ stdout_is_console = 1; data.wasm.zDefaultDbName = "/fiddle.sqlite3"; #else - stdin_is_interactive = isatty(0); - stdout_is_console = isatty(1); -#endif -#if SHELL_WIN_UTF8_OPT - probe_console(); /* Check for console I/O and UTF-8 capability. */ - if( !mbcs_opted ) atexit(console_restore); + csStreams = consoleClassifySetup(stdin, stdout, stderr); + stdin_is_interactive = (csStreams & CSCS_InConsole)!=0; + stdout_is_console = (csStreams & CSCS_OutConsole)!=0; + atexit(consoleRestore); #endif atexit(sayAbnormalExit); #ifdef SQLITE_DEBUG @@ -12322,14 +12036,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ */ stdin_is_interactive = 0; }else if( cli_strcmp(z,"-utf8")==0 ){ -#if SHELL_WIN_UTF8_OPT - /* Option accepted, but is ignored except for this diagnostic. */ - if( mbcs_opted ) fprintf(stderr, "Cannot do UTF-8 at this console.\n"); -#endif /* SHELL_WIN_UTF8_OPT */ }else if( cli_strcmp(z,"-no-utf8")==0 ){ -#if SHELL_WIN_UTF8_OPT - mbcs_opted = 1; -#endif /* SHELL_WIN_UTF8_OPT */ }else if( cli_strcmp(z,"-heap")==0 ){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) const char *zSize; @@ -12687,7 +12394,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ /* Acted upon in first pass. */ }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); - raw_printf(stderr,"Use -help for a list of options.\n"); + utf8_printf(stderr,"Use -help for a list of options.\n"); return 1; } data.cMode = data.mode; @@ -12729,13 +12436,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char *zHistory; const char *zCharset = ""; int nHistory; -#if SHELL_WIN_UTF8_OPT - switch( console_utf8_in+2*console_utf8_out ){ - default: case 0: break; - case 1: zCharset = " (utf8 in)"; break; - case 2: zCharset = " (utf8 out)"; break; - case 3: zCharset = " (utf8 I/O)"; break; - } +#if SHELL_CON_TRANSLATE + zCharset = " (UTF-16 console I/O)"; #endif printf( "SQLite version %s %.19s%s\n" /*extra-version-info*/ From 56fba47850fad7f22b5d66aa027681f8988bc3c6 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 6 Nov 2023 13:24:07 +0000 Subject: [PATCH 125/347] Add an fputs() equivalent to console I/O lib, and use in CLI. FossilOrigin-Name: d661f90724b1bd31948169841bacb80ba4ae91b52191a0b0021a89a22d932a02 --- manifest | 16 +- manifest.uuid | 2 +- src/console_io.c | 7 + src/console_io.h | 9 ++ src/shell.c.in | 404 ++++++++++++++++++++++++----------------------- 5 files changed, 228 insertions(+), 210 deletions(-) diff --git a/manifest b/manifest index f339a104d0..22d3223f7e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Cause\sCLI\sto\suse\sconsole_io\slibrary. -D 2023-11-06T03:09:10.820 +C Add\san\sfputs()\sequivalent\sto\sconsole\sI/O\slib,\sand\suse\sin\sCLI. +D 2023-11-06T13:24:07.790 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,8 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c 54dfcb0c76e946a1b96d819e4f4b64b16e7c5e6a12b002206531b487ec0af5a0 x -F src/console_io.h df286a858032fb626a7eb339ce0ab5d50b5387c2075536f602f9625d73215e7e +F src/console_io.c 6bc9b7f00dd29873519f650e52de974d811c9c36c3a2914fb65d8764c6fbabeb x +F src/console_io.h cf7a58e120603a16ea15cf5f32a5aa3012ee2def26a610b763009d3b266d15ad F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in 86d63996bc534872d20978b1a85d1f8bb20c84d4087c4abae2e7a35f840b93a9 +F src/shell.c.in 18440bb3296508bc92673c10e4293c97aa11bc3d3a6bb0ed0c16c0c73b6ec558 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 bb278d2496b27d2e2ee3cedd6fc54394e71ab2ba5d3d51593f97e897b8b306a4 -R 1fc68e9eabc68f8f7161c78d8df5cb64 +P bf66a7c1d330b04fd3c3e50f43ebe1e41307db59d7189798acafb3de77e0c8b2 +R 873651c11fd34fdadd7d1e7335c5cb89 U larrybr -Z ceef29f2dca26d535bb75e96dff32841 +Z 556f0a6b391897986638a1109b2be8a2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9e8e1296e2..45db515f6f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bf66a7c1d330b04fd3c3e50f43ebe1e41307db59d7189798acafb3de77e0c8b2 \ No newline at end of file +d661f90724b1bd31948169841bacb80ba4ae91b52191a0b0021a89a22d932a02 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index fed9142cfb..2c62c7049e 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -255,6 +255,13 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ return rv; } +INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ +#if SHELL_CON_TRANSLATE + return fprintfUtf8(pfO, "%s", z); +#else + return fputs(z, pfO); +#endif +} INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; diff --git a/src/console_io.h b/src/console_io.h index 6c3e16cd75..a46e69aa46 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -99,6 +99,15 @@ INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); */ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); +/* +** Render output like fputs(). If the output is going to the +** console and translation from UTF-8 is necessary, perform +** the needed translation. Otherwise, write given text to the +** provided stream almost as-is, possibly with newline +** translation as specified by set{Binary,Text}Mode(). +*/ +INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO); + /* ** Collect input like fgets(...) with special provisions for input ** from the console on platforms that require same. Defers to the diff --git a/src/shell.c.in b/src/shell.c.in index f05234b3ef..5b1275e7b4 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -568,10 +568,12 @@ static char *dynamicContinuePrompt(void){ #define fgets(b,n,f) fgetsUtf8(b,n,f) /* And, (varargs) utf8_printf(f,z,...) is redirected to the same. */ #define utf8_printf fprintfUtf8 +/* And, rfprintf(f,z) is redirected to fputsUtf8(z,f) in the library. */ +#define utf8_print(f,z) fputsUtf8(z,f) /* Indicate out-of-memory and exit. */ static void shell_out_of_memory(void){ - utf8_printf(stderr,"Error: out of memory\n"); + utf8_print(stderr,"Error: out of memory\n"); exit(1); } @@ -1704,7 +1706,7 @@ static void output_quoted_string(FILE *out, const char *z){ if( c==0 ){ utf8_printf(out,"'%s'",z); }else{ - utf8_printf(out, "'"); + utf8_print(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; @@ -1713,7 +1715,7 @@ static void output_quoted_string(FILE *out, const char *z){ z += i; } if( c=='\'' ){ - utf8_printf(out, "'"); + utf8_print(out, "'"); continue; } if( c==0 ){ @@ -1721,7 +1723,7 @@ static void output_quoted_string(FILE *out, const char *z){ } z++; } - utf8_printf(out, "'"); + utf8_print(out, "'"); } setTextMode(out, 1); } @@ -1753,14 +1755,14 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ if( z[i]=='\r' ) nCR++; } if( nNL ){ - utf8_printf(out, "replace("); + utf8_print(out, "replace("); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ - utf8_printf(out, "replace("); + utf8_print(out, "replace("); zCR = unused_string(z, "\\r", "\\015", zBuf2); } - utf8_printf(out, "'"); + utf8_print(out, "'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; @@ -1769,7 +1771,7 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ z += i; } if( c=='\'' ){ - utf8_printf(out, "'"); + utf8_print(out, "'"); continue; } if( c==0 ){ @@ -1777,12 +1779,12 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ } z++; if( c=='\n' ){ - utf8_printf(out, "%s", zNL); + utf8_print(out, zNL); continue; } - utf8_printf(out, "%s", zCR); + utf8_print(out, zCR); } - utf8_printf(out, "'"); + utf8_print(out, "'"); if( nCR ){ utf8_printf(out, ",'%s',char(13))", zCR); } @@ -1878,15 +1880,15 @@ static void output_html_string(FILE *out, const char *z){ utf8_printf(out,"%.*s",i,z); } if( z[i]=='<' ){ - utf8_printf(out,"<"); + utf8_print(out,"<"); }else if( z[i]=='&' ){ - utf8_printf(out,"&"); + utf8_print(out,"&"); }else if( z[i]=='>' ){ - utf8_printf(out,">"); + utf8_print(out,">"); }else if( z[i]=='\"' ){ - utf8_printf(out,"""); + utf8_print(out,"""); }else if( z[i]=='\'' ){ - utf8_printf(out,"'"); + utf8_print(out,"'"); }else{ break; } @@ -1938,14 +1940,14 @@ static void output_csv(ShellState *p, const char *z, int bSep){ if( i==0 || strstr(z, p->colSeparator)!=0 ){ char *zQuoted = sqlite3_mprintf("\"%w\"", z); shell_check_oom(zQuoted); - utf8_printf(out, "%s", zQuoted); + utf8_print(out, zQuoted); sqlite3_free(zQuoted); }else{ - utf8_printf(out, "%s", z); + utf8_print(out, z); } } if( bSep ){ - utf8_printf(p->out, "%s", p->colSeparator); + utf8_print(p->out, p->colSeparator); } } @@ -2055,14 +2057,14 @@ static int shellAuth( az[3] = zA4; utf8_printf(p->out, "authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - utf8_printf(p->out, " "); + utf8_print(p->out, " "); if( az[i] ){ output_c_string(p->out, az[i]); }else{ - utf8_printf(p->out, "NULL"); + utf8_print(p->out, "NULL"); } } - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } @@ -2212,7 +2214,7 @@ static void eqp_render(ShellState *p, i64 nCycle){ }else if( nCycle>0 ){ utf8_printf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); }else{ - utf8_printf(p->out, "QUERY PLAN\n"); + utf8_print(p->out, "QUERY PLAN\n"); } p->sGraph.zPrefix[0] = 0; eqp_render_level(p, 0); @@ -2301,7 +2303,7 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); + if( p->cnt++>0 ) utf8_print(p->out, p->rowSeparator); for(i=0; iout,"%*s = %s%s", w, azCol[i], azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); @@ -2444,49 +2446,49 @@ static int shell_callback( for(i=0; inullValue; - utf8_printf(p->out, "%s", z); + utf8_print(p->out, z); if( iout, "%s", p->colSeparator); + utf8_print(p->out, p->colSeparator); }else{ - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); } } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ - utf8_printf(p->out,""); + utf8_print(p->out,""); for(i=0; iout,""); + utf8_print(p->out,""); output_html_string(p->out, azCol[i]); - utf8_printf(p->out,"\n"); + utf8_print(p->out,"\n"); } - utf8_printf(p->out,"\n"); + utf8_print(p->out,"\n"); } if( azArg==0 ) break; - utf8_printf(p->out,""); + utf8_print(p->out,""); for(i=0; iout,""); + utf8_print(p->out,""); output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - utf8_printf(p->out,"\n"); + utf8_print(p->out,"\n"); } - utf8_printf(p->out,"\n"); + utf8_print(p->out,"\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->colSeparator); + if(iout, p->colSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); - if(iout, "%s", p->colSeparator); + if(iout, p->colSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); break; } case MODE_Csv: { @@ -2495,13 +2497,13 @@ static int shell_callback( for(i=0; iout, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); } if( nArg>0 ){ for(i=0; iout, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); } setTextMode(p->out, 1); break; @@ -2510,25 +2512,25 @@ static int shell_callback( if( azArg==0 ) break; utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->showHeader ){ - utf8_printf(p->out,"("); + utf8_print(p->out,"("); for(i=0; i0 ) utf8_printf(p->out, ","); + if( i>0 ) utf8_print(p->out, ","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); - utf8_printf(p->out, "%s", z); + utf8_printf(p->out, z); sqlite3_free(z); }else{ utf8_printf(p->out, "%s", azCol[i]); } } - utf8_printf(p->out,")"); + utf8_print(p->out,")"); } p->cnt++; for(i=0; iout, i>0 ? "," : " VALUES("); + utf8_print(p->out, i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + utf8_print(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); @@ -2536,16 +2538,16 @@ static int shell_callback( output_quoted_escaped_string(p->out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_print(p->out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - utf8_printf(p->out, "9.0e+999"); + utf8_print(p->out, "9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - utf8_printf(p->out, "-9.0e+999"); + utf8_print(p->out, "-9.0e+999"); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -2553,21 +2555,21 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - utf8_printf(p->out, "%s", z); + utf8_print(p->out, z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_print(p->out, azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ output_quoted_string(p->out, azArg[i]); }else{ output_quoted_escaped_string(p->out, azArg[i]); } } - utf8_printf(p->out,");\n"); + utf8_print(p->out,");\n"); break; } case MODE_Json: { @@ -2589,12 +2591,12 @@ static int shell_callback( sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - utf8_printf(p->out, "9.0e+999"); + utf8_print(p->out, "9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - utf8_printf(p->out, "-9.0e+999"); + utf8_print(p->out, "-9.0e+999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - utf8_printf(p->out, "%s", z); + utf8_print(p->out, z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); @@ -2603,13 +2605,13 @@ static int shell_callback( }else if( aiType && aiType[i]==SQLITE_TEXT ){ output_json_string(p->out, azArg[i], -1); }else{ - utf8_printf(p->out,"%s", azArg[i]); + utf8_print(p->out, azArg[i]); } if( iout); + utf8_print(p->out, ","); } } - putc('}', p->out); + utf8_print(p->out, "}"); break; } case MODE_Quote: { @@ -2625,22 +2627,22 @@ static int shell_callback( for(i=0; i0 ) fputs(p->colSeparator, p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + utf8_print(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ output_quoted_string(p->out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_print(p->out, azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - utf8_printf(p->out, "%s", z); + utf8_print(p->out, z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); + utf8_print(p->out, azArg[i]); }else{ output_quoted_string(p->out, azArg[i]); } @@ -2651,17 +2653,17 @@ static int shell_callback( case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); + if( i>0 ) utf8_print(p->out, p->colSeparator); + utf8_print(p->out, azCol[i] ? azCol[i] : ""); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); } if( azArg==0 ) break; for(i=0; i0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) utf8_print(p->out, p->colSeparator); + utf8_print(p->out, azArg[i] ? azArg[i] : p->nullValue); } - utf8_printf(p->out, "%s", p->rowSeparator); + utf8_print(p->out, p->rowSeparator); break; } case MODE_EQP: { @@ -2860,9 +2862,9 @@ static int run_table_dump_query( if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - utf8_printf(p->out, "\n;\n"); + utf8_print(p->out, "\n;\n"); }else{ - utf8_printf(p->out, ";\n"); + utf8_print(p->out, ";\n"); } rc = sqlite3_step(pSelect); } @@ -3491,7 +3493,7 @@ static void print_box_line(FILE *out, int N){ const int nDash = sizeof(zDash) - 1; N *= 3; while( N>nDash ){ - utf8_printf(out, zDash); + utf8_print(out, zDash); N -= nDash; } utf8_printf(out, "%.*s", N, zDash); @@ -3509,15 +3511,15 @@ static void print_box_row_separator( ){ int i; if( nArg>0 ){ - utf8_printf(p->out, "%s", zSep1); + utf8_print(p->out, zSep1); print_box_line(p->out, p->actualWidth[0]+2); for(i=1; iout, "%s", zSep2); + utf8_print(p->out, zSep2); print_box_line(p->out, p->actualWidth[i]+2); } - utf8_printf(p->out, "%s", zSep3); + utf8_print(p->out, zSep3); } - fputs("\n", p->out); + utf8_print(p->out, "\n"); } /* @@ -3799,7 +3801,7 @@ static void exec_prepared_stmt_columnar( w = p->actualWidth[i]; n = strlenChar(azData[i]); utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + utf8_print(p->out, i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "+"); break; @@ -3812,7 +3814,7 @@ static void exec_prepared_stmt_columnar( w = p->actualWidth[i]; n = strlenChar(azData[i]); utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + utf8_print(p->out, i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "|"); break; @@ -3821,7 +3823,7 @@ static void exec_prepared_stmt_columnar( colSep = " " BOX_13 " "; rowSep = " " BOX_13 "\n"; print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - utf8_printf(p->out, BOX_13 " "); + utf8_print(p->out, BOX_13 " "); for(i=0; iactualWidth[i]; n = strlenChar(azData[i]); @@ -3835,7 +3837,7 @@ static void exec_prepared_stmt_columnar( } for(i=nColumn, j=0; icMode!=MODE_Column ){ - utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); + utf8_print(p->out, p->cMode==MODE_Box?BOX_13" ":"| "); } z = azData[i]; if( z==0 ) z = p->nullValue; @@ -3843,20 +3845,20 @@ static void exec_prepared_stmt_columnar( if( p->colWidth[j]<0 ) w = -w; utf8_width_print(p->out, w, z); if( j==nColumn-1 ){ - utf8_printf(p->out, "%s", rowSep); + utf8_print(p->out, rowSep); if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1cMode==MODE_Table ){ print_row_separator(p, nColumn, "+"); }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); }else if( p->cMode==MODE_Column ){ - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); } } j = -1; if( seenInterrupt ) goto columnar_end; }else{ - utf8_printf(p->out, "%s", colSep); + utf8_print(p->out, colSep); } } if( p->cMode==MODE_Table ){ @@ -3866,7 +3868,7 @@ static void exec_prepared_stmt_columnar( } columnar_end: if( seenInterrupt ){ - utf8_printf(p->out, "Interrupt\n"); + utf8_print(p->out, "Interrupt\n"); } nData = (nRow+1)*nColumn; for(i=0; ishellFlgs & SHFLG_DumpNoSys)!=0; if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) utf8_printf(p->out, "DELETE FROM sqlite_sequence;\n"); + if( !dataOnly ) utf8_print(p->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); + if( !dataOnly ) utf8_print(p->out, "ANALYZE sqlite_schema;\n"); }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ @@ -4420,7 +4422,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ - utf8_printf(p->out, "PRAGMA writable_schema=ON;\n"); + utf8_print(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( @@ -4490,7 +4492,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->mode = p->cMode = MODE_Insert; rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - utf8_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + utf8_print(p->out, "/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); @@ -4521,7 +4523,7 @@ static int run_schema_dump_query( if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - utf8_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + utf8_print(p->out, "/****** CORRUPTION ERROR *******/\n"); if( zErr ){ utf8_printf(p->out, "/****** %s ******/\n", zErr); sqlite3_free(zErr); @@ -4978,7 +4980,7 @@ static char *readFile(const char *zName, int *pnByte){ rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ){ - utf8_printf(stderr, "Error: out of memory\n"); + utf8_print(stderr, "Error: out of memory\n"); fclose(in); return 0; } @@ -5128,7 +5130,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ shell_check_oom(a); memset(a, 0, n); if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ - utf8_printf(stderr, "invalid pagesize\n"); + utf8_print(stderr, "invalid pagesize\n"); goto readHexDb_error; } for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ @@ -5254,7 +5256,7 @@ static void open_db(ShellState *p, int openFlags){ sqlite3_close(p->db); sqlite3_open(":memory:", &p->db); if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ - utf8_printf(stderr, + utf8_print(stderr, "Also: unable to open substitute in-memory database.\n" ); exit(1); @@ -5615,7 +5617,7 @@ static int sql_trace_callback( i64 nSql; if( p->traceOut==0 ) return 0; if( mType==SQLITE_TRACE_CLOSE ){ - utf8_printf(p->traceOut, "-- closing database connection\n"); + utf8_print(p->traceOut, "-- closing database connection\n"); return 0; } if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){ @@ -6183,7 +6185,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ memcpy(aHdr, pb, 100); sqlite3_finalize(pStmt); }else{ - utf8_printf(stderr, "unable to read database header\n"); + utf8_print(stderr, "unable to read database header\n"); sqlite3_finalize(pStmt); return 1; } @@ -6199,12 +6201,12 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ utf8_printf(p->out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) utf8_printf(p->out, " (utf8)"); - if( val==2 ) utf8_printf(p->out, " (utf16le)"); - if( val==3 ) utf8_printf(p->out, " (utf16be)"); + if( val==1 ) utf8_print(p->out, " (utf8)"); + if( val==2 ) utf8_print(p->out, " (utf16le)"); + if( val==3 ) utf8_print(p->out, " (utf16be)"); } } - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -6594,7 +6596,7 @@ static int lintFkeyIndexes( if( rc!=SQLITE_OK ) break; if( res<0 ){ - utf8_printf(stderr, "Error: internal error"); + utf8_print(stderr, "Error: internal error"); break; }else{ if( bGroupByParent @@ -6648,8 +6650,8 @@ static int lintDotCommand( usage: utf8_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); - utf8_printf(stderr, "Where sub-commands are:\n"); - utf8_printf(stderr, " fkey-indexes\n"); + utf8_print(stderr, "Where sub-commands are:\n"); + utf8_print(stderr, " fkey-indexes\n"); return SQLITE_ERROR; } @@ -6790,9 +6792,9 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_end(ap); utf8_printf(stderr, "Error: %s\n", z); if( pAr->fromCmdLine ){ - utf8_printf(stderr, "Use \"-A\" for more help\n"); + utf8_print(stderr, "Use \"-A\" for more help\n"); }else{ - utf8_printf(stderr, "Use \".archive --help\" for more help\n"); + utf8_print(stderr, "Use \".archive --help\" for more help\n"); } sqlite3_free(z); return SQLITE_ERROR; @@ -6892,7 +6894,7 @@ static int arParseCommand( struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ - utf8_printf(stderr, "Wrong number of arguments. Usage:\n"); + utf8_print(stderr, "Wrong number of arguments. Usage:\n"); return arUsage(stderr); }else{ char *z = azArg[1]; @@ -6998,7 +7000,7 @@ static int arParseCommand( } } if( pAr->eCmd==0 ){ - utf8_printf(stderr, "Required argument missing. Usage:\n"); + utf8_print(stderr, "Required argument missing. Usage:\n"); return arUsage(stderr); } return SQLITE_OK; @@ -7450,7 +7452,7 @@ static int arDotCommand( if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ - utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); + utf8_print(stderr, "database does not contain an 'sqlar' table\n"); rc = SQLITE_ERROR; goto end_ar_command; } @@ -7848,7 +7850,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_AUTHORIZATION if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ if( nArg!=2 ){ - utf8_printf(stderr, "Usage: .auth ON|OFF\n"); + utf8_print(stderr, "Usage: .auth ON|OFF\n"); rc = 1; goto meta_command_exit; } @@ -7904,12 +7906,12 @@ static int do_meta_command(char *zLine, ShellState *p){ zDb = zDestFile; zDestFile = azArg[j]; }else{ - utf8_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); + utf8_print(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); return 1; } } if( zDestFile==0 ){ - utf8_printf(stderr, "missing FILENAME argument on .backup\n"); + utf8_print(stderr, "missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; @@ -7947,7 +7949,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ - utf8_printf(stderr, "Usage: .bail on|off\n"); + utf8_print(stderr, "Usage: .bail on|off\n"); rc = 1; } }else @@ -7961,9 +7963,9 @@ static int do_meta_command(char *zLine, ShellState *p){ setTextMode(p->out, 1); } }else{ - utf8_printf(stderr, "The \".binary\" command is deprecated." - " Use \".crnl\" instead.\n"); - utf8_printf(stderr, "Usage: .binary on|off\n"); + utf8_print(stderr, "The \".binary\" command is deprecated." + " Use \".crnl\" instead.\n" + "Usage: .binary on|off\n"); rc = 1; } }else @@ -7991,7 +7993,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; } }else{ - utf8_printf(stderr, "Usage: .cd DIRECTORY\n"); + utf8_print(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else @@ -8001,7 +8003,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ - utf8_printf(stderr, "Usage: .changes on|off\n"); + utf8_print(stderr, "Usage: .changes on|off\n"); rc = 1; } }else @@ -8015,7 +8017,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zRes = 0; output_reset(p); if( nArg!=2 ){ - utf8_printf(stderr, "Usage: .check GLOB-PATTERN\n"); + utf8_print(stderr, "Usage: .check GLOB-PATTERN\n"); rc = 2; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ rc = 2; @@ -8038,7 +8040,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ - utf8_printf(stderr, "Usage: .clone FILENAME\n"); + utf8_print(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else @@ -8077,7 +8079,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( i<0 || i>=ArraySize(p->aAuxDb) ){ /* No-op */ }else if( p->pAuxDb == &p->aAuxDb[i] ){ - utf8_printf(stderr, "cannot close the active database connection\n"); + utf8_print(stderr, "cannot close the active database connection\n"); rc = 1; }else if( p->aAuxDb[i].db ){ session_close_all(p, i); @@ -8085,7 +8087,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->aAuxDb[i].db = 0; } }else{ - utf8_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n"); + utf8_print(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n"); rc = 1; } }else @@ -8099,9 +8101,9 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else{ #if !defined(_WIN32) && !defined(WIN32) - utf8_printf(stderr, "The \".crnl\" is a no-op on non-Windows machines.\n"); + utf8_print(stderr, "The \".crnl\" is a no-op on non-Windows machines.\n"); #endif - utf8_printf(stderr, "Usage: .crnl on|off\n"); + utf8_print(stderr, "Usage: .crnl on|off\n"); rc = 1; } }else @@ -8182,7 +8184,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( nArg>1 && ii==ArraySize(aDbConfig) ){ utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); - utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); + utf8_print(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); } }else @@ -8212,7 +8214,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[0]=='-' ) z++; if( cli_strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE - utf8_printf(stderr, "The --preserve-rowids option is not compatible" + utf8_print(stderr, "The --preserve-rowids option is not compatible" " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; sqlite3_free(zLike); @@ -8265,8 +8267,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - utf8_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); - utf8_printf(p->out, "BEGIN TRANSACTION;\n"); + utf8_print(p->out, "PRAGMA foreign_keys=OFF;\n"); + utf8_print(p->out, "BEGIN TRANSACTION;\n"); } p->writableSchema = 0; p->showHeader = 0; @@ -8297,13 +8299,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_free(zLike); if( p->writableSchema ){ - utf8_printf(p->out, "PRAGMA writable_schema=OFF;\n"); + utf8_print(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - utf8_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + utf8_print(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); } p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; @@ -8313,7 +8315,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ - utf8_printf(stderr, "Usage: .echo on|off\n"); + utf8_print(stderr, "Usage: .echo on|off\n"); rc = 1; } }else @@ -8344,7 +8346,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ - utf8_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); + utf8_print(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); rc = 1; } }else @@ -8441,7 +8443,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all file-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available file-controls:\n"); + utf8_print(p->out, "Available file-controls:\n"); for(i=0; iout, " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); @@ -8552,7 +8554,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nArg = 1; } if( nArg!=1 ){ - utf8_printf(stderr, "Usage: .fullschema ?--indent?\n"); + utf8_print(stderr, "Usage: .fullschema ?--indent?\n"); rc = 1; goto meta_command_exit; } @@ -8578,15 +8580,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( doStats==0 ){ - utf8_printf(p->out, "/* No STAT tables available */\n"); + utf8_print(p->out, "/* No STAT tables available */\n"); }else{ - utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); + utf8_print(p->out, "ANALYZE sqlite_schema;\n"); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - utf8_printf(p->out, "ANALYZE sqlite_schema;\n"); + utf8_print(p->out, "ANALYZE sqlite_schema;\n"); } }else @@ -8595,7 +8597,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->showHeader = booleanValue(azArg[1]); p->shellFlgs |= SHFLG_HeaderSet; }else{ - utf8_printf(stderr, "Usage: .headers on|off\n"); + utf8_print(stderr, "Usage: .headers on|off\n"); rc = 1; } }else @@ -8687,19 +8689,19 @@ static int do_meta_command(char *zLine, ShellState *p){ ** the column and row separator characters from the output mode. */ nSep = strlen30(p->colSeparator); if( nSep==0 ){ - utf8_printf(stderr, + utf8_print(stderr, "Error: non-null column separator required for import\n"); goto meta_command_exit; } if( nSep>1 ){ - utf8_printf(stderr, + utf8_print(stderr, "Error: multi-character column separators not allowed" " for import\n"); goto meta_command_exit; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ - utf8_printf(stderr, + utf8_print(stderr, "Error: non-null row separator required for import\n"); goto meta_command_exit; } @@ -8714,7 +8716,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ - utf8_printf(stderr, "Error: multi-character row separators not allowed" + utf8_print(stderr, "Error: multi-character row separators not allowed" " for import\n"); goto meta_command_exit; } @@ -8725,7 +8727,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_print(stderr, "Error: pipes are not supported in this OS\n"); goto meta_command_exit; #else sCtx.in = popen(sCtx.zFile+1, "r"); @@ -8744,12 +8746,12 @@ static int do_meta_command(char *zLine, ShellState *p){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; - utf8_printf(p->out, "Column separator "); + utf8_print(p->out, "Column separator "); output_c_string(p->out, zSep); - utf8_printf(p->out, ", row separator "); + utf8_print(p->out, ", row separator "); zSep[0] = sCtx.cRowSep; output_c_string(p->out, zSep); - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); } sCtx.z = sqlite3_malloc64(120); if( sCtx.z==0 ){ @@ -8930,7 +8932,7 @@ static int do_meta_command(char *zLine, ShellState *p){ goto meta_command_exit; } if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ - utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" + utf8_print(stderr, "Usage: .imposter INDEX IMPOSTER\n" " .imposter off\n"); /* Also allowed, but not documented: ** @@ -9070,7 +9072,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_limit(p->db, aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ - utf8_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); + utf8_print(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); rc = 1; goto meta_command_exit; }else{ @@ -9115,7 +9117,7 @@ static int do_meta_command(char *zLine, ShellState *p){ failIfSafeMode(p, "cannot run .load in safe mode"); if( nArg<2 || azArg[1][0]==0 ){ /* Must have a non-empty FILE. (Will not load self.) */ - utf8_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); + utf8_print(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; goto meta_command_exit; } @@ -9133,7 +9135,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){ if( nArg!=2 ){ - utf8_printf(stderr, "Usage: .log FILENAME\n"); + utf8_print(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; @@ -9141,7 +9143,7 @@ static int do_meta_command(char *zLine, ShellState *p){ && cli_strcmp(zFile,"on")!=0 && cli_strcmp(zFile,"off")!=0 ){ - utf8_printf(stdout, "cannot set .log to anything other " + utf8_print(stdout, "cannot set .log to anything other " "than \"on\" or \"off\"\n"); zFile = "off"; } @@ -9182,7 +9184,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zTabname = z; }else if( z[0]=='-' ){ utf8_printf(stderr, "unknown option: %s\n", z); - utf8_printf(stderr, "options:\n" + utf8_print(stderr, "options:\n" " --noquote\n" " --quote\n" " --wordwrap on/off\n" @@ -9266,7 +9268,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( cli_strncmp(zMode,"json",n2)==0 ){ p->mode = MODE_Json; }else{ - utf8_printf(stderr, "Error: mode should be one of: " + utf8_print(stderr, "Error: mode should be one of: " "ascii box column csv html insert json line list markdown " "qbox quote table tabs tcl\n"); rc = 1; @@ -9277,7 +9279,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_SHELL_FIDDLE if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ if( nArg!=2 ){ - utf8_printf(stderr, "Usage: .nonce NONCE\n"); + utf8_print(stderr, "Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ utf8_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", @@ -9296,7 +9298,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ - utf8_printf(stderr, "Usage: .nullvalue STRING\n"); + utf8_print(stderr, "Usage: .nullvalue STRING\n"); rc = 1; } }else @@ -9482,7 +9484,7 @@ static int do_meta_command(char *zLine, ShellState *p){ shell_check_oom(zFile); if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_print(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else @@ -9620,10 +9622,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i1 ) utf8_printf(p->out, " "); - utf8_printf(p->out, "%s", azArg[i]); + if( i>1 ) utf8_print(p->out, " "); + utf8_print(p->out, azArg[i]); } - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); }else #ifndef SQLITE_OMIT_PROGRESS_CALLBACK @@ -9652,7 +9654,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( cli_strcmp(z,"limit")==0 ){ if( i+1>=nArg ){ - utf8_printf(stderr, "Error: missing argument on --limit\n"); + utf8_print(stderr, "Error: missing argument on --limit\n"); rc = 1; goto meta_command_exit; }else{ @@ -9693,13 +9695,13 @@ static int do_meta_command(char *zLine, ShellState *p){ int savedLineno = p->lineno; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ - utf8_printf(stderr, "Usage: .read FILE\n"); + utf8_print(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } if( azArg[1][0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - utf8_printf(stderr, "Error: pipes are not supported in this OS\n"); + utf8_print(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else @@ -9740,7 +9742,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zSrcFile = azArg[2]; zDb = azArg[1]; }else{ - utf8_printf(stderr, "Usage: .restore ?DB? FILE\n"); + utf8_print(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } @@ -9768,7 +9770,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - utf8_printf(stderr, "Error: source database is busy\n"); + utf8_print(stderr, "Error: source database is busy\n"); rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); @@ -9793,10 +9795,10 @@ static int do_meta_command(char *zLine, ShellState *p){ p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 ); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS - utf8_printf(stderr, "Warning: .scanstats not available in this build.\n"); + utf8_print(stderr, "Warning: .scanstats not available in this build.\n"); #endif }else{ - utf8_printf(stderr, "Usage: .scanstats on|off|est\n"); + utf8_print(stderr, "Usage: .scanstats on|off|est\n"); rc = 1; } }else @@ -9831,7 +9833,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zName==0 ){ zName = azArg[ii]; }else{ - utf8_printf(stderr, + utf8_print(stderr, "Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; @@ -9938,7 +9940,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ - utf8_printf(stderr,"Error: querying schema information\n"); + utf8_print(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; @@ -9984,7 +9986,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ){ session_not_open: - utf8_printf(stderr, "ERROR: No sessions are open\n"); + utf8_print(stderr, "ERROR: No sessions are open\n"); }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ @@ -10070,7 +10072,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nByte = sizeof(pSession->azFilter[0])*(nCmd-1); pSession->azFilter = sqlite3_malloc( nByte ); if( pSession->azFilter==0 ){ - utf8_printf(stderr, "Error: out or memory\n"); + utf8_print(stderr, "Error: out or memory\n"); exit(1); } for(ii=1; iiout, "%s", zBuf); + utf8_print(p->out, zBuf); } } }else @@ -10202,7 +10204,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); - utf8_printf(stderr, "Should be one of: --init -v\n"); + utf8_print(stderr, "Should be one of: --init -v\n"); rc = 1; goto meta_command_exit; } @@ -10231,7 +10233,7 @@ static int do_meta_command(char *zLine, ShellState *p){ -1, &pStmt, 0); } if( rc ){ - utf8_printf(stderr, "Error querying the selftest table\n"); + utf8_print(stderr, "Error querying the selftest table\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -10288,7 +10290,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ - utf8_printf(stderr, "Usage: .separator COL ?ROW?\n"); + utf8_print(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ @@ -10338,7 +10340,7 @@ static int do_meta_command(char *zLine, ShellState *p){ goto meta_command_exit; } }else if( zLike ){ - utf8_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); + utf8_print(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ @@ -10471,7 +10473,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); } } - if( rc ) utf8_printf(stderr, ".sha3sum failed.\n"); + if( rc ) utf8_print(stderr, ".sha3sum failed.\n"); sqlite3_free(zRevText); } #endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */ @@ -10487,7 +10489,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, x; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( nArg<2 ){ - utf8_printf(stderr, "Usage: .system COMMAND\n"); + utf8_print(stderr, "Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } @@ -10507,7 +10509,7 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zOut; int i; if( nArg!=1 ){ - utf8_printf(stderr, "Usage: .show\n"); + utf8_print(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } @@ -10530,15 +10532,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } utf8_printf(p->out, "%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); utf8_printf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); utf8_printf(p->out,"%12.12s: ", "colseparator"); output_c_string(p->out, p->colSeparator); - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); utf8_printf(p->out,"%12.12s: ", "rowseparator"); output_c_string(p->out, p->rowSeparator); - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); switch( p->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; @@ -10550,7 +10552,7 @@ static int do_meta_command(char *zLine, ShellState *p){ for (i=0;inWidth;i++) { utf8_printf(p->out, "%d ", p->colWidth[i]); } - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n", "filename", p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); }else @@ -10567,7 +10569,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ - utf8_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n"); + utf8_print(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n"); rc = 1; } }else @@ -10593,7 +10595,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ - utf8_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); + utf8_print(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -10672,7 +10674,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - utf8_printf(p->out, "\n"); + utf8_print(p->out, "\n"); } } @@ -10686,7 +10688,7 @@ static int do_meta_command(char *zLine, ShellState *p){ output_reset(p); p->out = output_file_open("testcase-out.txt", 0); if( p->out==0 ){ - utf8_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); + utf8_print(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); @@ -10747,7 +10749,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all test-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available test-controls:\n"); + utf8_print(p->out, "Available test-controls:\n"); for(i=0; iout, " .testctrl %s %s\n", @@ -10915,11 +10917,11 @@ static int do_meta_command(char *zLine, ShellState *p){ int val = 0; rc2 = sqlite3_test_control(testctrl, -id, &val); if( rc2!=SQLITE_OK ) break; - if( id>1 ) utf8_printf(p->out, " "); + if( id>1 ) utf8_print(p->out, " "); utf8_printf(p->out, "%d: %d", id, val); id++; } - if( id>1 ) utf8_printf(p->out, "\n"); + if( id>1 ) utf8_print(p->out, "\n"); isOk = 3; } break; @@ -10954,11 +10956,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ enableTimer = booleanValue(azArg[1]); if( enableTimer && !HAS_TIMER ){ - utf8_printf(stderr, "Error: timer not available on this system.\n"); + utf8_print(stderr, "Error: timer not available on this system.\n"); enableTimer = 0; } }else{ - utf8_printf(stderr, "Usage: .timer on|off\n"); + utf8_print(stderr, "Usage: .timer on|off\n"); rc = 1; } }else @@ -11019,7 +11021,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int lenOpt; char *zOpt; if( nArg<2 ){ - utf8_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); + utf8_print(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); rc = 1; goto meta_command_exit; } @@ -11041,14 +11043,14 @@ static int do_meta_command(char *zLine, ShellState *p){ #if SQLITE_USER_AUTHENTICATION if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ if( nArg<2 ){ - utf8_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); + utf8_print(stderr, "Usage: .user SUBCOMMAND ...\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( cli_strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ - utf8_printf(stderr, "Usage: .user login USER PASSWORD\n"); + utf8_print(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } @@ -11060,7 +11062,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else if( cli_strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ - utf8_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); + utf8_print(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } @@ -11072,7 +11074,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else if( cli_strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ - utf8_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); + utf8_print(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } @@ -11084,7 +11086,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else if( cli_strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ - utf8_printf(stderr, "Usage: .user delete USER\n"); + utf8_print(stderr, "Usage: .user delete USER\n"); rc = 1; goto meta_command_exit; } @@ -11094,7 +11096,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; } }else{ - utf8_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); + utf8_print(stderr, "Usage: .user login|add|edit|delete ...\n"); rc = 1; goto meta_command_exit; } @@ -11148,7 +11150,7 @@ static int do_meta_command(char *zLine, ShellState *p){ utf8_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); utf8_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - utf8_printf(p->out, "-----------------------------------\n"); + utf8_print(p->out, "-----------------------------------\n"); } } }else @@ -11669,7 +11671,7 @@ static void process_sqliterc( if( sqliterc == NULL ){ home_dir = find_home_dir(0); if( home_dir==0 ){ - utf8_printf(stderr, "-- warning: cannot find home directory;" + utf8_print(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } @@ -11771,7 +11773,7 @@ static void usage(int showDetail){ if( showDetail ){ utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); }else{ - utf8_printf(stderr, "Use the -help option for additional information\n"); + utf8_print(stderr, "Use the -help option for additional information\n"); } exit(1); } @@ -11782,7 +11784,7 @@ static void usage(int showDetail){ */ static void verify_uninitialized(void){ if( sqlite3_config(-1)==SQLITE_MISUSE ){ - utf8_printf(stdout, "WARNING: attempt to configure SQLite after" + utf8_print(stdout, "WARNING: attempt to configure SQLite after" " initialization.\n"); } } @@ -12394,7 +12396,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ /* Acted upon in first pass. */ }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); - utf8_printf(stderr,"Use -help for a list of options.\n"); + utf8_print(stderr,"Use -help for a list of options.\n"); return 1; } data.cMode = data.mode; From eb1898d6f3aea2bc023618f63af17aaaacd2b50b Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 6 Nov 2023 15:15:58 +0000 Subject: [PATCH 126/347] Some renaming, warnings cured, and more coding convention conformance. FossilOrigin-Name: 58815f0ad259599a674e7d0fe0d4676a1c4fb8e118c08b619580ff655fb3be40 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/console_io.c | 14 +++++++------- src/console_io.h | 23 ++++++++++++----------- src/shell.c.in | 27 ++++++++------------------- 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/manifest b/manifest index 22d3223f7e..34234276c1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\san\sfputs()\sequivalent\sto\sconsole\sI/O\slib,\sand\suse\sin\sCLI. -D 2023-11-06T13:24:07.790 +C Some\srenaming,\swarnings\scured,\sand\smore\scoding\sconvention\sconformance. +D 2023-11-06T15:15:58.179 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,8 +669,8 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c 6bc9b7f00dd29873519f650e52de974d811c9c36c3a2914fb65d8764c6fbabeb x -F src/console_io.h cf7a58e120603a16ea15cf5f32a5aa3012ee2def26a610b763009d3b266d15ad +F src/console_io.c 7c9d44ae378855778b7c4bb171df336e5f0e629107e56d93fcaaa41b512d253d x +F src/console_io.h 92a765da11922b57c899132fd31fb9f6a54802d9ca2489e618a66b7d5334acb9 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in 18440bb3296508bc92673c10e4293c97aa11bc3d3a6bb0ed0c16c0c73b6ec558 +F src/shell.c.in d1fdac3c508f939554c1e11694d36ce30645febfc653a91a2bdb78a75b65bb7a F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 bf66a7c1d330b04fd3c3e50f43ebe1e41307db59d7189798acafb3de77e0c8b2 -R 873651c11fd34fdadd7d1e7335c5cb89 +P d661f90724b1bd31948169841bacb80ba4ae91b52191a0b0021a89a22d932a02 +R 51e773e98c65b583317374019e2b48c6 U larrybr -Z 556f0a6b391897986638a1109b2be8a2 +Z 7c8e9716e1490034796ed7b1b7236e3d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 45db515f6f..90ab247018 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d661f90724b1bd31948169841bacb80ba4ae91b52191a0b0021a89a22d932a02 \ No newline at end of file +58815f0ad259599a674e7d0fe0d4676a1c4fb8e118c08b619580ff655fb3be40 \ No newline at end of file diff --git a/src/console_io.c b/src/console_io.c index 2c62c7049e..06db83aacc 100755 --- a/src/console_io.c +++ b/src/console_io.c @@ -122,7 +122,7 @@ static ConsoleInfo consoleInfo = { }; #undef CI_INITIALIZER -INT_LINKAGE ConsoleStdConsStreams +SQLITE_INTERNAL_LINKAGE ConsoleStdConsStreams consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ ConsoleStdConsStreams rv = CSCS_NoConsole; FILE *apf[3] = { pfIn, pfOut, pfErr }; @@ -152,7 +152,7 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ return rv; } -INT_LINKAGE void SQLITE_CDECL consoleRestore( void ){ +SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ if( consoleInfo.cscs ){ int ix; for( ix=0; ix<3; ++ix ){ @@ -199,15 +199,15 @@ static void setModeFlushQ(FILE *pf, short bFlush, int mode){ # define setModeFlushQ(f, b, m) if(isConOut(f)>0||b) fflush(f) #endif -INT_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ +SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ setModeFlushQ(pf, bFlush, _O_BINARY); } -INT_LINKAGE void setTextMode(FILE *pf, short bFlush){ +SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ setModeFlushQ(pf, bFlush, _O_TEXT); } #undef setModeFlushQ -INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ +SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ va_list ap; int rv = 0; #if SHELL_CON_TRANSLATE @@ -255,7 +255,7 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ return rv; } -INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ +SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ #if SHELL_CON_TRANSLATE return fprintfUtf8(pfO, "%s", z); #else @@ -263,7 +263,7 @@ INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ #endif } -INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ +SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; #if SHELL_CON_TRANSLATE if( pfIn == consoleInfo.pst[0].pf ){ diff --git a/src/console_io.h b/src/console_io.h index a46e69aa46..2cbc2c1f97 100644 --- a/src/console_io.h +++ b/src/console_io.h @@ -24,8 +24,8 @@ ** This code may change in tandem with other project code as needed. */ -#ifndef INT_LINKAGE -# define INT_LINKAGE extern /* Linkage external to translation unit. */ +#ifndef SQLITE_INTERNAL_LINKAGE +# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ # include # include # include @@ -71,7 +71,7 @@ typedef enum ConsoleStdConsStreams { ** inherits the same I/O streams may call this function after ** such a process exits to guard against console mode changes. */ -INT_LINKAGE ConsoleStdConsStreams +SQLITE_INTERNAL_LINKAGE ConsoleStdConsStreams consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); /* @@ -88,7 +88,7 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); ** before so that said process will have a console setup ** however users have configured it or come to expect. */ -INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); +SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); /* ** Render output like fprintf(). If the output is going to the @@ -97,7 +97,7 @@ INT_LINKAGE void SQLITE_CDECL consoleRestore( void ); ** to the provided stream almost as-is, possibly with newline ** translation as specified by set{Binary,Text}Mode(). */ -INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); +SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); /* ** Render output like fputs(). If the output is going to the @@ -106,7 +106,7 @@ INT_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); ** provided stream almost as-is, possibly with newline ** translation as specified by set{Binary,Text}Mode(). */ -INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO); +SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO); /* ** Collect input like fgets(...) with special provisions for input @@ -115,7 +115,7 @@ INT_LINKAGE int fputsUtf8(const char *z, FILE *pfO); ** translation may be done as set by set{Binary,Text}Mode(). As a ** convenience, pfIn==NULL is treated as stdin. */ -INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn); +SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn); /* ** Set given stream for binary mode, where newline translation is @@ -130,8 +130,8 @@ INT_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn); ** translation. On all platforms, newline to the console starts ** a new line and CR,LF chars from the console become a newline. */ -INT_LINKAGE void setBinaryMode(FILE *, short bFlush); -INT_LINKAGE void setTextMode(FILE *, short bFlush); +SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); +SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); typedef struct Prompts { int numPrompts; @@ -177,10 +177,11 @@ typedef struct Prompts { ** This function may call upon services of a line-editing ** library to interactively collect line edited input. */ -INT_LINKAGE char * +#if 0 /* not yet implemented */ +SQLITE_INTERNAL_LINKAGE char * shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, short isContinuation, Prompts azPrompt); - +#endif /* ** TBD: Define an interface for application(s) to generate ** completion candidates for use by the line-editor. diff --git a/src/shell.c.in b/src/shell.c.in index 5b1275e7b4..009b9d4318 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -237,7 +237,7 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif -#define INT_LINKAGE static +#define SQLITE_INTERNAL_LINKAGE static INCLUDE console_io.h INCLUDE console_io.c @@ -557,7 +557,8 @@ static char *dynamicContinuePrompt(void){ shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4); dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel); } - shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4); + shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, + PROMPT_LEN_MAX-4); } } return dynPrompt.dynamicPrompt; @@ -568,7 +569,7 @@ static char *dynamicContinuePrompt(void){ #define fgets(b,n,f) fgetsUtf8(b,n,f) /* And, (varargs) utf8_printf(f,z,...) is redirected to the same. */ #define utf8_printf fprintfUtf8 -/* And, rfprintf(f,z) is redirected to fputsUtf8(z,f) in the library. */ +/* And, utf8_print(f,z) is redirected to fputsUtf8(z,f) in the library. */ #define utf8_print(f,z) fputsUtf8(z,f) /* Indicate out-of-memory and exit. */ @@ -3340,7 +3341,7 @@ static void display_scanstats( UNUSED_PARAMETER(pArg); #else if( pArg->scanstatsOn==3 ){ - const char *zSql = + const char *zSql = " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec," " round(ncycle*100.0 / (sum(ncycle) OVER ()), 2)||'%' AS cycles" " FROM bytecode(?)"; @@ -4026,7 +4027,7 @@ static int expertFinish( const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); if( zIdx==0 ) zIdx = "(no new indexes)\n"; if( bVerbose ){ - utf8_printf(out, "-- Query %d --------------------------------\n",i+1); + utf8_printf(out,"-- Query %d --------------------------------\n",i+1); utf8_printf(out, "%s\n\n", zSql); } utf8_printf(out, "%s\n", zIdx); @@ -9990,7 +9991,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ - utf8_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); + utf8_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n",rc); rc = 0; } } @@ -10729,7 +10730,7 @@ static int do_meta_command(char *zLine, ShellState *p){ {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, - {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, + {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, }; int testctrl = -1; int iCtrl = -1; @@ -11737,9 +11738,6 @@ static const char zOptions[] = " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" -#if SHELL_WIN_UTF8_OPT - " -no-utf8 do not try to set up UTF-8 output (for legacy)\n" -#endif " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" @@ -12177,15 +12175,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ exit(1); } } -#if SHELL_WIN_UTF8_OPT - /* Get indicated Windows console setup done before running invocation commands. */ - if( in_console || out_console ){ - console_prepare_utf8(); - } - if( !in_console ){ - setBinaryMode(stdin, 0); - } -#endif if( data.pAuxDb->zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB From d0046388a00d966b730d938cb85b1a63240d9fd0 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 6 Nov 2023 15:31:08 +0000 Subject: [PATCH 127/347] Move console_io lib to its own subdirectory, etc/consio . FossilOrigin-Name: 1d0583f2eb69fdca1cbc55763c0e86a7e32cb7771bfbc2cdf02da4e3fedbfa23 --- {src => ext/consio}/console_io.c | 0 {src => ext/consio}/console_io.h | 0 manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/shell.c.in | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) rename {src => ext/consio}/console_io.c (100%) rename {src => ext/consio}/console_io.h (100%) diff --git a/src/console_io.c b/ext/consio/console_io.c similarity index 100% rename from src/console_io.c rename to ext/consio/console_io.c diff --git a/src/console_io.h b/ext/consio/console_io.h similarity index 100% rename from src/console_io.h rename to ext/consio/console_io.h diff --git a/manifest b/manifest index 34234276c1..6fec7ee4b3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Some\srenaming,\swarnings\scured,\sand\smore\scoding\sconvention\sconformance. -D 2023-11-06T15:15:58.179 +C Move\sconsole_io\slib\sto\sits\sown\ssubdirectory,\setc/consio\s. +D 2023-11-06T15:31:08.971 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,6 +50,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a +F ext/consio/console_io.c 7c9d44ae378855778b7c4bb171df336e5f0e629107e56d93fcaaa41b512d253d x src/console_io.c +F ext/consio/console_io.h 92a765da11922b57c899132fd31fb9f6a54802d9ca2489e618a66b7d5334acb9 w src/console_io.h F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@ -669,8 +671,6 @@ F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d1 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/console_io.c 7c9d44ae378855778b7c4bb171df336e5f0e629107e56d93fcaaa41b512d253d x -F src/console_io.h 92a765da11922b57c899132fd31fb9f6a54802d9ca2489e618a66b7d5334acb9 F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in d1fdac3c508f939554c1e11694d36ce30645febfc653a91a2bdb78a75b65bb7a +F src/shell.c.in 012b84f928dc883b589e27086540df7317b8b465d366b6baac2717b07f59ee0c F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 d661f90724b1bd31948169841bacb80ba4ae91b52191a0b0021a89a22d932a02 -R 51e773e98c65b583317374019e2b48c6 +P 58815f0ad259599a674e7d0fe0d4676a1c4fb8e118c08b619580ff655fb3be40 +R 9369b16a7548c239ad37858f67dafede U larrybr -Z 7c8e9716e1490034796ed7b1b7236e3d +Z ed8eeffcaf90e73880f84d59f5812858 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 90ab247018..55b7016ccf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58815f0ad259599a674e7d0fe0d4676a1c4fb8e118c08b619580ff655fb3be40 \ No newline at end of file +1d0583f2eb69fdca1cbc55763c0e86a7e32cb7771bfbc2cdf02da4e3fedbfa23 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 009b9d4318..b4a0d1936f 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -239,8 +239,8 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #define SQLITE_INTERNAL_LINKAGE static -INCLUDE console_io.h -INCLUDE console_io.c +INCLUDE ../ext/consio/console_io.h +INCLUDE ../ext/consio/console_io.c /* True if the timer is enabled */ static int enableTimer = 0; From a3e8822c4bc081d5b5094839e8c0a7665c3636e5 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 6 Nov 2023 18:40:25 +0000 Subject: [PATCH 128/347] Fix an fts5 problem where a transaction consisting of (a) a DELETE on rowid X, (b) a prefix query, and (c) an INSERT on rowid X, could corrupt the index. FossilOrigin-Name: c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 --- ext/fts5/fts5_hash.c | 6 ++---- ext/fts5/fts5_index.c | 10 +++++++++- ext/fts5/test/fts5prefix2.test | 30 ++++++++++++++++++++++++++++++ manifest | 18 +++++++++--------- manifest.uuid | 2 +- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/ext/fts5/fts5_hash.c b/ext/fts5/fts5_hash.c index 7e50c36608..391791c7ac 100644 --- a/ext/fts5/fts5_hash.c +++ b/ext/fts5/fts5_hash.c @@ -432,10 +432,8 @@ static Fts5HashEntry *fts5HashEntryMerge( } /* -** Extract all tokens from hash table iHash and link them into a list -** in sorted order. The hash table is cleared before returning. It is -** the responsibility of the caller to free the elements of the returned -** list. +** Link all tokens from hash table iHash into a list in sorted order. The +** tokens are not removed from the hash table. */ static int fts5HashEntrySort( Fts5Hash *pHash, diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 4e6afb2815..c7c02cf6fe 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -2719,6 +2719,14 @@ static void fts5SegIterHashInit( pLeaf->p = (u8*)pList; } } + + /* The call to sqlite3Fts5HashScanInit() causes the hash table to + ** fill the size field of all existing position lists. This means they + ** can no longer be appended to. Since the only scenario in which they + ** can be appended to is if the previous operation on this table was + ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this + ** possibility altogether. */ + p->bDelete = 0; }else{ p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), (const char*)pTerm, nTerm, (void**)&pLeaf, &nList @@ -6204,7 +6212,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ /* Flush the hash table to disk if required */ if( iRowidiWriteRowid || (iRowid==p->iWriteRowid && p->bDelete==0) - || (p->nPendingData > p->pConfig->nHashSize) + || (p->nPendingData > p->pConfig->nHashSize) ){ fts5IndexFlush(p); } diff --git a/ext/fts5/test/fts5prefix2.test b/ext/fts5/test/fts5prefix2.test index bf16e81a73..29744c86bf 100644 --- a/ext/fts5/test/fts5prefix2.test +++ b/ext/fts5/test/fts5prefix2.test @@ -52,6 +52,36 @@ do_execsql_test 2.1 { SELECT * FROM t2('to*'); } {top to tommy} +#------------------------------------------------------------------------- + +foreach {tn newrowid} { + 1 122 + 2 123 + 3 124 +} { + reset_db + do_execsql_test 3.$tn.0 { + CREATE VIRTUAL TABLE t12 USING fts5(x); + INSERT INTO t12(rowid, x) VALUES(123, 'wwww'); + } + do_execsql_test 3.$tn.1 { + BEGIN; + DELETE FROM t12 WHERE rowid=123; + SELECT * FROM t12('wwww*'); + INSERT INTO t12(rowid, x) VALUES($newrowid, 'wwww'); + SELECT * FROM t12('wwww*'); + END; + } {wwww} + do_execsql_test 3.$tn.2 { + INSERT INTO t12(t12) VALUES('integrity-check'); + } + do_execsql_test 3.$tn.3 { + SELECT rowid FROM t12('wwww*'); + } $newrowid +} + +finish_test + finish_test diff --git a/manifest b/manifest index f81c5b0edc..23292f0087 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sincremental\sblob\sI/O\ssupport\sto\sJNI\swrapper1. -D 2023-11-05T04:20:04.320 +C Fix\san\sfts5\sproblem\swhere\sa\stransaction\sconsisting\sof\s(a)\sa\sDELETE\son\srowid\sX,\s(b)\sa\sprefix\squery,\sand\s(c)\san\sINSERT\son\srowid\sX,\scould\scorrupt\sthe\sindex. +D 2023-11-06T18:40:25.654 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -93,8 +93,8 @@ F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d77 F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 -F ext/fts5/fts5_hash.c 65e7707bc8774706574346d18c20218facf87de3599b995963c3e6d6809f203d -F ext/fts5/fts5_index.c 730c9c32ada18ce1eb7ff847b36507f4b005d88d47af7b47db521e695a8ea4c7 +F ext/fts5/fts5_hash.c 076058f93327051952a752dc765df1acfe783eb11b419b30652aa1fc1f987902 +F ext/fts5/fts5_index.c 01b671fedd2189f6969385d96facc4c06d9c441f0f91d584386a62b724282f9f F ext/fts5/fts5_main.c a07ed863b8bd9e6fefb62db2fd40a3518eb30a5f7dcfda5be915dd2db45efa2f F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae @@ -193,7 +193,7 @@ F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd1 F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 F ext/fts5/test/fts5porter2.test 0d251a673f02fa13ca7f011654873b3add20745f7402f108600a23e52d8c7457 F ext/fts5/test/fts5prefix.test a0fa67b06650f2deaa7bf27745899d94e0fb547ad9ecbd08bfad98c04912c056 -F ext/fts5/test/fts5prefix2.test 3847ce46f70b82d61c6095103a9d7c53f2952c40a4704157bc079c04d9c8b18b +F ext/fts5/test/fts5prefix2.test ad751d4a5b029726ee908a7664e27d27bde7584218b8d7944c2a323afd381432 F ext/fts5/test/fts5query.test ac363b17a442620bb0780e93c24f16a5f963dfe2f23dc85647b869efcfada728 F ext/fts5/test/fts5rank.test 30f29e278cd7fb8831ba4f082feb74d8eb90c463bf07113ae200afc2b467ef32 F ext/fts5/test/fts5rebuild.test 55d6f17715cddbf825680dd6551efbc72ed916d8cf1cde40a46fc5d785b451e7 @@ -2142,8 +2142,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 d081a126697e214082f3b203f23ea63510080e5c2aac1d8badc9e6e4bfea7c95 -R d10239966e7198eb47ba47ac2878d98e -U stephan -Z e8e87bde5ba89e299cdcc353d989a2b2 +P 7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0 +R cd264cc39082738dd835b35e1796876f +U dan +Z ddf602c5afe5ca1e5ab19c1df349a6bc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 63c307a8c4..357f6f02e0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0 \ No newline at end of file +c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 \ No newline at end of file From 23056b608e2c5d7ad2fac621d358294aeb1896d0 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 6 Nov 2023 21:57:15 +0000 Subject: [PATCH 129/347] Add -DSQLITE_ENABLE_COLUMN_METADATA to the JNI build, as per [forum:9205518c0568fdf0|forum post 9205518c0568fdf0]. Add tests for the functions that flag enables so that the build will fail if that flag is missing. FossilOrigin-Name: 7a63b5b65a79d15658a160d0878c7371941c67e9b48a7442762c68c60b77288a --- ext/jni/GNUmakefile | 3 ++- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 4 ++++ manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 155e4e7f63..61c816194f 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -227,7 +227,8 @@ SQLITE_OPT += -DSQLITE_ENABLE_RTREE \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ -DSQLITE_ENABLE_PREUPDATE_HOOK \ -DSQLITE_ENABLE_NORMALIZE \ - -DSQLITE_ENABLE_SQLLOG + -DSQLITE_ENABLE_SQLLOG \ + -DSQLITE_ENABLE_COLUMN_METADATA endif ifeq (1,$(opt.debug)) diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index b25d077408..da9abb53c0 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -382,6 +382,10 @@ public class Tester1 implements Runnable { stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); affirm( sqlite3_stmt_readonly(stmt) ); affirm( !sqlite3_stmt_busy(stmt) ); + affirm("t".equals(CApi.sqlite3_column_table_name(stmt,0))); + affirm("main".equals(CApi.sqlite3_column_database_name(stmt,0))); + affirm("a".equals(CApi.sqlite3_column_origin_name(stmt,0))); + int total2 = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ affirm( sqlite3_stmt_busy(stmt) ); diff --git a/manifest b/manifest index 23292f0087..2b0d48205d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sfts5\sproblem\swhere\sa\stransaction\sconsisting\sof\s(a)\sa\sDELETE\son\srowid\sX,\s(b)\sa\sprefix\squery,\sand\s(c)\san\sINSERT\son\srowid\sX,\scould\scorrupt\sthe\sindex. -D 2023-11-06T18:40:25.654 +C Add\s-DSQLITE_ENABLE_COLUMN_METADATA\sto\sthe\sJNI\sbuild,\sas\sper\s[forum:9205518c0568fdf0|forum\spost\s9205518c0568fdf0].\sAdd\stests\sfor\sthe\sfunctions\sthat\sflag\senables\sso\sthat\sthe\sbuild\swill\sfail\sif\sthat\sflag\sis\smissing. +D 2023-11-06T21:57:15.302 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -238,7 +238,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 -F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072 +F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c b98d822a35ef4438023c3c14816b5ac17bdbd23bc838ff80b6463c3146a75d14 @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 2898e38cf5ee568a93e760fa9b84f448acb58138d0fca18d146e80a1da7c45fc +F ext/jni/src/org/sqlite/jni/capi/Tester1.java ea18a69a16cf60905a1c24f17a0ffe319fa397d009d444135d0809ada1ad448d F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -2142,8 +2142,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 7f1c76fe930d69a0274f70fa7b7e68e0db6226b731a065fa57d0936c8400ffb0 -R cd264cc39082738dd835b35e1796876f -U dan -Z ddf602c5afe5ca1e5ab19c1df349a6bc +P c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 +R a4e5f3d143d3dc87daaa4c71693d0a48 +U stephan +Z 38612cb49abf001f794e1c11b793254d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 357f6f02e0..3fe5e1fd42 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 \ No newline at end of file +7a63b5b65a79d15658a160d0878c7371941c67e9b48a7442762c68c60b77288a \ No newline at end of file From 4c78cb50bf12d84a91f9d910ac982b4024a15fd6 Mon Sep 17 00:00:00 2001 From: larrybr Date: Tue, 7 Nov 2023 02:41:46 +0000 Subject: [PATCH 130/347] Get dependencies into make recipes. Get legacy console I/O (-DSHELL_LEGACY_CONSOLE_IO) working. Due to movement of MBCS/UTF-8 translation into traditional stream I/O simulacra, the input translation does not happen the same way. (It works the same, but fails differently and a bit better.) Added printf() and fputs() look-alikes, and made CLI use them. FossilOrigin-Name: 1721dc6a434361c4e2b87c6e677b6dc223432b3cdd5b9eecabaa258889fb2d2a --- Makefile.in | 22 ++--- Makefile.msc | 2 + ext/consio/console_io.c | 175 +++++++++++++++++++++++++++++++--------- ext/consio/console_io.h | 13 +-- manifest | 20 ++--- manifest.uuid | 2 +- src/shell.c.in | 7 +- 7 files changed, 168 insertions(+), 73 deletions(-) diff --git a/Makefile.in b/Makefile.in index c521586a14..6b17d43997 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1135,19 +1135,21 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c # Source files that go into making shell.c SHELL_SRC = \ $(TOP)/src/shell.c.in \ - $(TOP)/ext/misc/appendvfs.c \ + $(TOP)/ext/consio/console_io.c \ + $(TOP)/ext/consio/console_io.h \ + $(TOP)/ext/misc/appendvfs.c \ $(TOP)/ext/misc/completion.c \ - $(TOP)/ext/misc/decimal.c \ - $(TOP)/ext/misc/basexx.c \ - $(TOP)/ext/misc/base64.c \ - $(TOP)/ext/misc/base85.c \ + $(TOP)/ext/misc/decimal.c \ + $(TOP)/ext/misc/basexx.c \ + $(TOP)/ext/misc/base64.c \ + $(TOP)/ext/misc/base85.c \ $(TOP)/ext/misc/fileio.c \ - $(TOP)/ext/misc/ieee754.c \ - $(TOP)/ext/misc/regexp.c \ - $(TOP)/ext/misc/series.c \ + $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/regexp.c \ + $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/shathree.c \ $(TOP)/ext/misc/sqlar.c \ - $(TOP)/ext/misc/uint.c \ + $(TOP)/ext/misc/uint.c \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/sqlite3expert.h \ $(TOP)/ext/misc/zipfile.c \ @@ -1156,7 +1158,7 @@ SHELL_SRC = \ $(TOP)/ext/recover/dbdata.c \ $(TOP)/ext/recover/sqlite3recover.c \ $(TOP)/ext/recover/sqlite3recover.h \ - $(TOP)/src/test_windirent.c + $(TOP)/src/test_windirent.c shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84 $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c diff --git a/Makefile.msc b/Makefile.msc index 4339be891f..3a4d46b01f 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -2261,6 +2261,8 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe # Source files that go into making shell.c SHELL_SRC = \ $(TOP)\src\shell.c.in \ + $(TOP)\ext\consio\console_io.c \ + $(TOP)\ext\consio\console_io.h \ $(TOP)\ext\misc\appendvfs.c \ $(TOP)\ext\misc\completion.c \ $(TOP)\ext\misc\base64.c \ diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index 06db83aacc..c60e2b0a49 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -18,19 +18,25 @@ #endif #ifndef SHELL_NO_SYSINC -#include -#include -#include "console_io.h" -#include "sqlite3.h" +# include +# include +# include +# include +# include "console_io.h" +# include "sqlite3.h" #endif #if defined(_WIN32) || defined(WIN32) # ifndef SHELL_NO_SYSINC # include # include +# undef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include # endif # ifdef SHELL_LEGACY_CONSOLE_IO # define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */ +extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); # else # define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ # endif @@ -144,7 +150,7 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ } if( ix > 0 ) fflush(apf[ix]); #if SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(apf[ix]), _O_U8TEXT); + // _setmode(_fileno(apf[ix]), _O_U8TEXT); _setmode(_fileno(apf[ix]), _O_TEXT); #endif } @@ -159,14 +165,14 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ if( consoleInfo.cscs & (CSCS_InConsole<hx, ppst->consMode); @@ -181,7 +187,6 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ } } } -#undef SHELL_INVALID_FILE_PTR static short isConOut(FILE *pf){ if( pf==consoleInfo.pst[1].pf ) return 1; @@ -207,45 +212,75 @@ SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ } #undef setModeFlushQ -SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ - va_list ap; +#if SHELL_CON_TRANSLATE +/* For fprintfUtf8() and printfUtf8() when stream is known as console. */ +static int conioVmPrintf(int rch, const char *zFormat, va_list ap){ int rv = 0; -#if SHELL_CON_TRANSLATE - short on = isConOut(pfO); -#endif - va_start(ap, zFormat); -#if SHELL_CON_TRANSLATE - if( on > 0 ){ - char *z1 = sqlite3_vmprintf(zFormat, ap); + char *z1 = sqlite3_vmprintf(zFormat, ap); # if SHELL_CON_TRANSLATE == 2 + if( z1!=NULL ){ + UINT ccp = GetConsoleOutputCP(); + FILE *pfO = consoleInfo.pst[rch].pf; /* Legacy translation to active code page, then MBCS chars out. */ char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); if( z2!=NULL ){ rv = (int)strlen(z2); - vfprintf(pfO, "%s", z2); + fputs(z2, pfO); sqlite3_free(z2); } -# else - /* Translation from UTF-8 to UTF-16, then WCHAR characters out. */ - if( z1!=NULL ){ - int nwc; - WCHAR *zw2 = 0; - rv = (int)strlen(z1); - nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,0,0); - if( nwc>0 ){ - zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); - if( zw2!=NULL ){ - HANDLE ho = (on==1)? consoleInfo.pst[1].hx : consoleInfo.pst[2].hx; - nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,zw2,nwc); - zw2[nwc] = 0; - WriteConsoleW(ho, zw2, nwc, 0, NULL); - sqlite3_free(zw2); - }else rv = 0; - } - } -# endif sqlite3_free(z1); + } +# elif SHELL_CON_TRANSLATE == 1 + /* Translation from UTF-8 to UTF-16, then WCHAR characters out. */ + if( z1!=NULL ){ + int nwc; + WCHAR *zw2 = 0; + rv = (int)strlen(z1); + nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,0,0); + if( nwc>0 ){ + zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); + if( zw2!=NULL ){ + HANDLE ho = consoleInfo.pst[rch].hx; + nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,zw2,nwc); + zw2[nwc] = 0; + WriteConsoleW(ho, zw2, nwc, 0, NULL); + sqlite3_free(zw2); + }else rv = 0; + } + sqlite3_free(z1); + } +# endif + return rv; +} +#endif + +SQLITE_INTERNAL_LINKAGE int printfUtf8(const char *zFormat, ...){ + va_list ap; + int rv; + va_start(ap, zFormat); +#if SHELL_CON_TRANSLATE + if( SHELL_INVALID_FILE_PTR != consoleInfo.pst[1].pf ){ + rv = conioVmPrintf(1, zFormat, ap); }else{ +#endif + rv = vfprintf(stdout, zFormat, ap); +#if SHELL_CON_TRANSLATE + } +#endif + va_end(ap); + return rv; +} +#undef SHELL_INVALID_FILE_PTR + +SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ + va_list ap; + int rv; + va_start(ap, zFormat); +#if SHELL_CON_TRANSLATE + short rch = isConOut(pfO); + if( rch > 0 ){ + rv = conioVmPrintf(rch, zFormat, ap); + }else { #endif rv = vfprintf(pfO, zFormat, ap); #if SHELL_CON_TRANSLATE @@ -263,11 +298,25 @@ SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ #endif } +#if SHELL_CON_TRANSLATE==2 +static int mbcsToUtf8InPlaceIfValid(char *pc, int nci, int nco, UINT codePage){ + WCHAR wcOneCode[2]; + int nuo = 0; + int nwConvert = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, + pc, nci, wcOneCode, 2); + if( nwConvert > 0 ){ + nuo = WideCharToMultiByte(CP_UTF8, 0, wcOneCode, nwConvert, pc, nco, 0,0); + } + return nuo; +} +#endif + SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; #if SHELL_CON_TRANSLATE if( pfIn == consoleInfo.pst[0].pf ){ -#define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ +# if SHELL_CON_TRANSLATE==1 +# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ WCHAR wcBuf[SHELL_GULP+1]; int lend = 0, noc = 0; if( consoleInfo.stdinEof ) return 0; @@ -275,7 +324,7 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ while( noc < ncMax-8-1 && !lend ){ /* There is room for at least 2 more characters and a 0-terminator. */ int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; -#undef SHELL_GULP +# undef SHELL_GULP DWORD nbr = 0; BOOL bRC = ReadConsoleW(consoleInfo.pst[0].hx, wcBuf, na, &nbr, 0); if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ @@ -304,7 +353,7 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ } } } - /* Check for ^Z (anywhere in line) too. */ + /* Check for ^Z (anywhere in line) too, to act as EOF. */ while( iseg < noc ){ if( cBuf[iseg]==0x1a ){ consoleInfo.stdinEof = 1; @@ -320,6 +369,52 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( noc == 0 ) return 0; cBuf[noc] = 0; return cBuf; +# elif SHELL_CON_TRANSLATE==2 + /* This is not done efficiently because it may never be used. + ** Also, it is interactive input so it need not be fast. */ + int nco = 0; + /* For converstion to WCHAR, or pre-test of same. */ + UINT ccp = GetConsoleCP(); /* For translation from mbcs. */ + if( ccp == CP_UTF8 ) return fgets(cBuf, ncMax, pfIn); + while( ncMax-nco >= 5 ){ + /* Have space for max UTF-8 group and 0-term. */ + int nug = 0; + int c = getc(pfIn); + if( c < 0 ){ + if( nco > 0 ) break; + else return 0; + } + cBuf[nco] = (char)c; + if( c < 0x80 ){ + ++nco; + if( c == '\n' ) break; + continue; + } + /* Deal with possible mbcs lead byte. */ + nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1, ncMax-nco-1, ccp); + if( nug > 0 ){ + nco += nug; + }else{ + /* Must have just mbcs lead byte; get the trail byte. */ + int ct = getc(pfIn); + if( ct < 0 || ct == '\n' ){ + /* Just drop whatever garbage preceded the newline or. + ** EOF. It's not valid, should not happen, and there + ** is no good way to deal with it, short of bailing. */ + if( ct > 0 ){ + cBuf[nco++] = (int)ct; + } + break; + } + /* Treat ct as bona fide MBCS trailing byte, if valid. */ + cBuf[nco+1] = ct; + nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 2, ncMax-nco-1, ccp); + nco += nug; + } + } + cBuf[nco] = 0; + return cBuf; +# endif }else{ #endif return fgets(cBuf, ncMax, pfIn); diff --git a/ext/consio/console_io.h b/ext/consio/console_io.h index 2cbc2c1f97..e799c71e74 100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@ -8,7 +8,7 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* +******************************************************************************** ** This file exposes various interfaces used for console I/O by the ** SQLite project command-line tools. These interfaces are used at ** either source conglomeration time, compilation time, or run time. @@ -27,15 +27,6 @@ #ifndef SQLITE_INTERNAL_LINKAGE # define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ # include -# include -# include -# if defined(_WIN32) || defined(WIN32) -# undef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# include -# include -# include -# endif #else # define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ #endif @@ -98,6 +89,8 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); ** translation as specified by set{Binary,Text}Mode(). */ SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...); +/* Like fprintfUtf8 except stream is always the recorded output. */ +SQLITE_INTERNAL_LINKAGE int printfUtf8(const char *zFormat, ...); /* ** Render output like fputs(). If the output is going to the diff --git a/manifest b/manifest index 6fec7ee4b3..a2db5a7373 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Move\sconsole_io\slib\sto\sits\sown\ssubdirectory,\setc/consio\s. -D 2023-11-06T15:31:08.971 +C Get\sdependencies\sinto\smake\srecipes.\sGet\slegacy\sconsole\sI/O\s(-DSHELL_LEGACY_CONSOLE_IO)\sworking.\sDue\sto\smovement\sof\sMBCS/UTF-8\stranslation\sinto\straditional\sstream\sI/O\ssimulacra,\sthe\sinput\stranslation\sdoes\snot\shappen\sthe\ssame\sway.\s(It\sworks\sthe\ssame,\sbut\sfails\sdifferently\sand\sa\sbit\sbetter.)\sAdded\sprintf()\sand\sfputs()\slook-alikes,\sand\smade\sCLI\suse\sthem. +D 2023-11-07T02:41:46.723 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 8b59912fc1538f96a08555605c5886cdcc733696ae7f22e374b2a4752196ca20 +F Makefile.in f1ec22cfe53927eb9411ed82e452a0984df0ef88acb7a45024d98c5b6909da12 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 -F Makefile.msc f0cf219350d9af4fba411b4f6306dce2adc897484e8f446de1fb4f40de674d00 +F Makefile.msc 59bb36dba001f0b38212be0794fb838f25371008b180929bcf08aa799694c168 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -50,8 +50,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 7c9d44ae378855778b7c4bb171df336e5f0e629107e56d93fcaaa41b512d253d x src/console_io.c -F ext/consio/console_io.h 92a765da11922b57c899132fd31fb9f6a54802d9ca2489e618a66b7d5334acb9 w src/console_io.h +F ext/consio/console_io.c 5011c039c6224831ebfa7f6522cf4bce72229f50c45c9aa66df0a45acd4690bf x +F ext/consio/console_io.h e6055b6a13a2a9f237e1672f9ef861126a37a61db0e6218a137832557f10ea25 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in 012b84f928dc883b589e27086540df7317b8b465d366b6baac2717b07f59ee0c +F src/shell.c.in b651e2c297bfef8bd063159765b4ffab14f27816cb373b4995a4b411c33ecd51 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 58815f0ad259599a674e7d0fe0d4676a1c4fb8e118c08b619580ff655fb3be40 -R 9369b16a7548c239ad37858f67dafede +P 1d0583f2eb69fdca1cbc55763c0e86a7e32cb7771bfbc2cdf02da4e3fedbfa23 +R 8fe955210f7825f8cd02e07383e28c60 U larrybr -Z ed8eeffcaf90e73880f84d59f5812858 +Z 407fd2a55e54d86f679485ba9f27b473 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 55b7016ccf..d95739e422 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1d0583f2eb69fdca1cbc55763c0e86a7e32cb7771bfbc2cdf02da4e3fedbfa23 \ No newline at end of file +1721dc6a434361c4e2b87c6e677b6dc223432b3cdd5b9eecabaa258889fb2d2a \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index b4a0d1936f..426401e671 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -567,8 +567,9 @@ static char *dynamicContinuePrompt(void){ /* From here onward, fgets() is redirected to the console_io library. */ #define fgets(b,n,f) fgetsUtf8(b,n,f) -/* And, (varargs) utf8_printf(f,z,...) is redirected to the same. */ +/* Also, (varargs) utf8_printf(f,z,...),printf(z,...) so redirected. */ #define utf8_printf fprintfUtf8 +#define printf printfUtf8 /* And, utf8_print(f,z) is redirected to fputsUtf8(z,f) in the library. */ #define utf8_print(f,z) fputsUtf8(z,f) @@ -12427,8 +12428,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char *zHistory; const char *zCharset = ""; int nHistory; -#if SHELL_CON_TRANSLATE +#if SHELL_CON_TRANSLATE==1 zCharset = " (UTF-16 console I/O)"; +#elif SHELL_CON_TRANSLATE==2 + zCharset = " (MBCS console I/O)"; #endif printf( "SQLite version %s %.19s%s\n" /*extra-version-info*/ From 6ca0cfd87aad53309c3d376adb5d90b0819d23ab Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 7 Nov 2023 13:22:49 +0000 Subject: [PATCH 131/347] Flesh out [7a63b5b65a79] to be able to build JNI with or without SQLITE_ENABLE_COLUMN_METADATA. FossilOrigin-Name: fcee41b3d4d2558299ead28cc17f290b9ff1957a84c3feaa0a24872feeb22901 --- ext/jni/src/c/sqlite3-jni.c | 2 ++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 15 ++++++++++++--- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 11 ++++++++--- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index c530651cd7..a5cc7e38d5 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2115,9 +2115,11 @@ WRAP_INT_STMT_INT(1column_1bytes16, sqlite3_column_bytes16) WRAP_INT_STMT(1column_1count, sqlite3_column_count) WRAP_STR_STMT_INT(1column_1decltype, sqlite3_column_decltype) WRAP_STR_STMT_INT(1column_1name, sqlite3_column_name) +#ifdef SQLITE_ENABLE_COLUMN_METADATA WRAP_STR_STMT_INT(1column_1database_1name, sqlite3_column_database_name) WRAP_STR_STMT_INT(1column_1origin_1name, sqlite3_column_origin_name) WRAP_STR_STMT_INT(1column_1table_1name, sqlite3_column_table_name) +#endif WRAP_INT_STMT_INT(1column_1type, sqlite3_column_type) WRAP_INT_STMT(1data_1count, sqlite3_data_count) WRAP_STR_DB_INT(1db_1name, sqlite3_db_name) diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index d1930e1e32..4864b24296 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -622,20 +622,29 @@ public final class CApi { return sqlite3_column_name(stmt.getNativePointer(), ndx); } - static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx); + private static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx); + /** + Only available if built with SQLITE_ENABLE_COLUMN_METADATA. + */ public static String sqlite3_column_database_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_database_name(stmt.getNativePointer(), ndx); } - static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx); + private static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx); + /** + Only available if built with SQLITE_ENABLE_COLUMN_METADATA. + */ public static String sqlite3_column_origin_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_origin_name(stmt.getNativePointer(), ndx); } - static native String sqlite3_column_table_name(@NotNull long ptrToStmt, int ndx); + private static native String sqlite3_column_table_name(@NotNull long ptrToStmt, int ndx); + /** + Only available if built with SQLITE_ENABLE_COLUMN_METADATA. + */ public static String sqlite3_column_table_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_table_name(stmt.getNativePointer(), ndx); } diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index da9abb53c0..f11a644a3b 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -382,9 +382,14 @@ public class Tester1 implements Runnable { stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); affirm( sqlite3_stmt_readonly(stmt) ); affirm( !sqlite3_stmt_busy(stmt) ); - affirm("t".equals(CApi.sqlite3_column_table_name(stmt,0))); - affirm("main".equals(CApi.sqlite3_column_database_name(stmt,0))); - affirm("a".equals(CApi.sqlite3_column_origin_name(stmt,0))); + if( sqlite3_compileoption_used("ENABLE_COLUMN_METADATA") ){ + /* Unlike in native C code, JNI won't trigger an + UnsatisfiedLinkError until these are called (on Linux, at + least). */ + affirm("t".equals(sqlite3_column_table_name(stmt,0))); + affirm("main".equals(sqlite3_column_database_name(stmt,0))); + affirm("a".equals(sqlite3_column_origin_name(stmt,0))); + } int total2 = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ diff --git a/manifest b/manifest index 2b0d48205d..e1f54ab601 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\s-DSQLITE_ENABLE_COLUMN_METADATA\sto\sthe\sJNI\sbuild,\sas\sper\s[forum:9205518c0568fdf0|forum\spost\s9205518c0568fdf0].\sAdd\stests\sfor\sthe\sfunctions\sthat\sflag\senables\sso\sthat\sthe\sbuild\swill\sfail\sif\sthat\sflag\sis\smissing. -D 2023-11-06T21:57:15.302 +C Flesh\sout\s[7a63b5b65a79]\sto\sbe\sable\sto\sbuild\sJNI\swith\sor\swithout\sSQLITE_ENABLE_COLUMN_METADATA. +D 2023-11-07T13:22:49.498 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c b98d822a35ef4438023c3c14816b5ac17bdbd23bc838ff80b6463c3146a75d14 +F ext/jni/src/c/sqlite3-jni.c ac7aa8ef250be4bebf99aa5b610d4a1b5f26578328a458a53db3b075271417ed F ext/jni/src/c/sqlite3-jni.h 9300900f7ec91fffa01445e1ad5815e35a7bece4c9ca07de464641f77195404a F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 41da9b46718b258ad2f195f3dec7265747eff37b6b350b8c427ee290eed0d83c +F ext/jni/src/org/sqlite/jni/capi/CApi.java 75b92b98ee1e621e71466e4bdb3bdff580da688e9873524adffd97db12675eb1 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java ea18a69a16cf60905a1c24f17a0ffe319fa397d009d444135d0809ada1ad448d +F ext/jni/src/org/sqlite/jni/capi/Tester1.java bcfc48cba038e8dc6e08cb9b8a974241e5d9920ab4015c3abf482b7130ac9ba4 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -2142,8 +2142,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 c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 -R a4e5f3d143d3dc87daaa4c71693d0a48 +P 7a63b5b65a79d15658a160d0878c7371941c67e9b48a7442762c68c60b77288a +R efbde6eb422be28e0f5c5e7a6eec616d U stephan -Z 38612cb49abf001f794e1c11b793254d +Z adceda1b2796226e98fdf1b3f6f9cd13 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3fe5e1fd42..26896fc11a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7a63b5b65a79d15658a160d0878c7371941c67e9b48a7442762c68c60b77288a \ No newline at end of file +fcee41b3d4d2558299ead28cc17f290b9ff1957a84c3feaa0a24872feeb22901 \ No newline at end of file From 6dc368e632d2c49e4db77f329e55663870995d21 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 7 Nov 2023 13:44:29 +0000 Subject: [PATCH 132/347] Diverse minor cleanups in the JNI pieces. FossilOrigin-Name: 35233dd900632b997b5e532170a3b2af0ca7f1dccb8407555b93f2b395b0f7b4 --- ext/jni/src/c/sqlite3-jni.c | 124 ++++++------- ext/jni/src/org/sqlite/jni/capi/CApi.java | 170 +++++++++--------- ext/jni/src/org/sqlite/jni/capi/sqlite3.java | 2 +- .../src/org/sqlite/jni/capi/sqlite3_stmt.java | 2 +- manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 155 insertions(+), 163 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index a5cc7e38d5..852b0ccceb 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1474,7 +1474,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, #define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ) #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ) /* -** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 struct +** LongPtrGet_T(X,Y) expects X to be an unqualified sqlite3 struct ** type name and Y to be a native pointer to such an object in the ** form of a jlong value. The jlong is simply cast to (X*). This ** approach is, as of 2023-09-27, supplanting the former approach. We @@ -1483,12 +1483,12 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, ** intptr_t part here is necessary for compatibility with (at least) ** ARM32. */ -#define S3JniLongPtr_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) -#define S3JniLongPtr_sqlite3(JLongAsPtr) S3JniLongPtr_T(sqlite3,JLongAsPtr) -#define S3JniLongPtr_sqlite3_backup(JLongAsPtr) S3JniLongPtr_T(sqlite3_backup,JLongAsPtr) -#define S3JniLongPtr_sqlite3_blob(JLongAsPtr) S3JniLongPtr_T(sqlite3_blob,JLongAsPtr) -#define S3JniLongPtr_sqlite3_stmt(JLongAsPtr) S3JniLongPtr_T(sqlite3_stmt,JLongAsPtr) -#define S3JniLongPtr_sqlite3_value(JLongAsPtr) S3JniLongPtr_T(sqlite3_value,JLongAsPtr) +#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) +#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,JLongAsPtr) +#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,JLongAsPtr) +#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,JLongAsPtr) +#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,JLongAsPtr) +#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,JLongAsPtr) /* ** Extracts the new S3JniDb instance from the free-list, or allocates ** one if needed, associates it with pDb, and returns. Returns NULL @@ -1547,7 +1547,7 @@ static void S3JniDb_xDestroy(void *p){ #define S3JniDb_from_c(sqlite3Ptr) \ ((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0) #define S3JniDb_from_jlong(sqlite3PtrAsLong) \ - S3JniDb_from_c(S3JniLongPtr_T(sqlite3,sqlite3PtrAsLong)) + S3JniDb_from_c(LongPtrGet_T(sqlite3,sqlite3PtrAsLong)) /* ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out @@ -2052,12 +2052,12 @@ static void udf_xInverse(sqlite3_context* cx, int argc, /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ #define WRAP_INT_STMT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt){ \ - return (jint)CName(S3JniLongPtr_sqlite3_stmt(jpStmt)); \ + return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt)); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */ #define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint n){ \ - return (jint)CName(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)n); \ + return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)n); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3_stmt*)). */ #define WRAP_BOOL_STMT(JniNameSuffix,CName) \ @@ -2068,41 +2068,41 @@ static void udf_xInverse(sqlite3_context* cx, int argc, #define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint ndx){ \ return s3jni_utf8_to_jstring( \ - CName(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx), \ + CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx), \ -1); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3*)). */ #define WRAP_BOOL_DB(JniNameSuffix,CName) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ - return CName(S3JniLongPtr_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \ + return CName(LongPtrGet_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ #define WRAP_INT_DB(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ - return (jint)CName(S3JniLongPtr_sqlite3(jpDb)); \ + return (jint)CName(LongPtrGet_sqlite3(jpDb)); \ } /** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ #define WRAP_INT64_DB(JniNameSuffix,CName) \ JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ - return (jlong)CName(S3JniLongPtr_sqlite3(jpDb)); \ + return (jlong)CName(LongPtrGet_sqlite3(jpDb)); \ } /** Create a trivial JNI wrapper for (jstring CName(sqlite3*,int)). */ #define WRAP_STR_DB_INT(JniNameSuffix,CName) \ JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpDb, jint ndx){ \ return s3jni_utf8_to_jstring( \ - CName(S3JniLongPtr_sqlite3(jpDb), (int)ndx), \ + CName(LongPtrGet_sqlite3(jpDb), (int)ndx), \ -1); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ #define WRAP_INT_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \ + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \ return (jint)(sv ? CName(sv): DfltOnNull); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3_value*)). */ #define WRAP_BOOL_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \ + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \ return (jint)(sv ? CName(sv) : DfltOnNull) \ ? JNI_TRUE : JNI_FALSE; \ } @@ -2317,7 +2317,7 @@ S3JniApi(sqlite3_backup_finish(),jint,1backup_1finish)( ){ int rc = 0; if( jpBack!=0 ){ - rc = sqlite3_backup_finish( S3JniLongPtr_sqlite3_backup(jpBack) ); + rc = sqlite3_backup_finish( LongPtrGet_sqlite3_backup(jpBack) ); } return rc; } @@ -2326,8 +2326,8 @@ S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)( JniArgsEnvClass, jlong jpDbDest, jstring jTDest, jlong jpDbSrc, jstring jTSrc ){ - sqlite3 * const pDest = S3JniLongPtr_sqlite3(jpDbDest); - sqlite3 * const pSrc = S3JniLongPtr_sqlite3(jpDbSrc); + sqlite3 * const pDest = LongPtrGet_sqlite3(jpDbDest); + sqlite3 * const pSrc = LongPtrGet_sqlite3(jpDbSrc); char * const zDest = s3jni_jstring_to_utf8(jTDest, 0); char * const zSrc = s3jni_jstring_to_utf8(jTSrc, 0); jobject rv = 0; @@ -2350,19 +2350,19 @@ S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)( S3JniApi(sqlite3_backup_pagecount(),jint,1backup_1pagecount)( JniArgsEnvClass, jlong jpBack ){ - return sqlite3_backup_pagecount(S3JniLongPtr_sqlite3_backup(jpBack)); + return sqlite3_backup_pagecount(LongPtrGet_sqlite3_backup(jpBack)); } S3JniApi(sqlite3_backup_remaining(),jint,1backup_1remaining)( JniArgsEnvClass, jlong jpBack ){ - return sqlite3_backup_remaining(S3JniLongPtr_sqlite3_backup(jpBack)); + return sqlite3_backup_remaining(LongPtrGet_sqlite3_backup(jpBack)); } S3JniApi(sqlite3_backup_step(),jint,1backup_1step)( JniArgsEnvClass, jlong jpBack, jint nPage ){ - return sqlite3_backup_step(S3JniLongPtr_sqlite3_backup(jpBack), (int)nPage); + return sqlite3_backup_step(LongPtrGet_sqlite3_backup(jpBack), (int)nPage); } S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( @@ -2375,13 +2375,13 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( if( nMax>nBA ){ nMax = nBA; } - rc = sqlite3_bind_blob(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, + rc = sqlite3_bind_blob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); s3jni_jbyteArray_release(baData, pBuf); }else{ rc = baData ? SQLITE_NOMEM - : sqlite3_bind_null( S3JniLongPtr_sqlite3_stmt(jpStmt), ndx ); + : sqlite3_bind_null( LongPtrGet_sqlite3_stmt(jpStmt), ndx ); } return (jint)rc; } @@ -2389,20 +2389,20 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val ){ - return (jint)sqlite3_bind_double(S3JniLongPtr_sqlite3_stmt(jpStmt), + return (jint)sqlite3_bind_double(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val); } S3JniApi(sqlite3_bind_int(),jint,1bind_1int)( JniArgsEnvClass, jlong jpStmt, jint ndx, jint val ){ - return (jint)sqlite3_bind_int(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, (int)val); + return (jint)sqlite3_bind_int(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); } S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong val ){ - return (jint)sqlite3_bind_int64(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); + return (jint)sqlite3_bind_int64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } /* @@ -2411,7 +2411,7 @@ S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( JniArgsEnvClass, jlong jpStmt, jint ndx, jobject val ){ - sqlite3_stmt * const pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt); + sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); int rc = SQLITE_MISUSE; if(pStmt){ @@ -2431,13 +2431,13 @@ S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( S3JniApi(sqlite3_bind_null(),jint,1bind_1null)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ - return (jint)sqlite3_bind_null(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx); + return (jint)sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); } S3JniApi(sqlite3_bind_parameter_count(),jint,1bind_1parameter_1count)( JniArgsEnvClass, jlong jpStmt ){ - return (jint)sqlite3_bind_parameter_count(S3JniLongPtr_sqlite3_stmt(jpStmt)); + return (jint)sqlite3_bind_parameter_count(LongPtrGet_sqlite3_stmt(jpStmt)); } S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)( @@ -2446,7 +2446,7 @@ S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)( int rc = 0; jbyte * const pBuf = s3jni_jbyteArray_bytes(jName); if( pBuf ){ - rc = sqlite3_bind_parameter_index(S3JniLongPtr_sqlite3_stmt(jpStmt), + rc = sqlite3_bind_parameter_index(LongPtrGet_sqlite3_stmt(jpStmt), (const char *)pBuf); s3jni_jbyteArray_release(jName, pBuf); } @@ -2457,7 +2457,7 @@ S3JniApi(sqlite3_bind_parameter_name(),jstring,1bind_1parameter_1name)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ const char *z = - sqlite3_bind_parameter_name(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx); + sqlite3_bind_parameter_name(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); return z ? s3jni_utf8_to_jstring(z, -1) : 0; } @@ -2479,14 +2479,14 @@ static int s3jni__bind_text(int is16, JNIEnv *env, jlong jpStmt, jint ndx, such cases, we do not expose the byte-limit arguments in the public API. */ rc = is16 - ? sqlite3_bind_text16(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, + ? sqlite3_bind_text16(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT) - : sqlite3_bind_text(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, + : sqlite3_bind_text(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, (int)nMax, SQLITE_TRANSIENT); }else{ rc = baData - ? sqlite3_bind_null(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx) + ? sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx) : SQLITE_NOMEM; } s3jni_jbyteArray_release(baData, pBuf); @@ -2510,9 +2510,9 @@ S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue ){ int rc = 0; - sqlite3_stmt * pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt); + sqlite3_stmt * pStmt = LongPtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ - sqlite3_value *v = S3JniLongPtr_sqlite3_value(jpValue); + sqlite3_value *v = LongPtrGet_sqlite3_value(jpValue); if( v ){ rc = sqlite3_bind_value(pStmt, (int)ndx, v); }else{ @@ -2527,27 +2527,27 @@ S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)( JniArgsEnvClass, jlong jpStmt, jint ndx, jint n ){ - return (jint)sqlite3_bind_zeroblob(S3JniLongPtr_sqlite3_stmt(jpStmt), + return (jint)sqlite3_bind_zeroblob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n); } S3JniApi(sqlite3_bind_zeroblob64(),jint,1bind_1zeroblob64)( JniArgsEnvClass, jlong jpStmt, jint ndx, jlong n ){ - return (jint)sqlite3_bind_zeroblob64(S3JniLongPtr_sqlite3_stmt(jpStmt), + return (jint)sqlite3_bind_zeroblob64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); } S3JniApi(sqlite3_blob_bytes(),jint,1blob_1bytes)( JniArgsEnvClass, jlong jpBlob ){ - return sqlite3_blob_bytes(S3JniLongPtr_sqlite3_blob(jpBlob)); + return sqlite3_blob_bytes(LongPtrGet_sqlite3_blob(jpBlob)); } S3JniApi(sqlite3_blob_close(),jint,1blob_1close)( JniArgsEnvClass, jlong jpBlob ){ - sqlite3_blob * const b = S3JniLongPtr_sqlite3_blob(jpBlob); + sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); return b ? (jint)sqlite3_blob_close(b) : SQLITE_MISUSE; } @@ -2555,7 +2555,7 @@ S3JniApi(sqlite3_blob_open(),jint,1blob_1open)( JniArgsEnvClass, jlong jpDb, jstring jDbName, jstring jTbl, jstring jCol, jlong jRowId, jint flags, jobject jOut ){ - sqlite3 * const db = S3JniLongPtr_sqlite3(jpDb); + sqlite3 * const db = LongPtrGet_sqlite3(jpDb); sqlite3_blob * pBlob = 0; char * zDbName = 0, * zTableName = 0, * zColumnName = 0; int rc; @@ -2589,7 +2589,7 @@ S3JniApi(sqlite3_blob_read(),jint,1blob_1read)( int rc = jTgt ? (pBa ? SQLITE_MISUSE : SQLITE_NOMEM) : SQLITE_MISUSE; if( pBa ){ jsize const nTgt = (*env)->GetArrayLength(env, jTgt); - rc = sqlite3_blob_read(S3JniLongPtr_sqlite3_blob(jpBlob), pBa, + rc = sqlite3_blob_read(LongPtrGet_sqlite3_blob(jpBlob), pBa, (int)nTgt, (int)iOffset); if( 0==rc ){ s3jni_jbyteArray_commit(jTgt, pBa); @@ -2603,14 +2603,14 @@ S3JniApi(sqlite3_blob_read(),jint,1blob_1read)( S3JniApi(sqlite3_blob_reopen(),jint,1blob_1reopen)( JniArgsEnvClass, jlong jpBlob, jlong iNewRowId ){ - return (jint)sqlite3_blob_reopen(S3JniLongPtr_sqlite3_blob(jpBlob), + return (jint)sqlite3_blob_reopen(LongPtrGet_sqlite3_blob(jpBlob), (sqlite3_int64)iNewRowId); } S3JniApi(sqlite3_blob_write(),jint,1blob_1write)( JniArgsEnvClass, jlong jpBlob, jbyteArray jBa, jint iOffset ){ - sqlite3_blob * const b = S3JniLongPtr_sqlite3_blob(jpBlob); + sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); jbyte * const pBuf = b ? s3jni_jbyteArray_bytes(jBa) : 0; const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jBa) : 0; int rc = SQLITE_MISUSE; @@ -2872,7 +2872,7 @@ S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( JniArgsEnvClass, jlong jpStmt, jint ndx ){ - sqlite3_stmt * const stmt = S3JniLongPtr_sqlite3_stmt(jpStmt); + sqlite3_stmt * const stmt = LongPtrGet_sqlite3_stmt(jpStmt); jobject rv = 0; if( stmt ){ sqlite3 * const db = sqlite3_db_handle(stmt); @@ -3612,7 +3612,7 @@ S3JniApi(sqlite3_finalize(),jint,1finalize)( JniArgsEnvClass, jlong jpStmt ){ return jpStmt - ? sqlite3_finalize(S3JniLongPtr_sqlite3_stmt(jpStmt)) + ? sqlite3_finalize(LongPtrGet_sqlite3_stmt(jpStmt)) : 0; } @@ -3841,7 +3841,7 @@ jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self, sqlite3_stmt * pStmt = 0; jobject jStmt = 0; const char * zTail = 0; - sqlite3 * const pDb = S3JniLongPtr_sqlite3(jpDb); + sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); jbyte * const pBuf = pDb ? s3jni_jbyteArray_bytes(baSql) : 0; int rc = SQLITE_ERROR; @@ -4095,7 +4095,7 @@ S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)( static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jlong jpDb, jint iCol, jobject jOut){ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - sqlite3 * const pDb = S3JniLongPtr_sqlite3(jpDb); + sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); int rc = SQLITE_MISUSE; if( pDb ){ sqlite3_value * pOut = 0; @@ -4852,7 +4852,7 @@ S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)( S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const jbyte * pBytes = sv ? sqlite3_value_blob(sv) : 0; int const nLen = pBytes ? sqlite3_value_bytes(sv) : 0; @@ -4865,14 +4865,14 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( S3JniApi(sqlite3_value_bytes(),int,1value_1bytes)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_bytes(sv) : 0; } S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_bytes16(sv) : 0; } @@ -4880,7 +4880,7 @@ S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)( S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jdouble) (sv ? sqlite3_value_double(sv) : 0.0); } @@ -4888,7 +4888,7 @@ S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); sqlite3_value * const sd = sv ? sqlite3_value_dup(sv) : 0; jobject rv = sd ? new_java_sqlite3_value(env, sd) : 0; if( sd && !rv ) { @@ -4901,7 +4901,7 @@ S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)( S3JniApi(sqlite3_value_free(),void,1value_1free)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); if( sv ){ sqlite3_value_free(sv); } @@ -4910,21 +4910,21 @@ S3JniApi(sqlite3_value_free(),void,1value_1free)( S3JniApi(sqlite3_value_int(),jint,1value_1int)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jint) (sv ? sqlite3_value_int(sv) : 0); } S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return (jlong) (sv ? sqlite3_value_int64(sv) : 0LL); } S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_pointer(sv, s3jni__value_jref_key) : 0; @@ -4933,7 +4933,7 @@ S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_new_jbyteArray(p, n) : 0; @@ -4944,7 +4944,7 @@ S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( S3JniApi(sqlite3_value_text(),jstring,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; @@ -4954,7 +4954,7 @@ S3JniApi(sqlite3_value_text(),jstring,1value_1text)( S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); const int n = sv ? sqlite3_value_bytes16(sv) : 0; const void * const p = sv ? sqlite3_value_text16(sv) : 0; return p ? s3jni_text16_to_jstring(env, p, n) : 0; diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 4864b24296..8bdcb3cf2d 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -164,13 +164,13 @@ public final class CApi { */ public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback); - static native int sqlite3_backup_finish(@NotNull long ptrToBackup); + private static native int sqlite3_backup_finish(@NotNull long ptrToBackup); public static int sqlite3_backup_finish(@NotNull sqlite3_backup b){ - return sqlite3_backup_finish(b.clearNativePointer()); + return null==b ? 0 : sqlite3_backup_finish(b.clearNativePointer()); } - static native sqlite3_backup sqlite3_backup_init( + private static native sqlite3_backup sqlite3_backup_init( @NotNull long ptrToDbDest, @NotNull String destTableName, @NotNull long ptrToDbSrc, @NotNull String srcTableName ); @@ -183,25 +183,25 @@ public final class CApi { dbSrc.getNativePointer(), srcTableName ); } - static native int sqlite3_backup_pagecount(@NotNull long ptrToBackup); + private static native int sqlite3_backup_pagecount(@NotNull long ptrToBackup); public static int sqlite3_backup_pagecount(@NotNull sqlite3_backup b){ return sqlite3_backup_pagecount(b.getNativePointer()); } - static native int sqlite3_backup_remaining(@NotNull long ptrToBackup); + private static native int sqlite3_backup_remaining(@NotNull long ptrToBackup); public static int sqlite3_backup_remaining(@NotNull sqlite3_backup b){ return sqlite3_backup_remaining(b.getNativePointer()); } - static native int sqlite3_backup_step(@NotNull long ptrToBackup, int nPage); + private static native int sqlite3_backup_step(@NotNull long ptrToBackup, int nPage); public static int sqlite3_backup_step(@NotNull sqlite3_backup b, int nPage){ return sqlite3_backup_step(b.getNativePointer(), nPage); } - static native int sqlite3_bind_blob( + private static native int sqlite3_bind_blob( @NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int n ); @@ -223,7 +223,7 @@ public final class CApi { : sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, data.length); } - static native int sqlite3_bind_double( + private static native int sqlite3_bind_double( @NotNull long ptrToStmt, int ndx, double v ); @@ -233,7 +233,7 @@ public final class CApi { return sqlite3_bind_double(stmt.getNativePointer(), ndx, v); } - static native int sqlite3_bind_int( + private static native int sqlite3_bind_int( @NotNull long ptrToStmt, int ndx, int v ); @@ -243,7 +243,7 @@ public final class CApi { return sqlite3_bind_int(stmt.getNativePointer(), ndx, v); } - static native int sqlite3_bind_int64( + private static native int sqlite3_bind_int64( @NotNull long ptrToStmt, int ndx, long v ); @@ -251,7 +251,7 @@ public final class CApi { return sqlite3_bind_int64( stmt.getNativePointer(), ndx, v ); } - static native int sqlite3_bind_java_object( + private static native int sqlite3_bind_java_object( @NotNull long ptrToStmt, int ndx, @Nullable Object o ); @@ -267,13 +267,13 @@ public final class CApi { return sqlite3_bind_java_object(stmt.getNativePointer(), ndx, o); } - static native int sqlite3_bind_null(@NotNull long ptrToStmt, int ndx); + private static native int sqlite3_bind_null(@NotNull long ptrToStmt, int ndx); public static int sqlite3_bind_null(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_bind_null(stmt.getNativePointer(), ndx); } - static native int sqlite3_bind_parameter_count(@NotNull long ptrToStmt); + private static native int sqlite3_bind_parameter_count(@NotNull long ptrToStmt); public static int sqlite3_bind_parameter_count(@NotNull sqlite3_stmt stmt){ return sqlite3_bind_parameter_count(stmt.getNativePointer()); @@ -300,7 +300,7 @@ public final class CApi { return null==utf8 ? 0 : sqlite3_bind_parameter_index(stmt.getNativePointer(), utf8); } - static native String sqlite3_bind_parameter_name( + private static native String sqlite3_bind_parameter_name( @NotNull long ptrToStmt, int index ); @@ -308,7 +308,7 @@ public final class CApi { return sqlite3_bind_parameter_name(stmt.getNativePointer(), index); } - static native int sqlite3_bind_text( + private static native int sqlite3_bind_text( @NotNull long ptrToStmt, int ndx, @Nullable byte[] utf8, int maxBytes ); @@ -352,7 +352,7 @@ public final class CApi { : sqlite3_bind_text(stmt.getNativePointer(), ndx, utf8, utf8.length); } - static native int sqlite3_bind_text16( + private static native int sqlite3_bind_text16( @NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int maxBytes ); @@ -393,7 +393,7 @@ public final class CApi { : sqlite3_bind_text16(stmt.getNativePointer(), ndx, data, data.length); } - static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue); + private static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue); /** Functions like the C-level sqlite3_bind_value(), or @@ -404,13 +404,13 @@ public final class CApi { null==val ? 0L : val.getNativePointer()); } - static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n); + private static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n); public static int sqlite3_bind_zeroblob(@NotNull sqlite3_stmt stmt, int ndx, int n){ return sqlite3_bind_zeroblob(stmt.getNativePointer(), ndx, n); } - static native int sqlite3_bind_zeroblob64( + private static native int sqlite3_bind_zeroblob64( @NotNull long ptrToStmt, int ndx, long n ); @@ -418,19 +418,19 @@ public final class CApi { return sqlite3_bind_zeroblob64(stmt.getNativePointer(), ndx, n); } - static native int sqlite3_blob_bytes(@NotNull long ptrToBlob); + private static native int sqlite3_blob_bytes(@NotNull long ptrToBlob); public static int sqlite3_blob_bytes(@NotNull sqlite3_blob blob){ return sqlite3_blob_bytes(blob.getNativePointer()); } - static native int sqlite3_blob_close(@Nullable long ptrToBlob); + private static native int sqlite3_blob_close(@Nullable long ptrToBlob); public static int sqlite3_blob_close(@Nullable sqlite3_blob blob){ - return sqlite3_blob_close(blob.clearNativePointer()); + return null==blob ? 0 : sqlite3_blob_close(blob.clearNativePointer()); } - static native int sqlite3_blob_open( + private static native int sqlite3_blob_open( @NotNull long ptrToDb, @NotNull String dbName, @NotNull String tableName, @NotNull String columnName, long iRow, int flags, @NotNull OutputPointer.sqlite3_blob out @@ -458,7 +458,7 @@ public final class CApi { return out.take(); }; - static native int sqlite3_blob_read( + private static native int sqlite3_blob_read( @NotNull long ptrToBlob, @NotNull byte[] target, int iOffset ); @@ -468,7 +468,7 @@ public final class CApi { return sqlite3_blob_read(b.getNativePointer(), target, iOffset); } - static native int sqlite3_blob_reopen( + private static native int sqlite3_blob_reopen( @NotNull long ptrToBlob, long newRowId ); @@ -476,7 +476,7 @@ public final class CApi { return sqlite3_blob_reopen(b.getNativePointer(), newRowId); } - static native int sqlite3_blob_write( + private static native int sqlite3_blob_write( @NotNull long ptrToBlob, @NotNull byte[] bytes, int iOffset ); @@ -486,7 +486,7 @@ public final class CApi { return sqlite3_blob_write(b.getNativePointer(), bytes, iOffset); } - static native int sqlite3_busy_handler( + private static native int sqlite3_busy_handler( @NotNull long ptrToDb, @Nullable BusyHandlerCallback handler ); @@ -501,7 +501,7 @@ public final class CApi { return sqlite3_busy_handler(db.getNativePointer(), handler); } - static native int sqlite3_busy_timeout(@NotNull long ptrToDb, int ms); + private static native int sqlite3_busy_timeout(@NotNull long ptrToDb, int ms); public static int sqlite3_busy_timeout(@NotNull sqlite3 db, int ms){ return sqlite3_busy_timeout(db.getNativePointer(), ms); @@ -511,64 +511,59 @@ public final class CApi { @NotNull AutoExtensionCallback ax ); - static native int sqlite3_changes(@NotNull long ptrToDb); + private static native int sqlite3_changes(@NotNull long ptrToDb); public static int sqlite3_changes(@NotNull sqlite3 db){ return sqlite3_changes(db.getNativePointer()); } - static native long sqlite3_changes64(@NotNull long ptrToDb); + private static native long sqlite3_changes64(@NotNull long ptrToDb); public static long sqlite3_changes64(@NotNull sqlite3 db){ return sqlite3_changes64(db.getNativePointer()); } - static native int sqlite3_clear_bindings(@NotNull long ptrToStmt); + private static native int sqlite3_clear_bindings(@NotNull long ptrToStmt); public static int sqlite3_clear_bindings(@NotNull sqlite3_stmt stmt){ return sqlite3_clear_bindings(stmt.getNativePointer()); } - static native int sqlite3_close(@Nullable long ptrToDb); + private static native int sqlite3_close(@Nullable long ptrToDb); public static int sqlite3_close(@Nullable sqlite3 db){ - int rc = 0; - if( null!=db ){ - rc = sqlite3_close(db.getNativePointer()); - if( 0==rc ) db.clearNativePointer(); - } - return rc; + return null==db ? 0 : sqlite3_close(db.clearNativePointer()); } - static native int sqlite3_close_v2(@Nullable long ptrToDb); + private static native int sqlite3_close_v2(@Nullable long ptrToDb); public static int sqlite3_close_v2(@Nullable sqlite3 db){ - return db==null ? 0 : sqlite3_close_v2(db.clearNativePointer()); + return null==db ? 0 : sqlite3_close_v2(db.clearNativePointer()); } public static native byte[] sqlite3_column_blob( @NotNull sqlite3_stmt stmt, int ndx ); - static native int sqlite3_column_bytes(@NotNull long ptrToStmt, int ndx); + private static native int sqlite3_column_bytes(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_bytes(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_bytes(stmt.getNativePointer(), ndx); } - static native int sqlite3_column_bytes16(@NotNull long ptrToStmt, int ndx); + private static native int sqlite3_column_bytes16(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_bytes16(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_bytes16(stmt.getNativePointer(), ndx); } - static native int sqlite3_column_count(@NotNull long ptrToStmt); + private static native int sqlite3_column_count(@NotNull long ptrToStmt); public static int sqlite3_column_count(@NotNull sqlite3_stmt stmt){ return sqlite3_column_count(stmt.getNativePointer()); } - static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx); + private static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_decltype(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_decltype(stmt.getNativePointer(), ndx); @@ -586,7 +581,7 @@ public final class CApi { @NotNull sqlite3_stmt stmt, int ndx ); - static native Object sqlite3_column_java_object( + private static native Object sqlite3_column_java_object( @NotNull long ptrToStmt, int ndx ); @@ -616,7 +611,7 @@ public final class CApi { return type.isInstance(o) ? (T)o : null; } - static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx); + private static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_name(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_name(stmt.getNativePointer(), ndx); @@ -702,7 +697,7 @@ public final class CApi { // return rv; // } - static native int sqlite3_column_type(@NotNull long ptrToStmt, int ndx); + private static native int sqlite3_column_type(@NotNull long ptrToStmt, int ndx); public static int sqlite3_column_type(@NotNull sqlite3_stmt stmt, int ndx){ return sqlite3_column_type(stmt.getNativePointer(), ndx); @@ -712,7 +707,7 @@ public final class CApi { @NotNull sqlite3_stmt stmt, int ndx ); - static native int sqlite3_collation_needed( + private static native int sqlite3_collation_needed( @NotNull long ptrToDb, @Nullable CollationNeededCallback callback ); @@ -726,7 +721,7 @@ public final class CApi { return sqlite3_collation_needed(db.getNativePointer(), callback); } - static native CommitHookCallback sqlite3_commit_hook( + private static native CommitHookCallback sqlite3_commit_hook( @NotNull long ptrToDb, @Nullable CommitHookCallback hook ); @@ -825,7 +820,7 @@ public final class CApi { int nArg, int eTextRep, @NotNull SQLFunction func ); - static native int sqlite3_data_count(@NotNull long ptrToStmt); + private static native int sqlite3_data_count(@NotNull long ptrToStmt); public static int sqlite3_data_count(@NotNull sqlite3_stmt stmt){ return sqlite3_data_count(stmt.getNativePointer()); @@ -879,7 +874,7 @@ public final class CApi { public static native String sqlite3_errmsg(@NotNull sqlite3 db); - static native int sqlite3_error_offset(@NotNull long ptrToDb); + private static native int sqlite3_error_offset(@NotNull long ptrToDb); /** Note that the returned byte offset values assume UTF-8-encoded @@ -893,7 +888,7 @@ public final class CApi { public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt); - static native int sqlite3_extended_errcode(@NotNull long ptrToDb); + private static native int sqlite3_extended_errcode(@NotNull long ptrToDb); public static int sqlite3_extended_errcode(@NotNull sqlite3 db){ return sqlite3_extended_errcode(db.getNativePointer()); @@ -903,7 +898,7 @@ public final class CApi { @NotNull sqlite3 db, boolean on ); - static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb); + private static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb); public static boolean sqlite3_get_autocommit(@NotNull sqlite3 db){ return sqlite3_get_autocommit(db.getNativePointer()); @@ -913,7 +908,7 @@ public final class CApi { @NotNull sqlite3_context cx, int n ); - static native int sqlite3_finalize(long ptrToStmt); + private static native int sqlite3_finalize(long ptrToStmt); public static int sqlite3_finalize(@NotNull sqlite3_stmt stmt){ return null==stmt ? 0 : sqlite3_finalize(stmt.clearNativePointer()); @@ -1285,7 +1280,7 @@ public final class CApi { return sqlite3_prepare_multi(db, sql, 0, p); } - static native int sqlite3_preupdate_blobwrite(@NotNull long ptrToDb); + private static native int sqlite3_preupdate_blobwrite(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this @@ -1296,7 +1291,7 @@ public final class CApi { return sqlite3_preupdate_blobwrite(db.getNativePointer()); } - static native int sqlite3_preupdate_count(@NotNull long ptrToDb); + private static native int sqlite3_preupdate_count(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this @@ -1307,7 +1302,7 @@ public final class CApi { return sqlite3_preupdate_count(db.getNativePointer()); } - static native int sqlite3_preupdate_depth(@NotNull long ptrToDb); + private static native int sqlite3_preupdate_depth(@NotNull long ptrToDb); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this @@ -1318,7 +1313,7 @@ public final class CApi { return sqlite3_preupdate_depth(db.getNativePointer()); } - static native PreupdateHookCallback sqlite3_preupdate_hook( + private static native PreupdateHookCallback sqlite3_preupdate_hook( @NotNull long ptrToDb, @Nullable PreupdateHookCallback hook ); @@ -1333,7 +1328,7 @@ public final class CApi { return sqlite3_preupdate_hook(db.getNativePointer(), hook); } - static native int sqlite3_preupdate_new(@NotNull long ptrToDb, int col, + private static native int sqlite3_preupdate_new(@NotNull long ptrToDb, int col, @NotNull OutputPointer.sqlite3_value out); /** @@ -1356,7 +1351,7 @@ public final class CApi { return out.take(); } - static native int sqlite3_preupdate_old(@NotNull long ptrToDb, int col, + private static native int sqlite3_preupdate_old(@NotNull long ptrToDb, int col, @NotNull OutputPointer.sqlite3_value out); /** @@ -1407,7 +1402,7 @@ public final class CApi { results in the C-level sqlite3_result_error() being called with a complaint about the invalid argument. */ - static native void sqlite3_result_error( + private static native void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); @@ -1479,9 +1474,6 @@ public final class CApi { cross-language semantic mismatch and (B) Java doesn't need that argument for its intended purpose (type safety). -

    Note that there is no sqlite3_column_java_object(), as the - C-level API has no sqlite3_column_pointer() to proxy. - @see #sqlite3_value_java_object @see #sqlite3_bind_java_object */ @@ -1684,7 +1676,7 @@ public final class CApi { } } - static native RollbackHookCallback sqlite3_rollback_hook( + private static native RollbackHookCallback sqlite3_rollback_hook( @NotNull long ptrToDb, @Nullable RollbackHookCallback hook ); @@ -1740,13 +1732,13 @@ public final class CApi { public static native boolean sqlite3_stmt_busy(@NotNull sqlite3_stmt stmt); - static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op); + private static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op); public static int sqlite3_stmt_explain(@NotNull sqlite3_stmt stmt, int op){ return sqlite3_stmt_explain(stmt.getNativePointer(), op); } - static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt); + private static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt); public static int sqlite3_stmt_isexplain(@NotNull sqlite3_stmt stmt){ return sqlite3_stmt_isexplain(stmt.getNativePointer()); @@ -1796,7 +1788,7 @@ public final class CApi { (int)escChar); } - static native int sqlite3_system_errno(@NotNull long ptrToDb); + private static native int sqlite3_system_errno(@NotNull long ptrToDb); public static int sqlite3_system_errno(@NotNull sqlite3 db){ return sqlite3_system_errno(db.getNativePointer()); @@ -1842,13 +1834,13 @@ public final class CApi { public static native int sqlite3_threadsafe(); - static native int sqlite3_total_changes(@NotNull long ptrToDb); + private static native int sqlite3_total_changes(@NotNull long ptrToDb); public static int sqlite3_total_changes(@NotNull sqlite3 db){ return sqlite3_total_changes(db.getNativePointer()); } - static native long sqlite3_total_changes64(@NotNull long ptrToDb); + private static native long sqlite3_total_changes64(@NotNull long ptrToDb); public static long sqlite3_total_changes64(@NotNull sqlite3 db){ return sqlite3_total_changes64(db.getNativePointer()); @@ -1871,7 +1863,7 @@ public final class CApi { @NotNull sqlite3 db, @Nullable String zSchema ); - static native UpdateHookCallback sqlite3_update_hook( + private static native UpdateHookCallback sqlite3_update_hook( @NotNull long ptrToDb, @Nullable UpdateHookCallback hook ); @@ -1891,67 +1883,67 @@ public final class CApi { sqlite3_create_function(). */ - static native byte[] sqlite3_value_blob(@NotNull long ptrToValue); + private static native byte[] sqlite3_value_blob(@NotNull long ptrToValue); public static byte[] sqlite3_value_blob(@NotNull sqlite3_value v){ return sqlite3_value_blob(v.getNativePointer()); } - static native int sqlite3_value_bytes(@NotNull long ptrToValue); + private static native int sqlite3_value_bytes(@NotNull long ptrToValue); public static int sqlite3_value_bytes(@NotNull sqlite3_value v){ return sqlite3_value_bytes(v.getNativePointer()); } - static native int sqlite3_value_bytes16(@NotNull long ptrToValue); + private static native int sqlite3_value_bytes16(@NotNull long ptrToValue); public static int sqlite3_value_bytes16(@NotNull sqlite3_value v){ return sqlite3_value_bytes16(v.getNativePointer()); } - static native double sqlite3_value_double(@NotNull long ptrToValue); + private static native double sqlite3_value_double(@NotNull long ptrToValue); public static double sqlite3_value_double(@NotNull sqlite3_value v){ return sqlite3_value_double(v.getNativePointer()); } - static native sqlite3_value sqlite3_value_dup(@NotNull long ptrToValue); + private static native sqlite3_value sqlite3_value_dup(@NotNull long ptrToValue); public static sqlite3_value sqlite3_value_dup(@NotNull sqlite3_value v){ return sqlite3_value_dup(v.getNativePointer()); } - static native int sqlite3_value_encoding(@NotNull long ptrToValue); + private static native int sqlite3_value_encoding(@NotNull long ptrToValue); public static int sqlite3_value_encoding(@NotNull sqlite3_value v){ return sqlite3_value_encoding(v.getNativePointer()); } - static native void sqlite3_value_free(@Nullable long ptrToValue); + private static native void sqlite3_value_free(@Nullable long ptrToValue); public static void sqlite3_value_free(@Nullable sqlite3_value v){ - sqlite3_value_free(v.getNativePointer()); + if( null!=v ) sqlite3_value_free(v.clearNativePointer()); } - static native boolean sqlite3_value_frombind(@NotNull long ptrToValue); + private static native boolean sqlite3_value_frombind(@NotNull long ptrToValue); public static boolean sqlite3_value_frombind(@NotNull sqlite3_value v){ return sqlite3_value_frombind(v.getNativePointer()); } - static native int sqlite3_value_int(@NotNull long ptrToValue); + private static native int sqlite3_value_int(@NotNull long ptrToValue); public static int sqlite3_value_int(@NotNull sqlite3_value v){ return sqlite3_value_int(v.getNativePointer()); } - static native long sqlite3_value_int64(@NotNull long ptrToValue); + private static native long sqlite3_value_int64(@NotNull long ptrToValue); public static long sqlite3_value_int64(@NotNull sqlite3_value v){ return sqlite3_value_int64(v.getNativePointer()); } - static native Object sqlite3_value_java_object(@NotNull long ptrToValue); + private static native Object sqlite3_value_java_object(@NotNull long ptrToValue); /** If the given value was set using {@link @@ -1977,25 +1969,25 @@ public final class CApi { return type.isInstance(o) ? (T)o : null; } - static native int sqlite3_value_nochange(@NotNull long ptrToValue); + private static native int sqlite3_value_nochange(@NotNull long ptrToValue); public static int sqlite3_value_nochange(@NotNull sqlite3_value v){ return sqlite3_value_nochange(v.getNativePointer()); } - static native int sqlite3_value_numeric_type(@NotNull long ptrToValue); + private static native int sqlite3_value_numeric_type(@NotNull long ptrToValue); public static int sqlite3_value_numeric_type(@NotNull sqlite3_value v){ return sqlite3_value_numeric_type(v.getNativePointer()); } - static native int sqlite3_value_subtype(@NotNull long ptrToValue); + private static native int sqlite3_value_subtype(@NotNull long ptrToValue); public static int sqlite3_value_subtype(@NotNull sqlite3_value v){ return sqlite3_value_subtype(v.getNativePointer()); } - static native byte[] sqlite3_value_text(@NotNull long ptrToValue); + private static native byte[] sqlite3_value_text(@NotNull long ptrToValue); /** Functions identially to the C API, and this note is just to @@ -2007,13 +1999,13 @@ public final class CApi { return sqlite3_value_text(v.getNativePointer()); } - static native String sqlite3_value_text16(@NotNull long ptrToValue); + private static native String sqlite3_value_text16(@NotNull long ptrToValue); public static String sqlite3_value_text16(@NotNull sqlite3_value v){ return sqlite3_value_text16(v.getNativePointer()); } - static native int sqlite3_value_type(@NotNull long ptrToValue); + private static native int sqlite3_value_type(@NotNull long ptrToValue); public static int sqlite3_value_type(@NotNull sqlite3_value v){ return sqlite3_value_type(v.getNativePointer()); diff --git a/ext/jni/src/org/sqlite/jni/capi/sqlite3.java b/ext/jni/src/org/sqlite/jni/capi/sqlite3.java index 901317f0ef..cc6f2e6e8d 100644 --- a/ext/jni/src/org/sqlite/jni/capi/sqlite3.java +++ b/ext/jni/src/org/sqlite/jni/capi/sqlite3.java @@ -38,6 +38,6 @@ public final class sqlite3 extends NativePointerHolder } @Override public void close(){ - CApi.sqlite3_close_v2(this.clearNativePointer()); + CApi.sqlite3_close_v2(this); } } diff --git a/ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java b/ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java index 3b8b71f8a5..564891c727 100644 --- a/ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java +++ b/ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java @@ -25,6 +25,6 @@ public final class sqlite3_stmt extends NativePointerHolder private sqlite3_stmt(){} @Override public void close(){ - CApi.sqlite3_finalize(this.clearNativePointer()); + CApi.sqlite3_finalize(this); } } diff --git a/manifest b/manifest index e1f54ab601..242ef22173 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Flesh\sout\s[7a63b5b65a79]\sto\sbe\sable\sto\sbuild\sJNI\swith\sor\swithout\sSQLITE_ENABLE_COLUMN_METADATA. -D 2023-11-07T13:22:49.498 +C Diverse\sminor\scleanups\sin\sthe\sJNI\spieces. +D 2023-11-07T13:44:29.266 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c ac7aa8ef250be4bebf99aa5b610d4a1b5f26578328a458a53db3b075271417ed +F ext/jni/src/c/sqlite3-jni.c 62455cadfc32d52c525b56c521467586a1704865556426cd648af642a770b6b6 F ext/jni/src/c/sqlite3-jni.h 9300900f7ec91fffa01445e1ad5815e35a7bece4c9ca07de464641f77195404a F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 75b92b98ee1e621e71466e4bdb3bdff580da688e9873524adffd97db12675eb1 +F ext/jni/src/org/sqlite/jni/capi/CApi.java 27e3c73685e1068e51331cd083152e0a6357b02278cb67b7c103240dbc262a5d F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -276,11 +276,11 @@ F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e -F ext/jni/src/org/sqlite/jni/capi/sqlite3.java 4010bbebc5bf44e2044e610786088cdee7dc155da2b333c0551492ff1cedf33b +F ext/jni/src/org/sqlite/jni/capi/sqlite3.java c6a5c555d163d76663534f2b2cce7cab15325b9852d0f58c6688a85e73ae52f0 F ext/jni/src/org/sqlite/jni/capi/sqlite3_backup.java 6742b431cd4d77e8000c1f92ec66265a58414c86bf3b0b5fbcb1164e08477227 F ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java f204ab6ab1263e119fe43730141a00662d80972129a5351dfb11aae5d282df36 F ext/jni/src/org/sqlite/jni/capi/sqlite3_context.java f0ef982009c335c4393ffcb68051809ca1711e4f47bcb8d1d46952f22c01bc22 -F ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java ff579621e9bd5ffbc6b2ef9f996c12db4df6e0c8cc5697c91273e5fca279fcf8 +F ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java 293b5fa7d5b5724c87de544654aca1103d76f3092bc2c8f4360102a65ba25dff F ext/jni/src/org/sqlite/jni/capi/sqlite3_value.java e1d62a257c13504b46d39d5c21c49cf157ad73fda00cc5f34c931aa008c37049 F ext/jni/src/org/sqlite/jni/fts5/Fts5.java e94681023785f1eff5399f0ddc82f46b035977d350f14838db659236ebdf6b41 F ext/jni/src/org/sqlite/jni/fts5/Fts5Context.java 338637e6e5a2cc385d962b220f3c1f475cc371d12ae43d18ef27327b6e6225f7 @@ -2142,8 +2142,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 7a63b5b65a79d15658a160d0878c7371941c67e9b48a7442762c68c60b77288a -R efbde6eb422be28e0f5c5e7a6eec616d +P fcee41b3d4d2558299ead28cc17f290b9ff1957a84c3feaa0a24872feeb22901 +R 2e556ea528a12ae342b294a8936cf3da U stephan -Z adceda1b2796226e98fdf1b3f6f9cd13 +Z 63d82a638a6f714cab2831b5cda37824 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 26896fc11a..8908c89a68 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fcee41b3d4d2558299ead28cc17f290b9ff1957a84c3feaa0a24872feeb22901 \ No newline at end of file +35233dd900632b997b5e532170a3b2af0ca7f1dccb8407555b93f2b395b0f7b4 \ No newline at end of file From 488125d4ced1736963a35adabc42e4a0091a089c Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 7 Nov 2023 15:56:39 +0000 Subject: [PATCH 133/347] Add Sqlite.prepareMulti() to JNI wrapper1, for preparing multiple statements from a single input. FossilOrigin-Name: e4670d68b52233ab376a1725983e148aaf2a2c3658a41f5768e37a0f1f87428a --- ext/jni/src/org/sqlite/jni/capi/CApi.java | 12 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 127 ++++++++++++++++-- .../src/org/sqlite/jni/wrapper1/Tester2.java | 33 ++++- manifest | 16 +-- manifest.uuid | 2 +- 5 files changed, 166 insertions(+), 24 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 8bdcb3cf2d..1b08c50a28 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -1202,26 +1202,26 @@ public final class CApi { */ public static int sqlite3_prepare_multi( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, - int preFlags, + int prepFlags, @NotNull PrepareMultiCallback p){ final OutputPointer.Int32 oTail = new OutputPointer.Int32(); int pos = 0, n = 1; byte[] sqlChunk = sqlUtf8; int rc = 0; final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); - while(0==rc && pos 0){ + if( pos>0 ){ sqlChunk = Arrays.copyOfRange(sqlChunk, pos, sqlChunk.length); } if( 0==sqlChunk.length ) break; - rc = sqlite3_prepare_v3(db, sqlChunk, preFlags, outStmt, oTail); + rc = sqlite3_prepare_v3(db, sqlChunk, prepFlags, outStmt, oTail); if( 0!=rc ) break; pos = oTail.value; stmt = outStmt.take(); - if( null == stmt ){ - // empty statement was parsed. + if( null==stmt ){ + // empty statement (whitespace/comments) continue; } rc = p.call(stmt); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 93c294f5de..e61b7e59dd 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -510,19 +510,27 @@ public final class Sqlite implements AutoCloseable { } /** + Analog to sqlite3_prepare_v3(), this prepares the first SQL + statement from the given input string and returns it as a + Stmt. It throws an SqliteException if preparation fails or an + IllegalArgumentException if the input is empty (e.g. contains + only comments or whitespace). + + The first argument must be SQL input in UTF-8 encoding. + prepFlags must be 0 or a bitmask of the PREPARE_... constants. - prepare() TODOs include: + For processing multiple statements from a single input, use + prepareMulti(). - - overloads taking byte[] and ByteBuffer. - - - multi-statement processing, like CApi.sqlite3_prepare_multi() - but using a callback specific to the higher-level Stmt class - rather than the sqlite3_stmt class. + Design note: though the C-level API succeeds with a null + statement object for empty inputs, that approach is cumbersome to + use in higher-level APIs because every prepared statement has to + be checked for null before using it. */ - public Stmt prepare(String sql, int prepFlags){ + public Stmt prepare(byte utf8Sql[], int prepFlags){ final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); - final int rc = CApi.sqlite3_prepare_v3(thisDb(), sql, prepFlags, out); + final int rc = CApi.sqlite3_prepare_v3(thisDb(), utf8Sql, prepFlags, out); checkRc(rc); final sqlite3_stmt q = out.take(); if( null==q ){ @@ -539,10 +547,113 @@ public final class Sqlite implements AutoCloseable { return new Stmt(this, q); } + /** + Equivalent to prepare(X, prepFlags), where X is + sql.getBytes(StandardCharsets.UTF_8). + */ + public Stmt prepare(String sql, int prepFlags){ + return prepare( sql.getBytes(StandardCharsets.UTF_8), prepFlags ); + } + + /** + Equivalent to prepare(sql, 0). + */ public Stmt prepare(String sql){ return prepare(sql, 0); } + + /** + Callback type for use with prepareMulti(). + */ + public interface PrepareMulti { + /** + Gets passed a Stmt which it may handle in arbitrary ways. + Ownership of st is passed to this function. It must throw on + error. + */ + void call(Sqlite.Stmt st); + } + + /** + A PrepareMulti implementation which calls another PrepareMulti + object and then finalizes its statement. + */ + public static class PrepareMultiFinalize implements PrepareMulti { + private final PrepareMulti pm; + /** + Proxies the given PrepareMulti via this object's call() method. + */ + public PrepareMultiFinalize(PrepareMulti proxy){ + this.pm = proxy; + } + /** + Passes st to the call() method of the object this one proxies, + then finalizes st, propagating any exceptions from call() after + finalizing st. + */ + @Override public void call(Stmt st){ + try{ pm.call(st); } + finally{ st.finalizeStmt(); } + } + } + + /** + Equivalent to prepareMulti(sql,0,visitor). + */ + public void prepareMulti(String sql, PrepareMulti visitor){ + prepareMulti( sql, 0, visitor ); + } + + /** + A variant of prepare() which can handle multiple SQL statements + in a single input string. For each statement in the given string, + the statement is passed to visitor.call() a single time, passing + ownership of the statement to that function. This function does + not step() or close() statements - those operations are left to + caller or the visitor function. + + Unlike prepare(), this function does not fail if the input + contains only whitespace or SQL comments. In that case it is up + to the caller to arrange for that to be an error (if desired). + + PrepareMultiFinalize offers a proxy which finalizes each + statement after it is passed to another client-defined visitor. + */ + public void prepareMulti(byte sqlUtf8[], int prepFlags, PrepareMulti visitor){ + int pos = 0, n = 1; + byte[] sqlChunk = sqlUtf8; + final org.sqlite.jni.capi.OutputPointer.sqlite3_stmt outStmt = + new org.sqlite.jni.capi.OutputPointer.sqlite3_stmt(); + final org.sqlite.jni.capi.OutputPointer.Int32 oTail = + new org.sqlite.jni.capi.OutputPointer.Int32(); + while( pos < sqlChunk.length ){ + sqlite3_stmt stmt = null; + if( pos>0 ){ + sqlChunk = java.util.Arrays.copyOfRange(sqlChunk, pos, sqlChunk.length); + } + if( 0==sqlChunk.length ) break; + checkRc( + CApi.sqlite3_prepare_v3(db, sqlChunk, prepFlags, outStmt, oTail) + ); + pos = oTail.value; + stmt = outStmt.take(); + if( null==stmt ){ + /* empty statement, e.g. only comments or whitespace, was parsed. */ + continue; + } + visitor.call(new Stmt(this, stmt)); + } + } + + /** + Equivallent to prepareMulti(X,prepFlags,visitor), where X is + sql.getBytes(StandardCharsets.UTF_8). + */ + public void prepareMulti(String sql, int prepFlags, PrepareMulti visitor){ + prepareMulti(sql.getBytes(StandardCharsets.UTF_8), prepFlags, visitor); + } + public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){ int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep, new SqlFunction.ScalarAdapter(f)); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index 3f7593c709..c7566b3b0f 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -125,7 +125,7 @@ public class Tester2 implements Runnable { } - public static void execSql(Sqlite db, String[] sql){ + public static void execSql(Sqlite db, String sql[]){ execSql(db, String.join("", sql)); } @@ -938,6 +938,37 @@ public class Tester2 implements Runnable { db.close(); } + void testPrepareMulti(){ + final ValueHolder fCount = new ValueHolder<>(0); + final ValueHolder mCount = new ValueHolder<>(0); + try (Sqlite db = openDb()) { + execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)"); + db.createFunction("counter", -1, new ScalarFunction(){ + @Override public void xFunc(SqlFunction.Arguments args){ + ++fCount.value; + args.resultNull(); + } + public void xDestroy(){} + } + ); + final Sqlite.PrepareMulti pm = new Sqlite.PrepareMultiFinalize( + new Sqlite.PrepareMulti() { + @Override public void call(Sqlite.Stmt q){ + ++mCount.value; + while(q.step()){} + } + } + ); + final String sql = "select counter(*) from t;"+ + "select counter(*) from t; /* comment */"+ + "select counter(*) from t; -- comment\n" + ; + db.prepareMulti(sql, pm); + } + affirm( 3 == mCount.value ); + affirm( 9 == fCount.value ); + } + private void runTests(boolean fromThread) throws Exception { List mlist = testMethods; affirm( null!=mlist ); diff --git a/manifest b/manifest index 242ef22173..003a80d74a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Diverse\sminor\scleanups\sin\sthe\sJNI\spieces. -D 2023-11-07T13:44:29.266 +C Add\sSqlite.prepareMulti()\sto\sJNI\swrapper1,\sfor\spreparing\smultiple\sstatements\sfrom\sa\ssingle\sinput. +D 2023-11-07T15:56:39.576 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 27e3c73685e1068e51331cd083152e0a6357b02278cb67b7c103240dbc262a5d +F ext/jni/src/org/sqlite/jni/capi/CApi.java 170cfd6501f6a4e68073808f3046970a6dd73d2faf478cbc9bd23f159ff3a646 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 6a310fe422d0daf79f7841c9b341f64d843ca7e85ef31829530623a81ecd25fa +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0ef62b43b1d6a9f044e106b56c9ea42bc7150b82ebeb79cff58f5be08cb9a435 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java a944fd0a9047a51a5074bffd707452d80cf06e715ebc78b541480ee98629fb93 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 2ecf37746f2d475a133b530ddaf9ba8a6a65ce238db0805cb8e410ee760d4793 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 fcee41b3d4d2558299ead28cc17f290b9ff1957a84c3feaa0a24872feeb22901 -R 2e556ea528a12ae342b294a8936cf3da +P 35233dd900632b997b5e532170a3b2af0ca7f1dccb8407555b93f2b395b0f7b4 +R e808fc8487495cabb8d32f4b1af8109a U stephan -Z 63d82a638a6f714cab2831b5cda37824 +Z 28ef3e488a5683b53a25a3e0e53067e7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8908c89a68..612e2ef446 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -35233dd900632b997b5e532170a3b2af0ca7f1dccb8407555b93f2b395b0f7b4 \ No newline at end of file +e4670d68b52233ab376a1725983e148aaf2a2c3658a41f5768e37a0f1f87428a \ No newline at end of file From 637922acac23fe995fd2772559eaf5b75d057c9b Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 7 Nov 2023 17:15:55 +0000 Subject: [PATCH 134/347] JNI: during static init record whether the current JVM supports JNI-level access to java.nio.ByteBuffer raw memory, and add sqlite3_jni_supports_nio() to query that. FossilOrigin-Name: fb8dbb77a4d8efafd6772333824b4ab589828cf155a63ca6a26730314d0a4bd9 --- ext/jni/src/c/sqlite3-jni.c | 45 +++++++++++++------ ext/jni/src/c/sqlite3-jni.h | 8 ++++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 6 +++ ext/jni/src/org/sqlite/jni/capi/Tester1.java | 1 + .../src/org/sqlite/jni/wrapper1/Tester2.java | 1 - manifest | 20 ++++----- manifest.uuid | 2 +- 7 files changed, 57 insertions(+), 26 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 852b0ccceb..7519dfb6c0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -651,6 +651,17 @@ struct S3JniGlobalType { jmethodID ctorLong1 /* the Long(long) constructor */; jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; + + /* + ByteBuffer may or may not be supported via JNI on any given + platform: + + https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#nio_support + + We only store a ref to the following if JNI support for + ByteBuffer is available (which we determine during static init). + */ + jclass cByteBuffer /* global ref to java.nio.ByteBuffer */; } g; /* ** The list of Java-side auto-extensions @@ -3661,6 +3672,11 @@ JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){ return rc ? JNI_TRUE : JNI_FALSE; } +JniDecl(jboolean,1jni_1supports_1nio)(JniArgsEnvClass){ + return SJG.g.cByteBuffer ? JNI_TRUE : JNI_FALSE; +} + + S3JniApi(sqlite3_keyword_check(),jboolean,1keyword_1check)( JniArgsEnvClass, jstring jWord ){ @@ -4576,20 +4592,8 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( S3JniEnv_uncache( SJG.envCache.aHead->env ); } } S3JniEnv_mutex_leave; -#if 0 - /* - ** Is automatically closing any still-open dbs a good idea? We will - ** get rid of the perDb list once sqlite3 gets a per-db client - ** state, at which point we won't have a central list of databases - ** to close. - */ - S3JniDb_mutex_enter; - while( SJG.perDb.pHead ){ - s3jni_close_db(env, SJG.perDb.pHead->jDb, 2); - } - S3JniDb_mutex_leave; -#endif - /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */ + /* Do not clear S3JniGlobal.jvm or S3JniGlobal.g: it's legal to + ** restart the lib. */ return sqlite3_shutdown(); } @@ -5930,6 +5934,19 @@ Java_org_sqlite_jni_capi_CApi_init(JniArgsEnvClass){ s3jni_oom_fatal( SJG.metrics.mutex ); #endif + { + /* Test whether this JVM supports direct memory access via + ByteBuffer. */ + unsigned char buf[16] = {0}; + jobject bb = (*env)->NewDirectByteBuffer(env, buf, 16); + if( bb ){ + SJG.g.cByteBuffer = (*env)->GetObjectClass(env, bb); + S3JniUnrefLocal(bb); + }else{ + SJG.g.cByteBuffer = 0; + } + } + sqlite3_shutdown() /* So that it becomes legal for Java-level code to call ** sqlite3_config(). */; diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index c4ca0559f2..f160b6453f 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -773,6 +773,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_init JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1java_1uncache_1thread (JNIEnv *, jclass); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_jni_supports_nio + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1supports_1nio + (JNIEnv *, jclass); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_aggregate_context diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 1b08c50a28..8e0cb8f4aa 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -123,6 +123,12 @@ public final class CApi { */ public static native boolean sqlite3_java_uncache_thread(); + /** + Returns true if this JVM has JNI-level support for direct memory + access using java.nio.ByteBuffer, else returns false. + */ + public static native boolean sqlite3_jni_supports_nio(); + ////////////////////////////////////////////////////////////////////// // Maintenance reminder: please keep the sqlite3_.... functions // alphabetized. The SQLITE_... values. on the other hand, are diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index f11a644a3b..3ac58c67d3 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -1920,6 +1920,7 @@ public class Tester1 implements Runnable { sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n", "SQLITE_THREADSAFE=",sqlite3_threadsafe()); + outln("JVM NIO support? ",sqlite3_jni_supports_nio() ? "YES" : "NO"); final boolean showLoopCount = (nRepeat>1 && nThread>1); if( showLoopCount ){ outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java index c7566b3b0f..c276e383be 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java @@ -948,7 +948,6 @@ public class Tester2 implements Runnable { ++fCount.value; args.resultNull(); } - public void xDestroy(){} } ); final Sqlite.PrepareMulti pm = new Sqlite.PrepareMultiFinalize( diff --git a/manifest b/manifest index 003a80d74a..4a41fc1505 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sSqlite.prepareMulti()\sto\sJNI\swrapper1,\sfor\spreparing\smultiple\sstatements\sfrom\sa\ssingle\sinput. -D 2023-11-07T15:56:39.576 +C JNI:\sduring\sstatic\sinit\srecord\swhether\sthe\scurrent\sJVM\ssupports\sJNI-level\saccess\sto\sjava.nio.ByteBuffer\sraw\smemory,\sand\sadd\ssqlite3_jni_supports_nio()\sto\squery\sthat. +D 2023-11-07T17:15:55.097 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,8 +241,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 62455cadfc32d52c525b56c521467586a1704865556426cd648af642a770b6b6 -F ext/jni/src/c/sqlite3-jni.h 9300900f7ec91fffa01445e1ad5815e35a7bece4c9ca07de464641f77195404a +F ext/jni/src/c/sqlite3-jni.c aac355ea590199dcbc8ef765f702ee616c90504528c812a37605a0d0994b1b23 +F ext/jni/src/c/sqlite3-jni.h 18925c56d6664fdec081c56daf3b2ffa0e0ff6b9b128b9f39b84862f34ba0601 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 170cfd6501f6a4e68073808f3046970a6dd73d2faf478cbc9bd23f159ff3a646 +F ext/jni/src/org/sqlite/jni/capi/CApi.java 16a28138c3c25f33356193970644389ff8ebc0720499549653934b2529c8d1dd F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java bcfc48cba038e8dc6e08cb9b8a974241e5d9920ab4015c3abf482b7130ac9ba4 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java b1a0c015d92a8d0c07a8f6751e9b057557cec9d803e002d48ee5f3b9963abd55 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -298,7 +298,7 @@ F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaac F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0ef62b43b1d6a9f044e106b56c9ea42bc7150b82ebeb79cff58f5be08cb9a435 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 -F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 2ecf37746f2d475a133b530ddaf9ba8a6a65ce238db0805cb8e410ee760d4793 +F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 40806dbbf8e120f115e33255d1813db13b40f0a598869e299a947a580429939b F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745 @@ -2142,8 +2142,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 35233dd900632b997b5e532170a3b2af0ca7f1dccb8407555b93f2b395b0f7b4 -R e808fc8487495cabb8d32f4b1af8109a +P e4670d68b52233ab376a1725983e148aaf2a2c3658a41f5768e37a0f1f87428a +R 73b75ed4c87333bbc1b63a547227eff1 U stephan -Z 28ef3e488a5683b53a25a3e0e53067e7 +Z ff630bf6abb02dbe33113baf6eccc049 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 612e2ef446..997a53ff67 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e4670d68b52233ab376a1725983e148aaf2a2c3658a41f5768e37a0f1f87428a \ No newline at end of file +fb8dbb77a4d8efafd6772333824b4ab589828cf155a63ca6a26730314d0a4bd9 \ No newline at end of file From 17a32953873c52b91a18f79f371bd15887a022ed Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 7 Nov 2023 19:03:13 +0000 Subject: [PATCH 135/347] Update the documentation to the sqlite3_set_auxdata() and sqlite3_get_auxdata() routines to make it clear that they do not work as one might expect when they are called during query planning, instead of during query execution. The JSON routines misuse those interfaces, so add a special flag to JSON routines that prevents them from being invoked during query planning. Fix for the problem in [forum:/forumpost/a655ee159eca1ea5|forum post a655ee159eca1ea5]. FossilOrigin-Name: 796a23f9ee33da0803844a2f40c1733db894cc4ef7fbaa1fa94af6af2d3b873b --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/sqlite.h.in | 14 +++++++++++--- src/sqliteInt.h | 4 ++-- src/vdbemem.c | 2 +- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 4a41fc1505..1c358844a5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\sduring\sstatic\sinit\srecord\swhether\sthe\scurrent\sJVM\ssupports\sJNI-level\saccess\sto\sjava.nio.ByteBuffer\sraw\smemory,\sand\sadd\ssqlite3_jni_supports_nio()\sto\squery\sthat. -D 2023-11-07T17:15:55.097 +C Update\sthe\sdocumentation\sto\sthe\ssqlite3_set_auxdata()\sand\ssqlite3_get_auxdata()\nroutines\sto\smake\sit\sclear\sthat\sthey\sdo\snot\swork\sas\sone\smight\sexpect\swhen\sthey\nare\scalled\sduring\squery\splanning,\sinstead\sof\sduring\squery\sexecution.\s\sThe\sJSON\nroutines\smisuse\sthose\sinterfaces,\sso\sadd\sa\sspecial\sflag\sto\sJSON\sroutines\sthat\nprevents\sthem\sfrom\sbeing\sinvoked\sduring\squery\splanning.\s\sFix\sfor\sthe\sproblem\nin\s[forum:/forumpost/a655ee159eca1ea5|forum\spost\sa655ee159eca1ea5]. +D 2023-11-07T19:03:13.268 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -726,10 +726,10 @@ F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 -F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 +F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 707095a0591e02f4866ed9798cbdd97a8ae8cf4d98f061808944c2cd1c95d1a9 +F src/sqliteInt.h b9f6cfac999b60def4d3523897dc1f5453ab9c24195e462fd346476541cf659a F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -800,7 +800,7 @@ F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c db190d007bdf5b9165edeb12369f4c59a459f88fd652c1671c1238862e662cc3 F src/vdbeaux.c dffcf79e7e415fcd6e4c8ac1ec7124cae5257018443adf09551c807655b04993 F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 -F src/vdbemem.c c936e9002af4993b84c4eb7133d6b1190efe46d391cc86117ecd67ba17b1a04b +F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 F src/vdbesort.c 237840ca1947511fa59bd4e18b9eeae93f2af2468c34d2427b059f896230a547 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7 @@ -2142,8 +2142,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 e4670d68b52233ab376a1725983e148aaf2a2c3658a41f5768e37a0f1f87428a -R 73b75ed4c87333bbc1b63a547227eff1 -U stephan -Z ff630bf6abb02dbe33113baf6eccc049 +P fb8dbb77a4d8efafd6772333824b4ab589828cf155a63ca6a26730314d0a4bd9 +R 153ffbf9f72d490791b9001d5155efbf +U drh +Z f301b7ed97efe53d235a7cee46d075cc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 997a53ff67..1267d0d18b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fb8dbb77a4d8efafd6772333824b4ab589828cf155a63ca6a26730314d0a4bd9 \ No newline at end of file +796a23f9ee33da0803844a2f40c1733db894cc4ef7fbaa1fa94af6af2d3b873b \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 01a7f4d4e0..2317d98f79 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5913,14 +5913,22 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*); **

  • ^(when sqlite3_set_auxdata() is invoked again on the same ** parameter)^, or **
  • ^(during the original sqlite3_set_auxdata() call when a memory -** allocation error occurs.)^ +** allocation error occurs.)^ +**
  • ^(during the original sqlite3_set_auxdata() call if the function +** is evaluated during query planning instead of during query execution, +** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ ** -** Note the last bullet in particular. The destructor X in +** Note the last two bullets in particular. The destructor X in ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after -** sqlite3_set_auxdata() has been called. +** sqlite3_set_auxdata() has been called. Furthermore, a call to +** sqlite3_get_auxdata() that occurs immediately after a corresponding call +** to sqlite3_set_auxdata() might still return NULL if an out-of-memory +** condition occurred during the sqlite3_set_auxdata() call or if the +** function is being evaluated during query planning rather than during +** query execution. ** ** ^(In practice, auxiliary data is preserved between function calls for ** function parameters that are compile-time constants, including literal diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 23beb48de3..1a22500539 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2014,7 +2014,7 @@ struct FuncDestructor { #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ -/* 0x8000 -- available for reuse */ +#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ @@ -2115,7 +2115,7 @@ struct FuncDestructor { xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } #define JFUNCTION(zName, nArg, iArg, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\ - SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ + SQLITE_FUNC_CONSTANT|SQLITE_UTF8|SQLITE_FUNC_RUNONLY, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ diff --git a/src/vdbemem.c b/src/vdbemem.c index eee828143a..d527164685 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1515,7 +1515,7 @@ static int valueFromFunction( #endif assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 - || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0 ){ return SQLITE_OK; } From 73f03276d918a5184463095b3fd6bbd9a042cece Mon Sep 17 00:00:00 2001 From: larrybr Date: Tue, 7 Nov 2023 19:30:14 +0000 Subject: [PATCH 136/347] Refactor MBCS/UTF-8 translation to avoid extra allocations, supporting non-formatted (faster) output. Some code cleanup. Wrap .system/.shell command exection with restoration of startup console mode and renewing mode setup. Changes to make legacy MBCS build work better (than legacy did, even with --no-utf8.) FossilOrigin-Name: d5e88fcde53ca7ba05bb164943a9f57bd92080bb7e5eebbbed64b9886ac97338 --- ext/consio/console_io.c | 141 +++++++++++++++++++++++----------------- manifest | 14 ++-- manifest.uuid | 2 +- src/shell.c.in | 2 + 4 files changed, 93 insertions(+), 66 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index c60e2b0a49..35016b0c4a 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -119,7 +119,6 @@ typedef struct ConsoleInfo { #endif static ConsoleInfo consoleInfo = { - /* {0,0,0}, // iInitialFmode */ { /* pst */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, #if SHELL_CON_TRANSLATE 0, 0, 1, /* haveInput, outputIx, stdinEof */ @@ -150,7 +149,6 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ } if( ix > 0 ) fflush(apf[ix]); #if SHELL_CON_TRANSLATE == 2 - // _setmode(_fileno(apf[ix]), _O_U8TEXT); _setmode(_fileno(apf[ix]), _O_TEXT); #endif } @@ -165,14 +163,13 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ if( consoleInfo.cscs & (CSCS_InConsole<hx, ppst->consMode); @@ -213,43 +210,59 @@ SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ #undef setModeFlushQ #if SHELL_CON_TRANSLATE +/* Write plain 0-terminated output to stream known as console. */ +static int conioZstrOut(int rch, const char *z){ + int rv = 0; + if( z!=NULL && *z!=0 ){ + int nc; + int nwc; +# if SHELL_CON_TRANSLATE == 2 + UINT cocp = GetConsoleOutputCP(); + FILE *pfO = consoleInfo.pst[rch].pf; + if( cocp == CP_UTF8 ){ + /* This is not legacy action. But it can work better, + ** when the console putatively can handle UTF-8. */ + return fputs(z, pfO)<0 ? 0 : (int)strlen(z); + } +# endif + nc = (int)strlen(z); + nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, 0,0); + if( nwc > 0 ){ + WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); + if( zw!=NULL ){ + nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, zw,nwc); + if( nwc > 0 ){ +# if SHELL_CON_TRANSLATE == 2 + /* Legacy translation to active code page, then MBCS out. */ + rv = WideCharToMultiByte(cocp,0, zw,nwc, 0,0, 0,0); + if( rv != 0 ){ + char *zmb = sqlite3_malloc64(rv+1); + if( zmb != NULL ){ + rv = WideCharToMultiByte(cocp,0, zw,nwc, zmb,rv, 0,0); + zmb[rv] = 0; + if( fputs(zmb, pfO)<0 ) rv = 0; + sqlite3_free(zmb); + } + } +# elif SHELL_CON_TRANSLATE == 1 + /* Translation from UTF-8 to UTF-16, then WCHARs out. */ + if( WriteConsoleW(consoleInfo.pst[rch].hx, zw,nwc, 0, NULL) ){ + rv = nc; + } +# endif + } + sqlite3_free(zw); + } + } + } + return rv; +} + /* For fprintfUtf8() and printfUtf8() when stream is known as console. */ static int conioVmPrintf(int rch, const char *zFormat, va_list ap){ - int rv = 0; - char *z1 = sqlite3_vmprintf(zFormat, ap); -# if SHELL_CON_TRANSLATE == 2 - if( z1!=NULL ){ - UINT ccp = GetConsoleOutputCP(); - FILE *pfO = consoleInfo.pst[rch].pf; - /* Legacy translation to active code page, then MBCS chars out. */ - char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); - if( z2!=NULL ){ - rv = (int)strlen(z2); - fputs(z2, pfO); - sqlite3_free(z2); - } - sqlite3_free(z1); - } -# elif SHELL_CON_TRANSLATE == 1 - /* Translation from UTF-8 to UTF-16, then WCHAR characters out. */ - if( z1!=NULL ){ - int nwc; - WCHAR *zw2 = 0; - rv = (int)strlen(z1); - nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,0,0); - if( nwc>0 ){ - zw2 = sqlite3_malloc64((nwc+1)*sizeof(WCHAR)); - if( zw2!=NULL ){ - HANDLE ho = consoleInfo.pst[rch].hx; - nwc = MultiByteToWideChar(CP_UTF8,0,z1,rv,zw2,nwc); - zw2[nwc] = 0; - WriteConsoleW(ho, zw2, nwc, 0, NULL); - sqlite3_free(zw2); - }else rv = 0; - } - sqlite3_free(z1); - } -# endif + char *z = sqlite3_vmprintf(zFormat, ap); + int rv = conioZstrOut(rch, z); + sqlite3_free(z); return rv; } #endif @@ -292,9 +305,14 @@ SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){ SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO){ #if SHELL_CON_TRANSLATE - return fprintfUtf8(pfO, "%s", z); -#else - return fputs(z, pfO); + short rch = isConOut(pfO); + if( rch > 0 ){ + return conioZstrOut(rch, z); + }else { +#endif + return (fputs(z, pfO)<0)? 0 : (int)strlen(z); +#if SHELL_CON_TRANSLATE + } #endif } @@ -374,8 +392,11 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ ** Also, it is interactive input so it need not be fast. */ int nco = 0; /* For converstion to WCHAR, or pre-test of same. */ - UINT ccp = GetConsoleCP(); /* For translation from mbcs. */ - if( ccp == CP_UTF8 ) return fgets(cBuf, ncMax, pfIn); + UINT cicp = GetConsoleCP(); /* For translation from mbcs. */ + /* If input code page is CP_UTF8, must bypass MBCS input + ** collection because getc() returns 0 for non-ASCII byte + ** Instead, use fgets() which repects character boundaries. */ + if( cicp == CP_UTF8 ) return fgets(cBuf, ncMax, pfIn); while( ncMax-nco >= 5 ){ /* Have space for max UTF-8 group and 0-term. */ int nug = 0; @@ -391,25 +412,29 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ continue; } /* Deal with possible mbcs lead byte. */ - nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1, ncMax-nco-1, ccp); + nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1, ncMax-nco-1, cicp); if( nug > 0 ){ nco += nug; }else{ - /* Must have just mbcs lead byte; get the trail byte. */ - int ct = getc(pfIn); - if( ct < 0 || ct == '\n' ){ - /* Just drop whatever garbage preceded the newline or. - ** EOF. It's not valid, should not happen, and there - ** is no good way to deal with it, short of bailing. */ - if( ct > 0 ){ - cBuf[nco++] = (int)ct; + /* Must have just mbcs lead byte; get the trail byte(s). */ + int ntb = 1, ct; + while( ntb <= 3 ){ /* No more under any multi-byte code. */ + ct = getc(pfIn); + if( ct < 0 || ct == '\n' ){ + /* Just drop whatever garbage preceded the newline or. + ** EOF. It's not valid, should not happen, and there + ** is no good way to deal with it, short of bailing. */ + if( ct > 0 ){ + cBuf[nco++] = (int)ct; + } + break; } - break; + /* Treat ct as bona fide MBCS trailing byte, if valid. */ + cBuf[nco+ntb] = ct; + nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1+ntb, ncMax-nco-1, cicp); + nco += nug; } - /* Treat ct as bona fide MBCS trailing byte, if valid. */ - cBuf[nco+1] = ct; - nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 2, ncMax-nco-1, ccp); - nco += nug; + if( ct < 0 ) break; } } cBuf[nco] = 0; diff --git a/manifest b/manifest index a2db5a7373..98578565a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\sdependencies\sinto\smake\srecipes.\sGet\slegacy\sconsole\sI/O\s(-DSHELL_LEGACY_CONSOLE_IO)\sworking.\sDue\sto\smovement\sof\sMBCS/UTF-8\stranslation\sinto\straditional\sstream\sI/O\ssimulacra,\sthe\sinput\stranslation\sdoes\snot\shappen\sthe\ssame\sway.\s(It\sworks\sthe\ssame,\sbut\sfails\sdifferently\sand\sa\sbit\sbetter.)\sAdded\sprintf()\sand\sfputs()\slook-alikes,\sand\smade\sCLI\suse\sthem. -D 2023-11-07T02:41:46.723 +C Refactor\sMBCS/UTF-8\stranslation\sto\savoid\sextra\sallocations,\ssupporting\snon-formatted\s(faster)\soutput.\sSome\scode\scleanup.\sWrap\s.system/.shell\scommand\sexection\swith\srestoration\sof\sstartup\sconsole\smode\sand\srenewing\smode\ssetup.\sChanges\sto\smake\slegacy\sMBCS\sbuild\swork\sbetter\s(than\slegacy\sdid,\seven\swith\s--no-utf8.) +D 2023-11-07T19:30:14.998 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,7 +50,7 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 5011c039c6224831ebfa7f6522cf4bce72229f50c45c9aa66df0a45acd4690bf x +F ext/consio/console_io.c adb7da4947a5dc661f0106a7a6962c7528653bf95709bcddae22f3422cde25f7 x F ext/consio/console_io.h e6055b6a13a2a9f237e1672f9ef861126a37a61db0e6218a137832557f10ea25 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in b651e2c297bfef8bd063159765b4ffab14f27816cb373b4995a4b411c33ecd51 +F src/shell.c.in 5afd6ba7c0144e2a55df1c24732d88e4ae860459970b25ee2b4a3812af53c358 F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 1d0583f2eb69fdca1cbc55763c0e86a7e32cb7771bfbc2cdf02da4e3fedbfa23 -R 8fe955210f7825f8cd02e07383e28c60 +P 1721dc6a434361c4e2b87c6e677b6dc223432b3cdd5b9eecabaa258889fb2d2a +R c19fe6cbae4ae273f253c34b1a4e1156 U larrybr -Z 407fd2a55e54d86f679485ba9f27b473 +Z 24f475e52cde2b6dcf8641f1d924498c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d95739e422..4d85fa6de7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1721dc6a434361c4e2b87c6e677b6dc223432b3cdd5b9eecabaa258889fb2d2a \ No newline at end of file +d5e88fcde53ca7ba05bb164943a9f57bd92080bb7e5eebbbed64b9886ac97338 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 426401e671..a32c4f5d57 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -10500,7 +10500,9 @@ static int do_meta_command(char *zLine, ShellState *p){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } + consoleRestore(); x = zCmd!=0 ? system(zCmd) : 1; + consoleClassifySetup(stdin, stdout, stderr); sqlite3_free(zCmd); if( x ) utf8_printf(stderr, "System command returns %d\n", x); }else From 344255e5aaddbb5b9ba5aba44d7aea124baec1b5 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 7 Nov 2023 19:39:23 +0000 Subject: [PATCH 137/347] An attempt to work around compilation errors on MinGW reported in [forum:9089d2049a|forum post 9089d2049a]. FossilOrigin-Name: d5658a3ee4f585cc1e96d84425aad2e1f56b3ae507c9e17b71e030f83f0b70e8 --- ext/jni/src/c/sqlite3-jni.c | 4 ++-- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 7519dfb6c0..9f44ecdbd0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -201,8 +201,8 @@ ** ** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers */ -#define JniArgsEnvObj JNIEnv * const env, jobject jSelf -#define JniArgsEnvClass JNIEnv * const env, jclass jKlazz +#define JniArgsEnvObj JNIEnv * env, jobject jSelf +#define JniArgsEnvClass JNIEnv * env, jclass jKlazz /* ** Helpers to account for -Xcheck:jni warnings about not having ** checked for exceptions. diff --git a/manifest b/manifest index 1c358844a5..099e8a1a6d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\sdocumentation\sto\sthe\ssqlite3_set_auxdata()\sand\ssqlite3_get_auxdata()\nroutines\sto\smake\sit\sclear\sthat\sthey\sdo\snot\swork\sas\sone\smight\sexpect\swhen\sthey\nare\scalled\sduring\squery\splanning,\sinstead\sof\sduring\squery\sexecution.\s\sThe\sJSON\nroutines\smisuse\sthose\sinterfaces,\sso\sadd\sa\sspecial\sflag\sto\sJSON\sroutines\sthat\nprevents\sthem\sfrom\sbeing\sinvoked\sduring\squery\splanning.\s\sFix\sfor\sthe\sproblem\nin\s[forum:/forumpost/a655ee159eca1ea5|forum\spost\sa655ee159eca1ea5]. -D 2023-11-07T19:03:13.268 +C An\sattempt\sto\swork\saround\scompilation\serrors\son\sMinGW\sreported\sin\s[forum:9089d2049a|forum\spost\s9089d2049a]. +D 2023-11-07T19:39:23.562 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c aac355ea590199dcbc8ef765f702ee616c90504528c812a37605a0d0994b1b23 +F ext/jni/src/c/sqlite3-jni.c 7c6d944799c12eebc32ecf9e21ec40f1060fbcb77a9c1faef569f1e7770a938d F ext/jni/src/c/sqlite3-jni.h 18925c56d6664fdec081c56daf3b2ffa0e0ff6b9b128b9f39b84862f34ba0601 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -2142,8 +2142,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 fb8dbb77a4d8efafd6772333824b4ab589828cf155a63ca6a26730314d0a4bd9 -R 153ffbf9f72d490791b9001d5155efbf -U drh -Z f301b7ed97efe53d235a7cee46d075cc +P 796a23f9ee33da0803844a2f40c1733db894cc4ef7fbaa1fa94af6af2d3b873b +R 86b853f71a942ef4a14b3758cd2ed59f +U stephan +Z 12a762f4452f720d39ec9297cb5542a9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1267d0d18b..9071024677 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -796a23f9ee33da0803844a2f40c1733db894cc4ef7fbaa1fa94af6af2d3b873b \ No newline at end of file +d5658a3ee4f585cc1e96d84425aad2e1f56b3ae507c9e17b71e030f83f0b70e8 \ No newline at end of file From 275234e3206758df0143dad63cf7aa8802c317f9 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 7 Nov 2023 20:11:49 +0000 Subject: [PATCH 138/347] Fix an assert() that could fail within calls to sqlite3_snapshot_open() in SQLITE_ENABLE_SETLK_TIMEOUT builds. FossilOrigin-Name: 84634bc268e5c80146f3f3b2e13118f239c9a7e4e4e9dfcaccef2b17252ce53b --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/os_unix.c | 15 +++++++++------ test/snapshot_up.test | 2 ++ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 099e8a1a6d..2b98b89df2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C An\sattempt\sto\swork\saround\scompilation\serrors\son\sMinGW\sreported\sin\s[forum:9089d2049a|forum\spost\s9089d2049a]. -D 2023-11-07T19:39:23.562 +C Fix\san\sassert()\sthat\scould\sfail\swithin\scalls\sto\ssqlite3_snapshot_open()\sin\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds. +D 2023-11-07T20:11:49.770 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -708,7 +708,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c cb116fde9e3ca3c1bbfdf89d6928f776a2a34da168e2667426523a4db353b271 +F src/os_unix.c 0a33005e6426702c7e76f3d451f296c088693a95b2be28ba9ef59c8d8529ce6b F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 699aab8dfc88056d796b03b40c0ab979040d58dfc3ae9db207f1be91e4880bbf @@ -1587,7 +1587,7 @@ F test/snapshot2.test 8d6ff5dd9cc503f6e12d408a30409c3f9c653507b24408d9cd7195931c F test/snapshot3.test 8744313270c55f6e18574283553d3c5c5fe4c5970585663613a0e75c151e599b F test/snapshot4.test d4e9347ef2fcabc491fc893506c7bbaf334da3be111d6eb4f3a97cc623b78322 F test/snapshot_fault.test 129234ceb9b26a0e1000e8563a16e790f5c1412354e70749cbd78c3d5d07d60a -F test/snapshot_up.test a0a29c4cf33475fcef07c3f8e64af795e24ab91b4cc68295863402a393cdd41c +F test/snapshot_up.test 77dc7853bfb2b4fa249f76e1714cfa1e596826165d9ef22c06ac3a0b7b778d9a F test/soak.test 18944cf21b94a7fe0df02016a6ee1e9632bc4e8d095a0cb49d95e15d5cca2d5c F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087 F test/sort.test f86751134159abb5e5fd4381a0d7038c91013638cd1e3fa1d7850901f6df6196 @@ -2142,8 +2142,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 796a23f9ee33da0803844a2f40c1733db894cc4ef7fbaa1fa94af6af2d3b873b -R 86b853f71a942ef4a14b3758cd2ed59f -U stephan -Z 12a762f4452f720d39ec9297cb5542a9 +P d5658a3ee4f585cc1e96d84425aad2e1f56b3ae507c9e17b71e030f83f0b70e8 +R 51ac3a4b6cfe47fd7ec11d417bc30543 +U dan +Z d5bc56d23e981b8dfdb9cdcebdb1ac5d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9071024677..cad3be627e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d5658a3ee4f585cc1e96d84425aad2e1f56b3ae507c9e17b71e030f83f0b70e8 \ No newline at end of file +84634bc268e5c80146f3f3b2e13118f239c9a7e4e4e9dfcaccef2b17252ce53b \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index a33e6f4dff..cd3e0fc54d 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4980,12 +4980,15 @@ static int unixShmLock( ** It is not permitted to block on the RECOVER lock. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( - (ofst!=2) /* not RECOVER */ - && (ofst!=1 || (p->exclMask|p->sharedMask)==0) - && (ofst!=0 || (p->exclMask|p->sharedMask)<3) - && (ofst<3 || (p->exclMask|p->sharedMask)<(1<exclMask|p->sharedMask); + assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( + (ofst!=2) /* not RECOVER */ + && (ofst!=1 || lockMask==0 || lockMask==2) + && (ofst!=0 || lockMask<3) + && (ofst<3 || lockMask<(1< Date: Tue, 7 Nov 2023 20:56:29 +0000 Subject: [PATCH 139/347] Add -DSQLITE_ENABLE_SETLK_TIMEOUT=1 to a release-test configuration. FossilOrigin-Name: cd3e38fb0f2f7e134bb6cfbe86b6621017344e4361dc6a09ec1367860ba8773b --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/releasetest_data.tcl | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 2b98b89df2..81edb7cb38 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sassert()\sthat\scould\sfail\swithin\scalls\sto\ssqlite3_snapshot_open()\sin\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds. -D 2023-11-07T20:11:49.770 +C Add\s-DSQLITE_ENABLE_SETLK_TIMEOUT=1\sto\sa\srelease-test\sconfiguration. +D 2023-11-07T20:56:29.493 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1487,7 +1487,7 @@ F test/recover.test fd5199f928757cb308661b5fdca1abc19398a798ff7f24b57c3071e9f8e0 F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1 F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5 F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d -F test/releasetest_data.tcl 80ef3941bf7ad136f4dc3d8960786f10a4f488797238171bdd757cc6eb4c3efa +F test/releasetest_data.tcl 50679c8de0e67ca93a47dc95fdf077ecbc4b6eceb14dcb76f19779ab44132e65 F test/resetdb.test 54c06f18bc832ac6d6319e5ab23d5c8dd49fdbeec7c696d791682a8006bd5fc3 F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/returning1.test db532cde29d6aebbc48c6ddc3149b30476f8e69ca7a2c4b53986c7635e6fd8ec @@ -2142,8 +2142,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 d5658a3ee4f585cc1e96d84425aad2e1f56b3ae507c9e17b71e030f83f0b70e8 -R 51ac3a4b6cfe47fd7ec11d417bc30543 +P 84634bc268e5c80146f3f3b2e13118f239c9a7e4e4e9dfcaccef2b17252ce53b +R abb9ab70d79eb8c8383bcdc7ce649ea3 U dan -Z d5bc56d23e981b8dfdb9cdcebdb1ac5d +Z 13575d6d42a1cdb37a2cb5cc87a1058f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cad3be627e..577b0790b1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -84634bc268e5c80146f3f3b2e13118f239c9a7e4e4e9dfcaccef2b17252ce53b \ No newline at end of file +cd3e38fb0f2f7e134bb6cfbe86b6621017344e4361dc6a09ec1367860ba8773b \ No newline at end of file diff --git a/test/releasetest_data.tcl b/test/releasetest_data.tcl index 4ed57a9c8e..95c1b7af19 100644 --- a/test/releasetest_data.tcl +++ b/test/releasetest_data.tcl @@ -221,6 +221,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_PERSIST_WAL=1 -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_SETLK_TIMEOUT=1 -DSQLITE_ENABLE_SNAPSHOT=1 # -DSQLITE_ENABLE_SQLLOG=1 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 From ec8f893e065c95a082e1e432bcacd4641ae8b13e Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Nov 2023 00:12:38 +0000 Subject: [PATCH 140/347] Changes a no-op call to freeP4() into an assert(). FossilOrigin-Name: 32a7b1bd4d88a6839c2c5061a38b4a28f31b22aa8fa08c743633ff96fc4bf88d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbeaux.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 81edb7cb38..500b44aadc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\s-DSQLITE_ENABLE_SETLK_TIMEOUT=1\sto\sa\srelease-test\sconfiguration. -D 2023-11-07T20:56:29.493 +C Changes\sa\sno-op\scall\sto\sfreeP4()\sinto\san\sassert(). +D 2023-11-08T00:12:38.510 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -798,7 +798,7 @@ F src/vdbe.c 14479441337135eed8e290fb1d4abb7db657d93217a3b1ea8a2f031d3895536a F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c db190d007bdf5b9165edeb12369f4c59a459f88fd652c1671c1238862e662cc3 -F src/vdbeaux.c dffcf79e7e415fcd6e4c8ac1ec7124cae5257018443adf09551c807655b04993 +F src/vdbeaux.c f3997b5956c8d97bd2fc3392db42caecddfa6549e9df82e0a7e5804653ca475a F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 F src/vdbesort.c 237840ca1947511fa59bd4e18b9eeae93f2af2468c34d2427b059f896230a547 @@ -2142,8 +2142,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 84634bc268e5c80146f3f3b2e13118f239c9a7e4e4e9dfcaccef2b17252ce53b -R abb9ab70d79eb8c8383bcdc7ce649ea3 -U dan -Z 13575d6d42a1cdb37a2cb5cc87a1058f +P cd3e38fb0f2f7e134bb6cfbe86b6621017344e4361dc6a09ec1367860ba8773b +R b6c8804aadc6653e2e6af22ab7efdb56 +U drh +Z 1be7d8d52cecb1d54f8658da7683cb85 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 577b0790b1..e8520c881c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cd3e38fb0f2f7e134bb6cfbe86b6621017344e4361dc6a09ec1367860ba8773b \ No newline at end of file +32a7b1bd4d88a6839c2c5061a38b4a28f31b22aa8fa08c743633ff96fc4bf88d \ No newline at end of file diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 54317b816f..1d9921b193 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1526,7 +1526,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full( int n ){ if( pOp->p4type ){ - freeP4(p->db, pOp->p4type, pOp->p4.p); + assert( pOp->p4type > P4_FREE_IF_LE ); pOp->p4type = 0; pOp->p4.p = 0; } From c78d3b59637449bc1c7dc4415e6e49ee9e52d156 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Nov 2023 00:45:14 +0000 Subject: [PATCH 141/347] Suppress harmless UBSAN warnings about memory overflow in OP_AddImm. The exact same machine code is generated by GCC. FossilOrigin-Name: b0594383b9fa021a8713d640a4606b9053f8e21d64b4ec8ea60a0b6cddfca306 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbe.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 500b44aadc..01a9a1b873 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Changes\sa\sno-op\scall\sto\sfreeP4()\sinto\san\sassert(). -D 2023-11-08T00:12:38.510 +C Suppress\sharmless\sUBSAN\swarnings\sabout\smemory\soverflow\sin\sOP_AddImm.\s\sThe\nexact\ssame\smachine\scode\sis\sgenerated\sby\sGCC. +D 2023-11-08T00:45:14.192 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -794,7 +794,7 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c b22cc9f203a8c0b9ee5338a67f8860347d14845864c10248bebe84518a781677 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 -F src/vdbe.c 14479441337135eed8e290fb1d4abb7db657d93217a3b1ea8a2f031d3895536a +F src/vdbe.c 7034cf3eec0c905df753368efbcdd96377fca0245584e66766ec47a29fe468c8 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c db190d007bdf5b9165edeb12369f4c59a459f88fd652c1671c1238862e662cc3 @@ -2142,8 +2142,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 cd3e38fb0f2f7e134bb6cfbe86b6621017344e4361dc6a09ec1367860ba8773b -R b6c8804aadc6653e2e6af22ab7efdb56 +P 32a7b1bd4d88a6839c2c5061a38b4a28f31b22aa8fa08c743633ff96fc4bf88d +R 018fee29b67a9c300c9a1c8f38582550 U drh -Z 1be7d8d52cecb1d54f8658da7683cb85 +Z d4e9689751359f866f309e9e035b0a5f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e8520c881c..7d15d1be9a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32a7b1bd4d88a6839c2c5061a38b4a28f31b22aa8fa08c743633ff96fc4bf88d \ No newline at end of file +b0594383b9fa021a8713d640a4606b9053f8e21d64b4ec8ea60a0b6cddfca306 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 221e8847db..544c8d8457 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2033,7 +2033,7 @@ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); - pIn1->u.i += pOp->p2; + *(u64*)&pIn1->u.i += (u64)pOp->p2; break; } From 86b1898cb438fe312efa073cbf42ba44d8cd1d92 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 8 Nov 2023 12:56:23 +0000 Subject: [PATCH 142/347] JNI build fixes for platforms where the jint type is not the same as int, as reported in [forum:9089d2049a|forum post 9089d2049a]. FossilOrigin-Name: b32b0873274bfe472114da8a308a04bee76ba26a5830d8d04fc921f9c1544f9d --- ext/jni/src/c/sqlite3-jni.c | 20 ++++++++++---------- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 9f44ecdbd0..9384fb9d21 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -3061,7 +3061,7 @@ S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( return rc; } -S3JniApi(sqlite3_complete(),int,1complete)( +S3JniApi(sqlite3_complete(),jint,1complete)( JniArgsEnvClass, jbyteArray jSql ){ jbyte * const pBuf = s3jni_jbyteArray_bytes(jSql); @@ -3507,7 +3507,7 @@ S3JniApi(sqlite3_db_readonly(),jint,1db_1readonly)( return (jint)rc; } -S3JniApi(sqlite3_db_release_memory(),int,1db_1release_1memory)( +S3JniApi(sqlite3_db_release_memory(),jint,1db_1release_1memory)( JniArgsEnvClass, jobject jDb ){ sqlite3 * const pDb = PtrGet_sqlite3(jDb); @@ -4012,11 +4012,11 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, #if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) /* We need no-op impls for preupdate_{count,depth,blobwrite}() */ -S3JniApi(sqlite3_preupdate_blobwrite(),int,1preupdate_1blobwrite)( +S3JniApi(sqlite3_preupdate_blobwrite(),jint,1preupdate_1blobwrite)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } -S3JniApi(sqlite3_preupdate_count(),int,1preupdate_1count)( +S3JniApi(sqlite3_preupdate_count(),jint,1preupdate_1count)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } -S3JniApi(sqlite3_preupdate_depth(),int,1preupdate_1depth)( +S3JniApi(sqlite3_preupdate_depth(),jint,1preupdate_1depth)( JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } #endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -4332,7 +4332,7 @@ S3JniApi(sqlite3_result_double(),void,1result_1double)( } S3JniApi(sqlite3_result_error(),void,1result_1error)( - JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, int eTextRep + JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, jint eTextRep ){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); @@ -4680,7 +4680,7 @@ S3JniApi(sqlite3_step(),jint,1step)( return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; } -S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)( +S3JniApi(sqlite3_table_column_metadata(),jint,1table_1column_1metadata)( JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName, jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull, jobject jPrimaryKey, jobject jAutoinc @@ -4866,14 +4866,14 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( : NULL; } -S3JniApi(sqlite3_value_bytes(),int,1value_1bytes)( +S3JniApi(sqlite3_value_bytes(),jint,1value_1bytes)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); return sv ? sqlite3_value_bytes(sv) : 0; } -S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)( +S3JniApi(sqlite3_value_bytes16(),jint,1value_1bytes16)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); @@ -5540,7 +5540,7 @@ JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){ return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx)); } -JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ +JniDeclFtsXA(jint,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; S3JniFts5AuxData * pAux; diff --git a/manifest b/manifest index 01a9a1b873..7fcaaf8277 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Suppress\sharmless\sUBSAN\swarnings\sabout\smemory\soverflow\sin\sOP_AddImm.\s\sThe\nexact\ssame\smachine\scode\sis\sgenerated\sby\sGCC. -D 2023-11-08T00:45:14.192 +C JNI\sbuild\sfixes\sfor\splatforms\swhere\sthe\sjint\stype\sis\snot\sthe\ssame\sas\sint,\sas\sreported\sin\s[forum:9089d2049a|forum\spost\s9089d2049a]. +D 2023-11-08T12:56:23.623 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,7 +241,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 7c6d944799c12eebc32ecf9e21ec40f1060fbcb77a9c1faef569f1e7770a938d +F ext/jni/src/c/sqlite3-jni.c 6b95974189d7cc394afbe15507050f1d174170a65be5a4dad201ab11f0a9777a F ext/jni/src/c/sqlite3-jni.h 18925c56d6664fdec081c56daf3b2ffa0e0ff6b9b128b9f39b84862f34ba0601 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba @@ -2142,8 +2142,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 32a7b1bd4d88a6839c2c5061a38b4a28f31b22aa8fa08c743633ff96fc4bf88d -R 018fee29b67a9c300c9a1c8f38582550 -U drh -Z d4e9689751359f866f309e9e035b0a5f +P b0594383b9fa021a8713d640a4606b9053f8e21d64b4ec8ea60a0b6cddfca306 +R 3f46c0761970583c9dda3b5c30472d0c +U stephan +Z b9bb3a53ca08337ab5c86e3c35fc1299 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7d15d1be9a..d465a17824 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b0594383b9fa021a8713d640a4606b9053f8e21d64b4ec8ea60a0b6cddfca306 \ No newline at end of file +b32b0873274bfe472114da8a308a04bee76ba26a5830d8d04fc921f9c1544f9d \ No newline at end of file From 43d05ccc314472ece8dfcb2eaac36c6e93ceee23 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 8 Nov 2023 14:55:20 +0000 Subject: [PATCH 143/347] Add declarations for new API functions. FossilOrigin-Name: b8a48cc18c94d15017f898c820fdd784efbaac20d7a45c4d97269333e8f2ec60 --- ext/fts5/fts5.h | 5 +++++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 323d73a28f..730a2fb5e3 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -298,6 +298,11 @@ struct Fts5ExtensionApi { int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); + + /* Below this point are iVersion>=3 only */ + int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char**, int*); + int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); + int (*xPhraseToken)(Fts5Context*, Fts5PhraseIter*, int, const char**, int*); }; /* diff --git a/manifest b/manifest index 8eba96ed3c..baff022d13 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\sinto\sthis\sbranch. -D 2023-11-06T19:16:38.711 +C Add\sdeclarations\sfor\snew\sAPI\sfunctions. +D 2023-11-08T14:55:20.854 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -87,7 +87,7 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7 F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h 05501612cc655504c5dce8ba765ab621d50fc478490089beaa0d75e00b23e520 +F ext/fts5/fts5.h 68256dd94eaba3e874762a63578922fd62a5ca87c76a28f7636effee4d9bd781 F ext/fts5/fts5Int.h a21eb1cf036ac9eb943e45ed307762901ea86f0159bf0848baa2079a112ddc2f F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 @@ -2143,8 +2143,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 ac5570614ebb77feff4943c7a78d877508e31087b550f337c11b45f1016e3c55 c2058a045b57571b2b5d342adb212fe606717c633a0422755691ae6bf5725d25 -R 5cb5a1a54d74cfe313891abc677e111f +P 3a869cf1f84b0e9bdcc4de53685430ab41eafacbba1ca7b87e727aa98811c6c5 +R 9e06a9e0b44056d476c02db915884372 U dan -Z d0201f081f93fb8379f972a79c65f95a +Z 6c0dbde9f9a0f05c4a27f8bd01924beb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 412eb8af05..cf6088cc7c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3a869cf1f84b0e9bdcc4de53685430ab41eafacbba1ca7b87e727aa98811c6c5 \ No newline at end of file +b8a48cc18c94d15017f898c820fdd784efbaac20d7a45c4d97269333e8f2ec60 \ No newline at end of file From 8657eddcdbda916c4f2cfe6cffe1ca7e712ad75c Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 8 Nov 2023 15:34:03 +0000 Subject: [PATCH 144/347] Remove an unused/invalid test from the wasm suite. FossilOrigin-Name: 916ae898743a969295a48ae2a6e9e366586834b32d77d3fa281bbaf7f2818502 --- ext/wasm/tester1.c-pp.js | 24 +----------------------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index c26bce25be..36ca4c976f 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1688,7 +1688,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; wasm.sqlite3_wasm_vfs_unlink(0, filename); } } - }/*sqlite3_js_vfs_create_file()*/) + }/*sqlite3_js_posix_create_file()*/) //////////////////////////////////////////////////////////////////// .t({ @@ -2605,28 +2605,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule; } } }/*kvvfs sanity checks*/) - .t({ - name: 'kvvfs sqlite3_js_vfs_create_file()', - predicate: ()=>"kvvfs does not currently support this", - test: function(sqlite3){ - let db; - try { - db = new this.JDb(this.kvvfsDbFile); - const exp = capi.sqlite3_js_db_export(db); - db.close(); - this.kvvfsUnlink(); - capi.sqlite3_js_vfs_create_file("kvvfs", this.kvvfsDbFile, exp); - db = new this.JDb(filename); - T.assert(6 === db.selectValue('select count(*) from kvvfs')); - }finally{ - db.close(); - this.kvvfsUnlink(); - } - delete this.kvvfsDbFile; - delete this.kvvfsUnlink; - delete this.JDb; - } - }/*kvvfs sqlite3_js_vfs_create_file()*/) ;/* end kvvfs tests */ //////////////////////////////////////////////////////////////////////// diff --git a/manifest b/manifest index 7fcaaf8277..5d7d990438 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\sbuild\sfixes\sfor\splatforms\swhere\sthe\sjint\stype\sis\snot\sthe\ssame\sas\sint,\sas\sreported\sin\s[forum:9089d2049a|forum\spost\s9089d2049a]. -D 2023-11-08T12:56:23.623 +C Remove\san\sunused/invalid\stest\sfrom\sthe\swasm\ssuite. +D 2023-11-08T15:34:03.261 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -636,7 +636,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2 -F ext/wasm/tester1.c-pp.js d628826e936bd143d64e0fa3089752abeeeea38a34a7e2b18d364f090d4e99c6 +F ext/wasm/tester1.c-pp.js a92dc256738dbd1b50f142d1fd0c835294ba09b7bb6526650360e942f88cb63f F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2142,8 +2142,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 b0594383b9fa021a8713d640a4606b9053f8e21d64b4ec8ea60a0b6cddfca306 -R 3f46c0761970583c9dda3b5c30472d0c +P b32b0873274bfe472114da8a308a04bee76ba26a5830d8d04fc921f9c1544f9d +R 203d0511d76de1bd08375343f3ff1602 U stephan -Z b9bb3a53ca08337ab5c86e3c35fc1299 +Z 7f67038dd1c5d3f1db527f87a3f9a58b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d465a17824..97b3d8d6f8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b32b0873274bfe472114da8a308a04bee76ba26a5830d8d04fc921f9c1544f9d \ No newline at end of file +916ae898743a969295a48ae2a6e9e366586834b32d77d3fa281bbaf7f2818502 \ No newline at end of file From 0ad5301378dd567d86cfe5cc97774dc226968cfd Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 8 Nov 2023 15:49:57 +0000 Subject: [PATCH 145/347] Avoid blocking as part of passive checkpoint operations, even if SQLITE_ENABLE_SETLK_TIMEOUT is defined. FossilOrigin-Name: e5ecc404cae1ce8b639d0263fa07571c066f11bfc62f5ba331ad7ae138e78572 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/wal.c | 7 ++++++- test/testrunner_data.tcl | 1 + 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 5d7d990438..a1dc817b0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sunused/invalid\stest\sfrom\sthe\swasm\ssuite. -D 2023-11-08T15:34:03.261 +C Avoid\sblocking\sas\spart\sof\spassive\scheckpoint\soperations,\seven\sif\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined. +D 2023-11-08T15:49:57.172 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -806,7 +806,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8 F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7 F src/vtab.c 154725ebecd3bc02f7fbd7ad3974334f73fff76e02a964e828e48a7c5fb7efff F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 01e051a1e713d9eabdb25df38602837cec8f4c2cae448ce2cf6accc87af903e9 +F src/wal.c bba7db5dae3ffe2c6b9c173fc10be4b570b125e985cb5b95a6c22716213adde4 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 F src/where.c 313ce81270d2a414672370e1ee74e65949ad620519193d4cac2986d073cbc8a0 @@ -1651,7 +1651,7 @@ F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede F test/testrunner.tcl 8a6721213bce1cfd3b33e1588cc6431143d96b98819206bf91f5a205fbb150d4 -F test/testrunner_data.tcl 7f73f93634d32dafc857ed491b840f371113d09fde6a8bfb9e47b938d47b8c85 +F test/testrunner_data.tcl e4d5017290a6d5c11785e36cc94c67d8bb950c8cdc2dbe4c1db2a3a583812560 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -2142,8 +2142,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 b32b0873274bfe472114da8a308a04bee76ba26a5830d8d04fc921f9c1544f9d -R 203d0511d76de1bd08375343f3ff1602 -U stephan -Z 7f67038dd1c5d3f1db527f87a3f9a58b +P 916ae898743a969295a48ae2a6e9e366586834b32d77d3fa281bbaf7f2818502 +R 5bea92ea8c9d0554fce6b86ad4fbcf31 +U dan +Z c817b07612729531bff15732a557a90b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 97b3d8d6f8..27f2be574e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -916ae898743a969295a48ae2a6e9e366586834b32d77d3fa281bbaf7f2818502 \ No newline at end of file +e5ecc404cae1ce8b639d0263fa07571c066f11bfc62f5ba331ad7ae138e78572 \ No newline at end of file diff --git a/src/wal.c b/src/wal.c index d63c13ffc4..655d78f591 100644 --- a/src/wal.c +++ b/src/wal.c @@ -4248,9 +4248,14 @@ int sqlite3WalCheckpoint( /* Read the wal-index header. */ SEH_TRY { if( rc==SQLITE_OK ){ + /* For a passive checkpoint, do not re-enable blocking locks after + ** reading the wal-index header. A passive checkpoint should not block + ** or invoke the busy handler. The only lock such a checkpoint may + ** attempt to obtain is a lock on a read-slot, and it should give up + ** immediately and do a partial checkpoint if it cannot obtain it. */ walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); - (void)walEnableBlocking(pWal); + if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal); if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); } diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl index 9032ced4dd..c4e24c4382 100644 --- a/test/testrunner_data.tcl +++ b/test/testrunner_data.tcl @@ -267,6 +267,7 @@ namespace eval trd { -DSQLITE_ENABLE_PERSIST_WAL=1 -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_SETLK_TIMEOUT=2 -DSQLITE_ENABLE_SNAPSHOT=1 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_MAX_LENGTH=2147483645 From e445586630bba75261a166d95bd27cd6063f390f Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 8 Nov 2023 15:51:42 +0000 Subject: [PATCH 146/347] Remove old files related to wapptest.tcl from test/ directory. FossilOrigin-Name: dd3e7b5bcad122ac1e7e19ec547f4486ce90a6a2aa89a64e36bea13a216492fe --- manifest | 13 +- manifest.uuid | 2 +- test/releasetest_data.tcl | 846 -------------------------------- test/wapp.tcl | 987 -------------------------------------- test/wapptest.tcl | 909 ----------------------------------- 5 files changed, 6 insertions(+), 2751 deletions(-) delete mode 100644 test/releasetest_data.tcl delete mode 100644 test/wapp.tcl delete mode 100755 test/wapptest.tcl diff --git a/manifest b/manifest index a1dc817b0a..f8b0d56fd2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sblocking\sas\spart\sof\spassive\scheckpoint\soperations,\seven\sif\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined. -D 2023-11-08T15:49:57.172 +C Remove\sold\sfiles\srelated\sto\swapptest.tcl\sfrom\stest/\sdirectory. +D 2023-11-08T15:51:42.810 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1487,7 +1487,6 @@ F test/recover.test fd5199f928757cb308661b5fdca1abc19398a798ff7f24b57c3071e9f8e0 F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1 F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5 F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d -F test/releasetest_data.tcl 50679c8de0e67ca93a47dc95fdf077ecbc4b6eceb14dcb76f19779ab44132e65 F test/resetdb.test 54c06f18bc832ac6d6319e5ab23d5c8dd49fdbeec7c696d791682a8006bd5fc3 F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/returning1.test db532cde29d6aebbc48c6ddc3149b30476f8e69ca7a2c4b53986c7635e6fd8ec @@ -1952,8 +1951,6 @@ F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370 F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747 F test/walvfs.test e1a6ad0f3c78e98b55c3d5f0889cf366cc0d0a1cb2bccb44ac9ec67384adc4a1 -F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec -F test/wapptest.tcl 1bea58a6a8e68a73f542ee4fca28b771b84ed803bd0c9e385087070b3d747b3c x F test/where.test 59abb854eee24f166b5f7ba9d17eb250abc59ce0a66c48912ffb10763648196d F test/where2.test 03c21a11e7b90e2845fc3c8b4002fc44cc2797fa74c86ee47d70bd7ea4f29ed6 F test/where3.test 5b4ffc0ac2ea0fe92f02b1244b7531522fe4d7bccf6fa8741d54e82c10e67753 @@ -2142,8 +2139,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 916ae898743a969295a48ae2a6e9e366586834b32d77d3fa281bbaf7f2818502 -R 5bea92ea8c9d0554fce6b86ad4fbcf31 +P e5ecc404cae1ce8b639d0263fa07571c066f11bfc62f5ba331ad7ae138e78572 +R ce83d55831eeaab82518466b14d3660d U dan -Z c817b07612729531bff15732a557a90b +Z aaf04791c4ef545a121de15f3eab560c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 27f2be574e..198e99f2b4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e5ecc404cae1ce8b639d0263fa07571c066f11bfc62f5ba331ad7ae138e78572 \ No newline at end of file +dd3e7b5bcad122ac1e7e19ec547f4486ce90a6a2aa89a64e36bea13a216492fe \ No newline at end of file diff --git a/test/releasetest_data.tcl b/test/releasetest_data.tcl deleted file mode 100644 index 95c1b7af19..0000000000 --- a/test/releasetest_data.tcl +++ /dev/null @@ -1,846 +0,0 @@ -# 2019 August 01 -# -# 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 a program that produces scripts (either shell scripts -# or batch files) to implement a particular test that is part of the SQLite -# release testing procedure. For example, to run veryquick.test with a -# specified set of -D compiler switches. -# -# A "configuration" is a set of options passed to [./configure] and [make] -# to build the SQLite library in a particular fashion. A "platform" is a -# list of tests; most platforms are named after the hardware/OS platform -# that the tests will be run on as part of the release procedure. Each -# "test" is a combination of a configuration and a makefile target (e.g. -# "fulltest"). The program may be invoked as follows: -# -set USAGE { -$argv0 script ?-msvc? CONFIGURATION TARGET - Given a configuration and make target, return a bash (or, if -msvc - is specified, batch) script to execute the test. The first argument - passed to the script must be a directory containing SQLite source code. - -$argv0 configurations - List available configurations. - -$argv0 platforms - List available platforms. - -$argv0 tests ?-nodebug? PLATFORM - List tests in a specified platform. If the -nodebug switch is - specified, synthetic debug/ndebug configurations are omitted. Each - test is a combination of a configuration and a makefile target. -} - -# Omit comments (text between # and \n) in a long multi-line string. -# -proc strip_comments {in} { - regsub -all {#[^\n]*\n} $in {} out - return $out -} - -array set ::Configs [strip_comments { - "Default" { - -O2 - --disable-amalgamation --disable-shared - --enable-session - -DSQLITE_ENABLE_RBU - } - "All-Debug" { - --enable-debug --enable-all - } - "All-O0" { - -O0 --enable-all - } - "Sanitize" { - CC=clang -fsanitize=address,undefined - -DSQLITE_ENABLE_STAT4 - -DCONFIG_SLOWDOWN_FACTOR=5.0 - --enable-debug - --enable-all - } - "Stdcall" { - -DUSE_STDCALL=1 - -O2 - } - "Have-Not" { - # The "Have-Not" configuration sets all possible -UHAVE_feature options - # in order to verify that the code works even on platforms that lack - # these support services. - -DHAVE_FDATASYNC=0 - -DHAVE_GMTIME_R=0 - -DHAVE_ISNAN=0 - -DHAVE_LOCALTIME_R=0 - -DHAVE_LOCALTIME_S=0 - -DHAVE_MALLOC_USABLE_SIZE=0 - -DHAVE_STRCHRNUL=0 - -DHAVE_USLEEP=0 - -DHAVE_UTIME=0 - } - "Unlock-Notify" { - -O2 - -DSQLITE_ENABLE_UNLOCK_NOTIFY - -DSQLITE_THREADSAFE - -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 - } - "User-Auth" { - -O2 - -DSQLITE_USER_AUTHENTICATION=1 - } - "Secure-Delete" { - -O2 - -DSQLITE_SECURE_DELETE=1 - -DSQLITE_SOUNDEX=1 - } - "Update-Delete-Limit" { - -O2 - -DSQLITE_DEFAULT_FILE_FORMAT=4 - -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 - -DSQLITE_ENABLE_STMT_SCANSTATUS - -DSQLITE_LIKE_DOESNT_MATCH_BLOBS - -DSQLITE_ENABLE_CURSOR_HINTS - } - "Check-Symbols" { - -DSQLITE_MEMDEBUG=1 - -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 - -DSQLITE_ENABLE_FTS3=1 - -DSQLITE_ENABLE_RTREE=1 - -DSQLITE_ENABLE_MEMSYS5=1 - -DSQLITE_ENABLE_MEMSYS3=1 - -DSQLITE_ENABLE_COLUMN_METADATA=1 - -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 - -DSQLITE_SECURE_DELETE=1 - -DSQLITE_SOUNDEX=1 - -DSQLITE_ENABLE_ATOMIC_WRITE=1 - -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 - -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 - -DSQLITE_ENABLE_STAT4 - -DSQLITE_ENABLE_STMT_SCANSTATUS - --enable-fts5 --enable-session - } - "Debug-One" { - --disable-shared - -O2 -funsigned-char - -DSQLITE_DEBUG=1 - -DSQLITE_MEMDEBUG=1 - -DSQLITE_MUTEX_NOOP=1 - -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 - -DSQLITE_ENABLE_FTS3=1 - -DSQLITE_ENABLE_RTREE=1 - -DSQLITE_ENABLE_MEMSYS5=1 - -DSQLITE_ENABLE_COLUMN_METADATA=1 - -DSQLITE_ENABLE_STAT4 - -DSQLITE_ENABLE_HIDDEN_COLUMNS - -DSQLITE_MAX_ATTACHED=125 - -DSQLITE_MUTATION_TEST - --enable-fts5 - } - "Debug-Two" { - -DSQLITE_DEFAULT_MEMSTATUS=0 - -DSQLITE_MAX_EXPR_DEPTH=0 - --enable-debug - } - "Fast-One" { - -O6 - -DSQLITE_ENABLE_FTS4=1 - -DSQLITE_ENABLE_RTREE=1 - -DSQLITE_ENABLE_STAT4 - -DSQLITE_ENABLE_RBU - -DSQLITE_MAX_ATTACHED=125 - -DSQLITE_MAX_MMAP_SIZE=12884901888 - -DSQLITE_ENABLE_SORTER_MMAP=1 - -DLONGDOUBLE_TYPE=double - --enable-session - } - "Device-One" { - -O2 - -DSQLITE_DEBUG=1 - -DSQLITE_DEFAULT_AUTOVACUUM=1 - -DSQLITE_DEFAULT_CACHE_SIZE=64 - -DSQLITE_DEFAULT_PAGE_SIZE=1024 - -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32 - -DSQLITE_DISABLE_LFS=1 - -DSQLITE_ENABLE_ATOMIC_WRITE=1 - -DSQLITE_ENABLE_IOTRACE=1 - -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 - -DSQLITE_MAX_PAGE_SIZE=4096 - -DSQLITE_OMIT_LOAD_EXTENSION=1 - -DSQLITE_OMIT_PROGRESS_CALLBACK=1 - -DSQLITE_OMIT_VIRTUALTABLE=1 - -DSQLITE_ENABLE_HIDDEN_COLUMNS - -DSQLITE_TEMP_STORE=3 - } - "Device-Two" { - -DSQLITE_4_BYTE_ALIGNED_MALLOC=1 - -DSQLITE_DEFAULT_AUTOVACUUM=1 - -DSQLITE_DEFAULT_CACHE_SIZE=1000 - -DSQLITE_DEFAULT_LOCKING_MODE=0 - -DSQLITE_DEFAULT_PAGE_SIZE=1024 - -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000 - -DSQLITE_DISABLE_LFS=1 - -DSQLITE_ENABLE_FTS3=1 - -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 - -DSQLITE_ENABLE_RTREE=1 - -DSQLITE_MAX_COMPOUND_SELECT=50 - -DSQLITE_MAX_PAGE_SIZE=32768 - -DSQLITE_OMIT_TRACE=1 - -DSQLITE_TEMP_STORE=3 - -DSQLITE_THREADSAFE=2 - --enable-fts5 --enable-session - } - "Locking-Style" { - -O2 - -DSQLITE_ENABLE_LOCKING_STYLE=1 - } - "Apple" { - -Os - -DHAVE_GMTIME_R=1 - -DHAVE_ISNAN=1 - -DHAVE_LOCALTIME_R=1 - -DHAVE_PREAD=1 - -DHAVE_PWRITE=1 - -DHAVE_UTIME=1 - -DSQLITE_DEFAULT_CACHE_SIZE=1000 - -DSQLITE_DEFAULT_CKPTFULLFSYNC=1 - -DSQLITE_DEFAULT_MEMSTATUS=1 - -DSQLITE_DEFAULT_PAGE_SIZE=1024 - -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS=1 - -DSQLITE_ENABLE_API_ARMOR=1 - -DSQLITE_ENABLE_AUTO_PROFILE=1 - -DSQLITE_ENABLE_FLOCKTIMEOUT=1 - -DSQLITE_ENABLE_FTS3=1 - -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 - -DSQLITE_ENABLE_FTS3_TOKENIZER=1 - -DSQLITE_ENABLE_PERSIST_WAL=1 - -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 - -DSQLITE_ENABLE_RTREE=1 - -DSQLITE_ENABLE_SETLK_TIMEOUT=1 - -DSQLITE_ENABLE_SNAPSHOT=1 - # -DSQLITE_ENABLE_SQLLOG=1 - -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 - -DSQLITE_MAX_LENGTH=2147483645 - -DSQLITE_MAX_VARIABLE_NUMBER=500000 - # -DSQLITE_MEMDEBUG=1 - -DSQLITE_NO_SYNC=1 - -DSQLITE_OMIT_AUTORESET=1 - -DSQLITE_OMIT_LOAD_EXTENSION=1 - -DSQLITE_PREFER_PROXY_LOCKING=1 - -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 - -DSQLITE_THREADSAFE=2 - -DSQLITE_USE_URI=1 - -DSQLITE_WRITE_WALFRAME_PREBUFFERED=1 - -DUSE_GUARDED_FD=1 - -DUSE_PREAD=1 - --enable-fts5 - } - "Extra-Robustness" { - -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 - -DSQLITE_MAX_ATTACHED=62 - } - "Devkit" { - -DSQLITE_DEFAULT_FILE_FORMAT=4 - -DSQLITE_MAX_ATTACHED=30 - -DSQLITE_ENABLE_COLUMN_METADATA - -DSQLITE_ENABLE_FTS4 - -DSQLITE_ENABLE_FTS5 - -DSQLITE_ENABLE_FTS4_PARENTHESIS - -DSQLITE_DISABLE_FTS4_DEFERRED - -DSQLITE_ENABLE_RTREE - --enable-fts5 - } - "No-lookaside" { - -DSQLITE_TEST_REALLOC_STRESS=1 - -DSQLITE_OMIT_LOOKASIDE=1 - } - "Valgrind" { - -DSQLITE_ENABLE_STAT4 - -DSQLITE_ENABLE_FTS4 - -DSQLITE_ENABLE_RTREE - -DSQLITE_ENABLE_HIDDEN_COLUMNS - -DLONGDOUBLE_TYPE=double - -DCONFIG_SLOWDOWN_FACTOR=8.0 - } - - "Windows-Memdebug" { - MEMDEBUG=1 - DEBUG=3 - } - "Windows-Win32Heap" { - WIN32HEAP=1 - DEBUG=4 - } - - # The next group of configurations are used only by the - # Failure-Detection platform. They are all the same, but we need - # different names for them all so that they results appear in separate - # subdirectories. - # - Fail0 {-O0} - Fail2 {-O0} - Fail3 {-O0} - Fail4 {-O0} - FuzzFail1 {-O0} - FuzzFail2 {-O0} -}] -if {$tcl_platform(os)=="Darwin"} { - lappend Configs(Apple) -DSQLITE_ENABLE_LOCKING_STYLE=1 -} - -array set ::Platforms [strip_comments { - Linux-x86_64 { - "Check-Symbols*" "" checksymbols - "Fast-One" QUICKTEST_INCLUDE=rbu.test "fuzztest test" - "Debug-One" "" "mptest test" - "Debug-Two" "" test - "Have-Not" "" test - "Secure-Delete" "" test - "Unlock-Notify" QUICKTEST_INCLUDE=notify2.test test - "User-Auth" "" tcltest - "Update-Delete-Limit" "" test - "Extra-Robustness" "" test - "Device-Two" "" "threadtest test" - "No-lookaside" "" test - "Devkit" "" test - "Apple" "" test - "Sanitize*" "" test - "Device-One" "" alltest - "Default" "" "threadtest fuzztest alltest" - "Valgrind*" "" valgrindtest - } - Linux-i686 { - "Devkit" "" test - "Have-Not" "" test - "Unlock-Notify" QUICKTEST_INCLUDE=notify2.test test - "Device-One" "" test - "Device-Two" "" test - "Default" "" "threadtest fuzztest alltest" - } - Darwin-i386 { - "Locking-Style" "" "mptest test" - "Have-Not" "" test - "Apple" "" "threadtest fuzztest alltest" - } - Darwin-x86_64 { - "Locking-Style" "" "mptest test" - "Have-Not" "" test - "Apple" "" "threadtest fuzztest alltest" - } - Darwin-arm64 { - "Locking-Style" "" "mptest test" - "Have-Not" "" test - "Apple" "" "threadtest fuzztest alltest" - } - "Windows NT-intel" { - "Stdcall" "" test - "Have-Not" "" test - "Windows-Memdebug*" "" test - "Windows-Win32Heap*" "" test - "Default" "" "mptest fulltestonly" - } - "Windows NT-amd64" { - "Stdcall" "" test - "Have-Not" "" test - "Windows-Memdebug*" "" test - "Windows-Win32Heap*" "" test - "Default" "" "mptest fulltestonly" - } - - # The Failure-Detection platform runs various tests that deliberately - # fail. This is used as a test of this script to verify that this script - # correctly identifies failures. - # - Failure-Detection { - Fail0* "TEST_FAILURE=0" test - Sanitize* "TEST_FAILURE=1" test - Fail2* "TEST_FAILURE=2" valgrindtest - Fail3* "TEST_FAILURE=3" valgrindtest - Fail4* "TEST_FAILURE=4" test - FuzzFail1* "TEST_FAILURE=5" test - FuzzFail2* "TEST_FAILURE=5" valgrindtest - } -}] - -#-------------------------------------------------------------------------- -#-------------------------------------------------------------------------- -#-------------------------------------------------------------------------- -# End of configuration section. -#-------------------------------------------------------------------------- -#-------------------------------------------------------------------------- -#-------------------------------------------------------------------------- - -# Configuration verification: Check that each entry in the list of configs -# specified for each platforms exists. -# -foreach {key value} [array get ::Platforms] { - foreach {v vars t} $value { - if {[string range $v end end]=="*"} { - set v [string range $v 0 end-1] - } - if {0==[info exists ::Configs($v)]} { - puts stderr "No such configuration: \"$v\"" - exit -1 - } - } -} - -proc usage {} { - global argv0 - puts stderr [subst $::USAGE] - exit 1 -} - -proc is_prefix {p str min} { - set n [string length $p] - if {$n<$min} { return 0 } - if {[string range $str 0 [expr $n-1]]!=$p} { return 0 } - return 1 -} - -proc main_configurations {} { - foreach k [lsort [array names ::Configs]] { - puts $k - } -} - -proc main_platforms {} { - foreach k [lsort [array names ::Platforms]] { - puts "\"$k\"" - } -} - -proc main_script {args} { - set bMsvc 0 - set nArg [llength $args] - if {$nArg==3} { - if {![is_prefix [lindex $args 0] -msvc 2]} usage - set bMsvc 1 - } elseif {$nArg<2 || $nArg>3} { - usage - } - set config [lindex $args end-1] - set target [lindex $args end] - - set opts [list] ;# OPTS value - set cflags [expr {$bMsvc ? "-Zi" : "-g"}] ;# CFLAGS value - set makeOpts [list] ;# Extra args for [make] - set configOpts [list] ;# Extra args for [configure] - - if {$::tcl_platform(platform)=="windows" || $bMsvc} { - lappend opts -DSQLITE_OS_WIN=1 - } else { - lappend opts -DSQLITE_OS_UNIX=1 - } - - # Figure out if this is a synthetic ndebug or debug configuration. - # - set bRemoveDebug 0 - if {[string match *-ndebug $config]} { - set bRemoveDebug 1 - set config [string range $config 0 end-7] - } - if {[string match *-debug $config]} { - lappend opts -DSQLITE_DEBUG - lappend opts -DSQLITE_EXTRA_IFNULLROW - set config [string range $config 0 end-6] - } - regexp {^(.*)-[0-9]+} $config -> config - - # Ensure that the named configuration exists. - # - if {![info exists ::Configs($config)]} { - puts stderr "No such config: $config" - exit 1 - } - - # Loop through the parameters of the nominated configuration, updating - # $opts, $cflags, $makeOpts and $configOpts along the way. Rules are as - # follows: - # - # 1. If the parameter begins with a "*", discard it. - # - # 2. If $bRemoveDebug is set and the parameter is -DSQLITE_DEBUG or - # -DSQLITE_DEBUG=1, discard it - # - # 3. If the parameter begins with "-D", add it to $opts. - # - # 4. If the parameter begins with "--" add it to $configOpts. Unless - # this command is preparing a script for MSVC - then add an - # equivalent to $makeOpts or $opts. - # - # 5. If the parameter begins with "-" add it to $cflags. If in MSVC - # mode and the parameter is an -O option, instead add - # an OPTIMIZATIONS= switch to $makeOpts. - # - # 6. If none of the above apply, add the parameter to $makeOpts - # - foreach param $::Configs($config) { - if {[string range $param 0 0]=="*"} continue - - if {$bRemoveDebug} { - if {$param=="-DSQLITE_DEBUG" || $param=="-DSQLITE_DEBUG=1" - || $param=="-DSQLITE_MEMDEBUG" || $param=="-DSQLITE_MEMDEBUG=1" - || $param=="--enable-debug" - } { - continue - } - } - - if {[string range $param 0 1]=="-D"} { - lappend opts $param - continue - } - - if {[string range $param 0 1]=="--"} { - if {$bMsvc} { - switch -- $param { - --disable-amalgamation { - lappend makeOpts USE_AMALGAMATION=0 - } - --disable-shared { - lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0 - } - --enable-fts5 { - lappend opts -DSQLITE_ENABLE_FTS5 - } - --enable-shared { - lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1 - } - --enable-session { - lappend opts -DSQLITE_ENABLE_PREUPDATE_HOOK - lappend opts -DSQLITE_ENABLE_SESSION - } - default { - error "Cannot translate $param for MSVC" - } - } - } else { - lappend configOpts $param - } - - continue - } - - if {[string range $param 0 0]=="-"} { - if {$bMsvc && [regexp -- {^-O(\d+)$} $param -> level]} { - lappend makeOpts OPTIMIZATIONS=$level - } else { - lappend cflags $param - } - continue - } - - lappend makeOpts $param - } - - # Some configurations specify -DHAVE_USLEEP=0. For all others, add - # -DHAVE_USLEEP=1. - # - if {[lsearch $opts "-DHAVE_USLEEP=0"]<0} { - lappend opts -DHAVE_USLEEP=1 - } - - if {$bMsvc==0} { - puts {set -e} - puts {} - puts {if [ "$#" -ne 1 ] ; then} - puts { echo "Usage: $0 " } - puts { exit -1 } - puts {fi } - puts {SRCDIR=$1} - puts {} - puts "TCL=\"[::tcl::pkgconfig get libdir,install]\"" - - puts "\$SRCDIR/configure --with-tcl=\$TCL $configOpts" - puts {} - puts {OPTS=" -DSQLITE_NO_SYNC=1"} - foreach o $opts { - puts "OPTS=\"\$OPTS $o\"" - } - puts {} - puts "CFLAGS=\"$cflags\"" - puts {} - puts "make $target \"CFLAGS=\$CFLAGS\" \"OPTS=\$OPTS\" $makeOpts" - } else { - - puts {set SRCDIR=%1} - set makecmd "nmake /f %SRCDIR%\\Makefile.msc TOP=%SRCDIR% $target " - append makecmd "\"CFLAGS=$cflags\" \"OPTS=$opts\" $makeOpts" - - puts "set TMP=%CD%" - puts $makecmd - } -} - -proc main_trscript {args} { - set bMsvc 0 - set nArg [llength $args] - if {$nArg==3} { - if {![is_prefix [lindex $args 0] -msvc 2]} usage - set bMsvc 1 - } elseif {$nArg<2 || $nArg>3} { - usage - } - set config [lindex $args end-1] - set srcdir [lindex $args end] - - set opts [list] ;# OPTS value - set cflags [expr {$bMsvc ? "-Zi" : "-g"}] ;# CFLAGS value - set makeOpts [list] ;# Extra args for [make] - set configOpts [list] ;# Extra args for [configure] - - if {$::tcl_platform(platform)=="windows" || $bMsvc} { - lappend opts -DSQLITE_OS_WIN=1 - } else { - lappend opts -DSQLITE_OS_UNIX=1 - } - - # Figure out if this is a synthetic ndebug or debug configuration. - # - set bRemoveDebug 0 - if {[string match *-ndebug $config]} { - set bRemoveDebug 1 - set config [string range $config 0 end-7] - } - if {[string match *-debug $config]} { - lappend opts -DSQLITE_DEBUG - lappend opts -DSQLITE_EXTRA_IFNULLROW - set config [string range $config 0 end-6] - } - regexp {^(.*)-[0-9]+} $config -> config - - # Ensure that the named configuration exists. - # - if {![info exists ::Configs($config)]} { - puts stderr "No such config: $config" - exit 1 - } - - # Loop through the parameters of the nominated configuration, updating - # $opts, $cflags, $makeOpts and $configOpts along the way. Rules are as - # follows: - # - # 1. If the parameter begins with a "*", discard it. - # - # 2. If $bRemoveDebug is set and the parameter is -DSQLITE_DEBUG or - # -DSQLITE_DEBUG=1, discard it - # - # 3. If the parameter begins with "-D", add it to $opts. - # - # 4. If the parameter begins with "--" add it to $configOpts. Unless - # this command is preparing a script for MSVC - then add an - # equivalent to $makeOpts or $opts. - # - # 5. If the parameter begins with "-" add it to $cflags. If in MSVC - # mode and the parameter is an -O option, instead add - # an OPTIMIZATIONS= switch to $makeOpts. - # - # 6. If none of the above apply, add the parameter to $makeOpts - # - foreach param $::Configs($config) { - if {[string range $param 0 0]=="*"} continue - - if {$bRemoveDebug} { - if {$param=="-DSQLITE_DEBUG" || $param=="-DSQLITE_DEBUG=1" - || $param=="-DSQLITE_MEMDEBUG" || $param=="-DSQLITE_MEMDEBUG=1" - || $param=="--enable-debug" - } { - continue - } - } - - if {[string range $param 0 1]=="-D"} { - lappend opts $param - continue - } - - if {[string range $param 0 1]=="--"} { - if {$bMsvc} { - switch -- $param { - --disable-amalgamation { - lappend makeOpts USE_AMALGAMATION=0 - } - --disable-shared { - lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0 - } - --enable-fts5 { - lappend opts -DSQLITE_ENABLE_FTS5 - } - --enable-shared { - lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1 - } - --enable-session { - lappend opts -DSQLITE_ENABLE_PREUPDATE_HOOK - lappend opts -DSQLITE_ENABLE_SESSION - } - --enable-all { - } - --enable-debug { - # lappend makeOpts OPTIMIZATIONS=0 - lappend opts -DSQLITE_DEBUG - } - default { - error "Cannot translate $param for MSVC" - } - } - } else { - lappend configOpts $param - } - - continue - } - - if {[string range $param 0 0]=="-"} { - if {$bMsvc && [regexp -- {^-O(\d+)$} $param -> level]} { - lappend makeOpts OPTIMIZATIONS=$level - } else { - lappend cflags $param - } - continue - } - - lappend makeOpts $param - } - - # Some configurations specify -DHAVE_USLEEP=0. For all others, add - # -DHAVE_USLEEP=1. - # - if {[lsearch $opts "-DHAVE_USLEEP=0"]<0} { - lappend opts -DHAVE_USLEEP=1 - } - - if {$bMsvc==0} { - puts {set -e} - puts {} - puts {if [ "$#" -ne 1 ] ; then} - puts { echo "Usage: $0 " } - puts { exit -1 } - puts {fi } - puts "SRCDIR=\"$srcdir\"" - puts {} - puts "TCL=\"[::tcl::pkgconfig get libdir,install]\"" - - puts {if [ ! -f Makefile ] ; then} - puts " \$SRCDIR/configure --with-tcl=\$TCL $configOpts" - puts {fi} - puts {} - if {[info exists ::env(OPTS)]} { - puts "# From environment variable:" - puts "OPTS=$::env(OPTS)" - puts "" - } - puts {OPTS="$OPTS -DSQLITE_NO_SYNC=1"} - foreach o $opts { - puts "OPTS=\"\$OPTS $o\"" - } - puts {} - puts "CFLAGS=\"$cflags\"" - puts {} - puts "make \$1 \"CFLAGS=\$CFLAGS\" \"OPTS=\$OPTS\" $makeOpts" - } else { - - set srcdir [file nativename [file normalize $srcdir]] - # set srcdir [string map [list "\\" "\\\\"] $srcdir] - - puts {set TARGET=%1} - set makecmd "nmake /f $srcdir\\Makefile.msc TOP=\"$srcdir\" %TARGET% " - append makecmd "\"CFLAGS=$cflags\" \"OPTS=$opts\" $makeOpts" - - puts "set TMP=%CD%" - puts $makecmd - } -} - -proc main_tests {args} { - set bNodebug 0 - set nArg [llength $args] - if {$nArg==2} { - if {[is_prefix [lindex $args 0] -nodebug 2]} { - set bNodebug 1 - } elseif {[is_prefix [lindex $args 0] -debug 2]} { - set bNodebug 0 - } else usage - } elseif {$nArg==0 || $nArg>2} { - usage - } - set p [lindex $args end] - if {![info exists ::Platforms($p)]} { - puts stderr "No such platform: $p" - exit 1 - } - - set lTest [list] - - foreach {config vars target} $::Platforms($p) { - if {[string range $config end end]=="*"} { - set config [string range $config 0 end-1] - } elseif {$bNodebug==0} { - set dtarget test - if {[lsearch $target fuzztest]<0 && [lsearch $target test]<0} { - set dtarget tcltest - } - if {$vars!=""} { set dtarget "$vars $dtarget" } - - if {[string first SQLITE_DEBUG $::Configs($config)]>=0 - || [string first --enable-debug $::Configs($config)]>=0 - } { - lappend lTest "$config-ndebug \"$dtarget\"" - } else { - lappend lTest "$config-debug \"$dtarget\"" - } - } - - if {[llength $target]==1 && ([string match "*TEST_FAILURE*" $vars] || ( - [lsearch $target "valgrindtest"]<0 - && [lsearch $target "alltest"]<0 - && [lsearch $target "fulltestonly"]<0 - && ![string match Sanitize* $config] - ))} { - if {$vars!=""} { set target "$vars $target" } - lappend lTest "$config \"$target\"" - } else { - set idir -1 - foreach t $target { - if {$t=="valgrindtest" || $t=="alltest" || $t=="fulltestonly" - || [string match Sanitize* $config] - } { - if {$vars!=""} { set t "$vars $t" } - for {set ii 1} {$ii<=4} {incr ii} { - lappend lTest "$config-[incr idir] \"TCLTEST_PART=$ii/4 $t\"" - } - } else { - if {$vars!=""} { set t "$vars $t" } - lappend lTest "$config-[incr idir] \"$t\"" - } - } - } - } - - foreach l $lTest { - puts $l - } - -} - -if {[llength $argv]==0} { usage } -set cmd [lindex $argv 0] -set n [expr [llength $argv]-1] -if {[string match ${cmd}* configurations] && $n==0} { - main_configurations -} elseif {[string match ${cmd}* script]} { - main_script {*}[lrange $argv 1 end] -} elseif {[string match ${cmd}* trscript]} { - main_trscript {*}[lrange $argv 1 end] -} elseif {[string match ${cmd}* platforms] && $n==0} { - main_platforms -} elseif {[string match ${cmd}* tests]} { - main_tests {*}[lrange $argv 1 end] -} else { - usage -} diff --git a/test/wapp.tcl b/test/wapp.tcl deleted file mode 100644 index 53c21e892f..0000000000 --- a/test/wapp.tcl +++ /dev/null @@ -1,987 +0,0 @@ -# Copyright (c) 2017 D. Richard Hipp -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the Simplified BSD License (also -# known as the "2-Clause License" or "FreeBSD License".) -# -# This program is distributed in the hope that it will be useful, -# but without any warranty; without even the implied warranty of -# merchantability or fitness for a particular purpose. -# -#--------------------------------------------------------------------------- -# -# Design rules: -# -# (1) All identifiers in the global namespace begin with "wapp" -# -# (2) Indentifiers intended for internal use only begin with "wappInt" -# -package require Tcl 8.6 - -# Add text to the end of the HTTP reply. No interpretation or transformation -# of the text is performs. The argument should be enclosed within {...} -# -proc wapp {txt} { - global wapp - dict append wapp .reply $txt -} - -# Add text to the page under construction. Do no escaping on the text. -# -# Though "unsafe" in general, there are uses for this kind of thing. -# For example, if you want to return the complete, unmodified content of -# a file: -# -# set fd [open content.html rb] -# wapp-unsafe [read $fd] -# close $fd -# -# You could do the same thing using ordinary "wapp" instead of "wapp-unsafe". -# The difference is that wapp-safety-check will complain about the misuse -# of "wapp", but it assumes that the person who write "wapp-unsafe" understands -# the risks. -# -# Though occasionally necessary, the use of this interface should be minimized. -# -proc wapp-unsafe {txt} { - global wapp - dict append wapp .reply $txt -} - -# Add text to the end of the reply under construction. The following -# substitutions are made: -# -# %html(...) Escape text for inclusion in HTML -# %url(...) Escape text for use as a URL -# %qp(...) Escape text for use as a URI query parameter -# %string(...) Escape text for use within a JSON string -# %unsafe(...) No transformations of the text -# -# The substitutions above terminate at the first ")" character. If the -# text of the TCL string in ... contains ")" characters itself, use instead: -# -# %html%(...)% -# %url%(...)% -# %qp%(...)% -# %string%(...)% -# %unsafe%(...)% -# -# In other words, use "%(...)%" instead of "(...)" to include the TCL string -# to substitute. -# -# The %unsafe substitution should be avoided whenever possible, obviously. -# In addition to the substitutions above, the text also does backslash -# escapes. -# -# The wapp-trim proc works the same as wapp-subst except that it also removes -# whitespace from the left margin, so that the generated HTML/CSS/Javascript -# does not appear to be indented when delivered to the client web browser. -# -if {$tcl_version>=8.7} { - proc wapp-subst {txt} { - global wapp - regsub -all -command \ - {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt wappInt-enc txt - dict append wapp .reply [subst -novariables -nocommand $txt] - } - proc wapp-trim {txt} { - global wapp - regsub -all {\n\s+} [string trim $txt] \n txt - regsub -all -command \ - {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt wappInt-enc txt - dict append wapp .reply [subst -novariables -nocommand $txt] - } - proc wappInt-enc {all mode nu1 txt} { - return [uplevel 2 "wappInt-enc-$mode \"$txt\""] - } -} else { - proc wapp-subst {txt} { - global wapp - regsub -all {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt \ - {[wappInt-enc-\1 "\3"]} txt - dict append wapp .reply [uplevel 1 [list subst -novariables $txt]] - } - proc wapp-trim {txt} { - global wapp - regsub -all {\n\s+} [string trim $txt] \n txt - regsub -all {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt \ - {[wappInt-enc-\1 "\3"]} txt - dict append wapp .reply [uplevel 1 [list subst -novariables $txt]] - } -} - -# There must be a wappInt-enc-NAME routine for each possible substitution -# in wapp-subst. Thus there are routines for "html", "url", "qp", and "unsafe". -# -# wappInt-enc-html Escape text so that it is safe to use in the -# body of an HTML document. -# -# wappInt-enc-url Escape text so that it is safe to pass as an -# argument to href= and src= attributes in HTML. -# -# wappInt-enc-qp Escape text so that it is safe to use as the -# value of a query parameter in a URL or in -# post data or in a cookie. -# -# wappInt-enc-string Escape ", ', \, and < for using inside of a -# javascript string literal. The < character -# is escaped to prevent "" from causing -# problems in embedded javascript. -# -# wappInt-enc-unsafe Perform no encoding at all. Unsafe. -# -proc wappInt-enc-html {txt} { - return [string map {& & < < > > \" " \\ \} $txt] -} -proc wappInt-enc-unsafe {txt} { - return $txt -} -proc wappInt-enc-url {s} { - if {[regsub -all {[^-{}@~?=#_.:/a-zA-Z0-9]} $s {[wappInt-%HHchar {&}]} s]} { - set s [subst -novar -noback $s] - } - if {[regsub -all {[{}]} $s {[wappInt-%HHchar \\&]} s]} { - set s [subst -novar -noback $s] - } - return $s -} -proc wappInt-enc-qp {s} { - if {[regsub -all {[^-{}_.a-zA-Z0-9]} $s {[wappInt-%HHchar {&}]} s]} { - set s [subst -novar -noback $s] - } - if {[regsub -all {[{}]} $s {[wappInt-%HHchar \\&]} s]} { - set s [subst -novar -noback $s] - } - return $s -} -proc wappInt-enc-string {s} { - return [string map {\\ \\\\ \" \\\" ' \\' < \\u003c} $s] -} - -# This is a helper routine for wappInt-enc-url and wappInt-enc-qp. It returns -# an appropriate %HH encoding for the single character c. If c is a unicode -# character, then this routine might return multiple bytes: %HH%HH%HH -# -proc wappInt-%HHchar {c} { - if {$c==" "} {return +} - return [regsub -all .. [binary encode hex [encoding convertto utf-8 $c]] {%&}] -} - - -# Undo the www-url-encoded format. -# -# HT: This code stolen from ncgi.tcl -# -proc wappInt-decode-url {str} { - set str [string map [list + { } "\\" "\\\\" \[ \\\[ \] \\\]] $str] - regsub -all -- \ - {%([Ee][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ - $str {[encoding convertfrom utf-8 [binary decode hex \1\2\3]]} str - regsub -all -- \ - {%([CDcd][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ - $str {[encoding convertfrom utf-8 [binary decode hex \1\2]]} str - regsub -all -- {%([0-7][A-Fa-f0-9])} $str {\\u00\1} str - return [subst -novar $str] -} - -# Reset the document back to an empty string. -# -proc wapp-reset {} { - global wapp - dict set wapp .reply {} -} - -# Change the mime-type of the result document. -# -proc wapp-mimetype {x} { - global wapp - dict set wapp .mimetype $x -} - -# Change the reply code. -# -proc wapp-reply-code {x} { - global wapp - dict set wapp .reply-code $x -} - -# Set a cookie -# -proc wapp-set-cookie {name value} { - global wapp - dict lappend wapp .new-cookies $name $value -} - -# Unset a cookie -# -proc wapp-clear-cookie {name} { - wapp-set-cookie $name {} -} - -# Add extra entries to the reply header -# -proc wapp-reply-extra {name value} { - global wapp - dict lappend wapp .reply-extra $name $value -} - -# Specifies how the web-page under construction should be cached. -# The argument should be one of: -# -# no-cache -# max-age=N (for some integer number of seconds, N) -# private,max-age=N -# -proc wapp-cache-control {x} { - wapp-reply-extra Cache-Control $x -} - -# Redirect to a different web page -# -proc wapp-redirect {uri} { - wapp-reply-code {307 Redirect} - wapp-reply-extra Location $uri -} - -# Return the value of a wapp parameter -# -proc wapp-param {name {dflt {}}} { - global wapp - if {![dict exists $wapp $name]} {return $dflt} - return [dict get $wapp $name] -} - -# Return true if a and only if the wapp parameter $name exists -# -proc wapp-param-exists {name} { - global wapp - return [dict exists $wapp $name] -} - -# Set the value of a wapp parameter -# -proc wapp-set-param {name value} { - global wapp - dict set wapp $name $value -} - -# Return all parameter names that match the GLOB pattern, or all -# names if the GLOB pattern is omitted. -# -proc wapp-param-list {{glob {*}}} { - global wapp - return [dict keys $wapp $glob] -} - -# By default, Wapp does not decode query parameters and POST parameters -# for cross-origin requests. This is a security restriction, designed to -# help prevent cross-site request forgery (CSRF) attacks. -# -# As a consequence of this restriction, URLs for sites generated by Wapp -# that contain query parameters will not work as URLs found in other -# websites. You cannot create a link from a second website into a Wapp -# website if the link contains query planner, by default. -# -# Of course, it is sometimes desirable to allow query parameters on external -# links. For URLs for which this is safe, the application should invoke -# wapp-allow-xorigin-params. This procedure tells Wapp that it is safe to -# go ahead and decode the query parameters even for cross-site requests. -# -# In other words, for Wapp security is the default setting. Individual pages -# need to actively disable the cross-site request security if those pages -# are safe for cross-site access. -# -proc wapp-allow-xorigin-params {} { - global wapp - if {![dict exists $wapp .qp] && ![dict get $wapp SAME_ORIGIN]} { - wappInt-decode-query-params - } -} - -# Set the content-security-policy. -# -# The default content-security-policy is very strict: "default-src 'self'" -# The default policy prohibits the use of in-line javascript or CSS. -# -# Provide an alternative CSP as the argument. Or use "off" to disable -# the CSP completely. -# -proc wapp-content-security-policy {val} { - global wapp - if {$val=="off"} { - dict unset wapp .csp - } else { - dict set wapp .csp $val - } -} - -# Examine the bodys of all procedures in this program looking for -# unsafe calls to various Wapp interfaces. Return a text string -# containing warnings. Return an empty string if all is ok. -# -# This routine is advisory only. It misses some constructs that are -# dangerous and flags others that are safe. -# -proc wapp-safety-check {} { - set res {} - foreach p [info procs] { - set ln 0 - foreach x [split [info body $p] \n] { - incr ln - if {[regexp {^[ \t]*wapp[ \t]+([^\n]+)} $x all tail] - && [string index $tail 0]!="\173" - && [regexp {[[$]} $tail] - } { - append res "$p:$ln: unsafe \"wapp\" call: \"[string trim $x]\"\n" - } - if {[regexp {^[ \t]*wapp-(subst|trim)[ \t]+[^\173]} $x all cx]} { - append res "$p:$ln: unsafe \"wapp-$cx\" call: \"[string trim $x]\"\n" - } - } - } - return $res -} - -# Return a string that descripts the current environment. Applications -# might find this useful for debugging. -# -proc wapp-debug-env {} { - global wapp - set out {} - foreach var [lsort [dict keys $wapp]] { - if {[string index $var 0]=="."} continue - append out "$var = [list [dict get $wapp $var]]\n" - } - append out "\[pwd\] = [list [pwd]]\n" - return $out -} - -# Tracing function for each HTTP request. This is overridden by wapp-start -# if tracing is enabled. -# -proc wappInt-trace {} {} - -# Start up a listening socket. Arrange to invoke wappInt-new-connection -# for each inbound HTTP connection. -# -# port Listen on this TCP port. 0 means to select a port -# that is not currently in use -# -# wappmode One of "scgi", "remote-scgi", "server", or "local". -# -# fromip If not {}, then reject all requests from IP addresses -# other than $fromip -# -proc wappInt-start-listener {port wappmode fromip} { - if {[string match *scgi $wappmode]} { - set type SCGI - set server [list wappInt-new-connection \ - wappInt-scgi-readable $wappmode $fromip] - } else { - set type HTTP - set server [list wappInt-new-connection \ - wappInt-http-readable $wappmode $fromip] - } - if {$wappmode=="local" || $wappmode=="scgi"} { - set x [socket -server $server -myaddr 127.0.0.1 $port] - } else { - set x [socket -server $server $port] - } - set coninfo [chan configure $x -sockname] - set port [lindex $coninfo 2] - if {$wappmode=="local"} { - wappInt-start-browser http://127.0.0.1:$port/ - } elseif {$fromip!=""} { - puts "Listening for $type requests on TCP port $port from IP $fromip" - } else { - puts "Listening for $type requests on TCP port $port" - } -} - -# Start a web-browser and point it at $URL -# -proc wappInt-start-browser {url} { - global tcl_platform - if {$tcl_platform(platform)=="windows"} { - exec cmd /c start $url & - } elseif {$tcl_platform(os)=="Darwin"} { - exec open $url & - } elseif {[catch {exec xdg-open $url}]} { - exec firefox $url & - } -} - -# This routine is a "socket -server" callback. The $chan, $ip, and $port -# arguments are added by the socket command. -# -# Arrange to invoke $callback when content is available on the new socket. -# The $callback will process inbound HTTP or SCGI content. Reject the -# request if $fromip is not an empty string and does not match $ip. -# -proc wappInt-new-connection {callback wappmode fromip chan ip port} { - upvar #0 wappInt-$chan W - if {$fromip!="" && ![string match $fromip $ip]} { - close $chan - return - } - set W [dict create REMOTE_ADDR $ip REMOTE_PORT $port WAPP_MODE $wappmode \ - .header {}] - fconfigure $chan -blocking 0 -translation binary - fileevent $chan readable [list $callback $chan] -} - -# Close an input channel -# -proc wappInt-close-channel {chan} { - if {$chan=="stdout"} { - # This happens after completing a CGI request - exit 0 - } else { - unset ::wappInt-$chan - close $chan - } -} - -# Process new text received on an inbound HTTP request -# -proc wappInt-http-readable {chan} { - if {[catch [list wappInt-http-readable-unsafe $chan] msg]} { - puts stderr "$msg\n$::errorInfo" - wappInt-close-channel $chan - } -} -proc wappInt-http-readable-unsafe {chan} { - upvar #0 wappInt-$chan W wapp wapp - if {![dict exists $W .toread]} { - # If the .toread key is not set, that means we are still reading - # the header - set line [string trimright [gets $chan]] - set n [string length $line] - if {$n>0} { - if {[dict get $W .header]=="" || [regexp {^\s+} $line]} { - dict append W .header $line - } else { - dict append W .header \n$line - } - if {[string length [dict get $W .header]]>100000} { - error "HTTP request header too big - possible DOS attack" - } - } elseif {$n==0} { - # We have reached the blank line that terminates the header. - global argv0 - set a0 [file normalize $argv0] - dict set W SCRIPT_FILENAME $a0 - dict set W DOCUMENT_ROOT [file dir $a0] - if {[wappInt-parse-header $chan]} { - catch {close $chan} - return - } - set len 0 - if {[dict exists $W CONTENT_LENGTH]} { - set len [dict get $W CONTENT_LENGTH] - } - if {$len>0} { - # Still need to read the query content - dict set W .toread $len - } else { - # There is no query content, so handle the request immediately - set wapp $W - wappInt-handle-request $chan 0 - } - } - } else { - # If .toread is set, that means we are reading the query content. - # Continue reading until .toread reaches zero. - set got [read $chan [dict get $W .toread]] - dict append W CONTENT $got - dict set W .toread [expr {[dict get $W .toread]-[string length $got]}] - if {[dict get $W .toread]<=0} { - # Handle the request as soon as all the query content is received - set wapp $W - wappInt-handle-request $chan 0 - } - } -} - -# Decode the HTTP request header. -# -# This routine is always running inside of a [catch], so if -# any problems arise, simply raise an error. -# -proc wappInt-parse-header {chan} { - upvar #0 wappInt-$chan W - set hdr [split [dict get $W .header] \n] - if {$hdr==""} {return 1} - set req [lindex $hdr 0] - dict set W REQUEST_METHOD [set method [lindex $req 0]] - if {[lsearch {GET HEAD POST} $method]<0} { - error "unsupported request method: \"[dict get $W REQUEST_METHOD]\"" - } - set uri [lindex $req 1] - set split_uri [split $uri ?] - set uri0 [lindex $split_uri 0] - if {![regexp {^/[-.a-z0-9_/]*$} $uri0]} { - error "invalid request uri: \"$uri0\"" - } - dict set W REQUEST_URI $uri0 - dict set W PATH_INFO $uri0 - set uri1 [lindex $split_uri 1] - dict set W QUERY_STRING $uri1 - set n [llength $hdr] - for {set i 1} {$i<$n} {incr i} { - set x [lindex $hdr $i] - if {![regexp {^(.+): +(.*)$} $x all name value]} { - error "invalid header line: \"$x\"" - } - set name [string toupper $name] - switch -- $name { - REFERER {set name HTTP_REFERER} - USER-AGENT {set name HTTP_USER_AGENT} - CONTENT-LENGTH {set name CONTENT_LENGTH} - CONTENT-TYPE {set name CONTENT_TYPE} - HOST {set name HTTP_HOST} - COOKIE {set name HTTP_COOKIE} - ACCEPT-ENCODING {set name HTTP_ACCEPT_ENCODING} - default {set name .hdr:$name} - } - dict set W $name $value - } - return 0 -} - -# Decode the QUERY_STRING parameters from a GET request or the -# application/x-www-form-urlencoded CONTENT from a POST request. -# -# This routine sets the ".qp" element of the ::wapp dict as a signal -# that query parameters have already been decoded. -# -proc wappInt-decode-query-params {} { - global wapp - dict set wapp .qp 1 - if {[dict exists $wapp QUERY_STRING]} { - foreach qterm [split [dict get $wapp QUERY_STRING] &] { - set qsplit [split $qterm =] - set nm [lindex $qsplit 0] - if {[regexp {^[a-z][a-z0-9]*$} $nm]} { - dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] - } - } - } - if {[dict exists $wapp CONTENT_TYPE] && [dict exists $wapp CONTENT]} { - set ctype [dict get $wapp CONTENT_TYPE] - if {$ctype=="application/x-www-form-urlencoded"} { - foreach qterm [split [string trim [dict get $wapp CONTENT]] &] { - set qsplit [split $qterm =] - set nm [lindex $qsplit 0] - if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { - dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] - } - } - } elseif {[string match multipart/form-data* $ctype]} { - regexp {^(.*?)\r\n(.*)$} [dict get $wapp CONTENT] all divider body - set ndiv [string length $divider] - while {[string length $body]} { - set idx [string first $divider $body] - set unit [string range $body 0 [expr {$idx-3}]] - set body [string range $body [expr {$idx+$ndiv+2}] end] - if {[regexp {^Content-Disposition: form-data; (.*?)\r\n\r\n(.*)$} \ - $unit unit hdr content]} { - if {[regexp {name="(.*)"; filename="(.*)"\r\nContent-Type: (.*?)$}\ - $hdr hr name filename mimetype]} { - dict set wapp $name.filename \ - [string map [list \\\" \" \\\\ \\] $filename] - dict set wapp $name.mimetype $mimetype - dict set wapp $name.content $content - } elseif {[regexp {name="(.*)"} $hdr hr name]} { - dict set wapp $name $content - } - } - } - } - } -} - -# Invoke application-supplied methods to generate a reply to -# a single HTTP request. -# -# This routine always runs within [catch], so handle exceptions by -# invoking [error]. -# -proc wappInt-handle-request {chan useCgi} { - global wapp - dict set wapp .reply {} - dict set wapp .mimetype {text/html; charset=utf-8} - dict set wapp .reply-code {200 Ok} - dict set wapp .csp {default-src 'self'} - - # Set up additional CGI environment values - # - if {![dict exists $wapp HTTP_HOST]} { - dict set wapp BASE_URL {} - } elseif {[dict exists $wapp HTTPS]} { - dict set wapp BASE_URL https://[dict get $wapp HTTP_HOST] - } else { - dict set wapp BASE_URL http://[dict get $wapp HTTP_HOST] - } - if {![dict exists $wapp REQUEST_URI]} { - dict set wapp REQUEST_URI / - } elseif {[regsub {\?.*} [dict get $wapp REQUEST_URI] {} newR]} { - # Some servers (ex: nginx) append the query parameters to REQUEST_URI. - # These need to be stripped off - dict set wapp REQUEST_URI $newR - } - if {[dict exists $wapp SCRIPT_NAME]} { - dict append wapp BASE_URL [dict get $wapp SCRIPT_NAME] - } else { - dict set wapp SCRIPT_NAME {} - } - if {![dict exists $wapp PATH_INFO]} { - # If PATH_INFO is missing (ex: nginx) then construct it - set URI [dict get $wapp REQUEST_URI] - set skip [string length [dict get $wapp SCRIPT_NAME]] - dict set wapp PATH_INFO [string range $URI $skip end] - } - if {[regexp {^/([^/]+)(.*)$} [dict get $wapp PATH_INFO] all head tail]} { - dict set wapp PATH_HEAD $head - dict set wapp PATH_TAIL [string trimleft $tail /] - } else { - dict set wapp PATH_INFO {} - dict set wapp PATH_HEAD {} - dict set wapp PATH_TAIL {} - } - dict set wapp SELF_URL [dict get $wapp BASE_URL]/[dict get $wapp PATH_HEAD] - - # Parse query parameters from the query string, the cookies, and - # POST data - # - if {[dict exists $wapp HTTP_COOKIE]} { - foreach qterm [split [dict get $wapp HTTP_COOKIE] {;}] { - set qsplit [split [string trim $qterm] =] - set nm [lindex $qsplit 0] - if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { - dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] - } - } - } - set same_origin 0 - if {[dict exists $wapp HTTP_REFERER]} { - set referer [dict get $wapp HTTP_REFERER] - set base [dict get $wapp BASE_URL] - if {$referer==$base || [string match $base/* $referer]} { - set same_origin 1 - } - } - dict set wapp SAME_ORIGIN $same_origin - if {$same_origin} { - wappInt-decode-query-params - } - - # Invoke the application-defined handler procedure for this page - # request. If an error occurs while running that procedure, generate - # an HTTP reply that contains the error message. - # - wapp-before-dispatch-hook - wappInt-trace - set mname [dict get $wapp PATH_HEAD] - if {[catch { - if {$mname!="" && [llength [info proc wapp-page-$mname]]>0} { - wapp-page-$mname - } else { - wapp-default - } - } msg]} { - if {[wapp-param WAPP_MODE]=="local" || [wapp-param WAPP_MODE]=="server"} { - puts "ERROR: $::errorInfo" - } - wapp-reset - wapp-reply-code "500 Internal Server Error" - wapp-mimetype text/html - wapp-trim { -

    Wapp Application Error

    -
    %html($::errorInfo)
    - } - dict unset wapp .new-cookies - } - - # Transmit the HTTP reply - # - if {$chan=="stdout"} { - puts $chan "Status: [dict get $wapp .reply-code]\r" - } else { - puts $chan "HTTP/1.1 [dict get $wapp .reply-code]\r" - puts $chan "Server: wapp\r" - puts $chan "Connection: close\r" - } - if {[dict exists $wapp .reply-extra]} { - foreach {name value} [dict get $wapp .reply-extra] { - puts $chan "$name: $value\r" - } - } - if {[dict exists $wapp .csp]} { - puts $chan "Content-Security-Policy: [dict get $wapp .csp]\r" - } - set mimetype [dict get $wapp .mimetype] - puts $chan "Content-Type: $mimetype\r" - if {[dict exists $wapp .new-cookies]} { - foreach {nm val} [dict get $wapp .new-cookies] { - if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { - if {$val==""} { - puts $chan "Set-Cookie: $nm=; HttpOnly; Path=/; Max-Age=1\r" - } else { - set val [wappInt-enc-url $val] - puts $chan "Set-Cookie: $nm=$val; HttpOnly; Path=/\r" - } - } - } - } - if {[string match text/* $mimetype]} { - set reply [encoding convertto utf-8 [dict get $wapp .reply]] - if {[regexp {\ygzip\y} [wapp-param HTTP_ACCEPT_ENCODING]]} { - catch { - set x [zlib gzip $reply] - set reply $x - puts $chan "Content-Encoding: gzip\r" - } - } - } else { - set reply [dict get $wapp .reply] - } - puts $chan "Content-Length: [string length $reply]\r" - puts $chan \r - puts -nonewline $chan $reply - flush $chan - wappInt-close-channel $chan -} - -# This routine runs just prior to request-handler dispatch. The -# default implementation is a no-op, but applications can override -# to do additional transformations or checks. -# -proc wapp-before-dispatch-hook {} {return} - -# Process a single CGI request -# -proc wappInt-handle-cgi-request {} { - global wapp env - foreach key { - CONTENT_LENGTH - CONTENT_TYPE - DOCUMENT_ROOT - HTTP_ACCEPT_ENCODING - HTTP_COOKIE - HTTP_HOST - HTTP_REFERER - HTTP_USER_AGENT - HTTPS - PATH_INFO - QUERY_STRING - REMOTE_ADDR - REQUEST_METHOD - REQUEST_URI - REMOTE_USER - SCRIPT_FILENAME - SCRIPT_NAME - SERVER_NAME - SERVER_PORT - SERVER_PROTOCOL - } { - if {[info exists env($key)]} { - dict set wapp $key $env($key) - } - } - set len 0 - if {[dict exists $wapp CONTENT_LENGTH]} { - set len [dict get $wapp CONTENT_LENGTH] - } - if {$len>0} { - fconfigure stdin -translation binary - dict set wapp CONTENT [read stdin $len] - } - dict set wapp WAPP_MODE cgi - fconfigure stdout -translation binary - wappInt-handle-request stdout 1 -} - -# Process new text received on an inbound SCGI request -# -proc wappInt-scgi-readable {chan} { - if {[catch [list wappInt-scgi-readable-unsafe $chan] msg]} { - puts stderr "$msg\n$::errorInfo" - wappInt-close-channel $chan - } -} -proc wappInt-scgi-readable-unsafe {chan} { - upvar #0 wappInt-$chan W wapp wapp - if {![dict exists $W .toread]} { - # If the .toread key is not set, that means we are still reading - # the header. - # - # An SGI header is short. This implementation assumes the entire - # header is available all at once. - # - dict set W .remove_addr [dict get $W REMOTE_ADDR] - set req [read $chan 15] - set n [string length $req] - scan $req %d:%s len hdr - incr len [string length "$len:,"] - append hdr [read $chan [expr {$len-15}]] - foreach {nm val} [split $hdr \000] { - if {$nm==","} break - dict set W $nm $val - } - set len 0 - if {[dict exists $W CONTENT_LENGTH]} { - set len [dict get $W CONTENT_LENGTH] - } - if {$len>0} { - # Still need to read the query content - dict set W .toread $len - } else { - # There is no query content, so handle the request immediately - dict set W SERVER_ADDR [dict get $W .remove_addr] - set wapp $W - wappInt-handle-request $chan 0 - } - } else { - # If .toread is set, that means we are reading the query content. - # Continue reading until .toread reaches zero. - set got [read $chan [dict get $W .toread]] - dict append W CONTENT $got - dict set W .toread [expr {[dict get $W .toread]-[string length $got]}] - if {[dict get $W .toread]<=0} { - # Handle the request as soon as all the query content is received - dict set W SERVER_ADDR [dict get $W .remove_addr] - set wapp $W - wappInt-handle-request $chan 0 - } - } -} - -# Start up the wapp framework. Parameters are a list passed as the -# single argument. -# -# -server $PORT Listen for HTTP requests on this TCP port $PORT -# -# -local $PORT Listen for HTTP requests on 127.0.0.1:$PORT -# -# -scgi $PORT Listen for SCGI requests on 127.0.0.1:$PORT -# -# -remote-scgi $PORT Listen for SCGI requests on TCP port $PORT -# -# -cgi Handle a single CGI request -# -# With no arguments, the behavior is called "auto". In "auto" mode, -# if the GATEWAY_INTERFACE environment variable indicates CGI, then run -# as CGI. Otherwise, start an HTTP server bound to the loopback address -# only, on an arbitrary TCP port, and automatically launch a web browser -# on that TCP port. -# -# Additional options: -# -# -fromip GLOB Reject any incoming request where the remote -# IP address does not match the GLOB pattern. This -# value defaults to '127.0.0.1' for -local and -scgi. -# -# -nowait Do not wait in the event loop. Return immediately -# after all event handlers are established. -# -# -trace "puts" each request URL as it is handled, for -# debugging -# -# -lint Run wapp-safety-check on the application instead -# of running the application itself -# -# -Dvar=value Set TCL global variable "var" to "value" -# -# -proc wapp-start {arglist} { - global env - set mode auto - set port 0 - set nowait 0 - set fromip {} - set n [llength $arglist] - for {set i 0} {$i<$n} {incr i} { - set term [lindex $arglist $i] - if {[string match --* $term]} {set term [string range $term 1 end]} - switch -glob -- $term { - -server { - incr i; - set mode "server" - set port [lindex $arglist $i] - } - -local { - incr i; - set mode "local" - set fromip 127.0.0.1 - set port [lindex $arglist $i] - } - -scgi { - incr i; - set mode "scgi" - set fromip 127.0.0.1 - set port [lindex $arglist $i] - } - -remote-scgi { - incr i; - set mode "remote-scgi" - set port [lindex $arglist $i] - } - -cgi { - set mode "cgi" - } - -fromip { - incr i - set fromip [lindex $arglist $i] - } - -nowait { - set nowait 1 - } - -trace { - proc wappInt-trace {} { - set q [wapp-param QUERY_STRING] - set uri [wapp-param BASE_URL][wapp-param PATH_INFO] - if {$q!=""} {append uri ?$q} - puts $uri - } - } - -lint { - set res [wapp-safety-check] - if {$res!=""} { - puts "Potential problems in this code:" - puts $res - exit 1 - } else { - exit - } - } - -D*=* { - if {[regexp {^.D([^=]+)=(.*)$} $term all var val]} { - set ::$var $val - } - } - default { - error "unknown option: $term" - } - } - } - if {$mode=="auto"} { - if {[info exists env(GATEWAY_INTERFACE)] - && [string match CGI/1.* $env(GATEWAY_INTERFACE)]} { - set mode cgi - } else { - set mode local - } - } - if {$mode=="cgi"} { - wappInt-handle-cgi-request - } else { - wappInt-start-listener $port $mode $fromip - if {!$nowait} { - vwait ::forever - } - } -} - -# Call this version 1.0 -package provide wapp 1.0 diff --git a/test/wapptest.tcl b/test/wapptest.tcl deleted file mode 100755 index d37b2e48c6..0000000000 --- a/test/wapptest.tcl +++ /dev/null @@ -1,909 +0,0 @@ -#!/bin/sh -# \ -exec wapptclsh "$0" ${1+"$@"} - -# package required wapp -source [file join [file dirname [info script]] wapp.tcl] - -# Variables set by the "control" form: -# -# G(platform) - User selected platform. -# G(cfgglob) - Glob pattern that all configurations must match -# G(test) - Set to "Normal", "Veryquick", "Smoketest" or "Build-Only". -# G(keep) - Boolean. True to delete no files after each test. -# G(msvc) - Boolean. True to use MSVC as the compiler. -# G(tcl) - Use Tcl from this directory for builds. -# G(jobs) - How many sub-processes to run simultaneously. -# -set G(platform) $::tcl_platform(os)-$::tcl_platform(machine) -set G(cfgglob) * -set G(test) Normal -set G(keep) 1 -set G(msvc) 0 -set G(tcl) [::tcl::pkgconfig get libdir,install] -set G(jobs) 3 -set G(debug) 0 - -set G(noui) 0 -set G(stdout) 0 - - -proc wapptest_init {} { - global G - - set lSave [list platform test keep msvc tcl jobs debug noui stdout cfgglob] - foreach k $lSave { set A($k) $G($k) } - array unset G - foreach k $lSave { set G($k) $A($k) } - - # The root of the SQLite source tree. - set G(srcdir) [file dirname [file dirname [info script]]] - - set G(sqlite_version) "unknown" - - # Either "config", "running" or "stopped": - set G(state) "config" - - set G(hostname) "(unknown host)" - catch { set G(hostname) [exec hostname] } - set G(host) $G(hostname) - append G(host) " $::tcl_platform(os) $::tcl_platform(osVersion)" - append G(host) " $::tcl_platform(machine) $::tcl_platform(byteOrder)" -} - -proc wapptest_run {} { - global G - set_test_array - set G(state) "running" - - wapptest_openlog - - wapptest_output "Running the following for $G(platform). $G(jobs) jobs." - foreach t $G(test_array) { - set config [dict get $t config] - set target [dict get $t target] - wapptest_output [format " %-25s%s" $config $target] - } - wapptest_output [string repeat * 70] -} - -proc releasetest_data {args} { - global G - set rtd [file join $G(srcdir) test releasetest_data.tcl] - set fd [open "|[info nameofexecutable] $rtd $args" r+] - set ret [read $fd] - close $fd - return $ret -} - -# Generate the text for the box at the top of the UI. The current SQLite -# version, according to fossil, along with a warning if there are -# uncommitted changes in the checkout. -# -proc generate_fossil_info {} { - global G - set pwd [pwd] - cd $G(srcdir) - set rc [catch { - set r1 [exec fossil info] - set r2 [exec fossil changes] - }] - cd $pwd - if {$rc} return - - foreach line [split $r1 "\n"] { - if {[regexp {^checkout: *(.*)$} $line -> co]} { - wapp-trim {
    %html($co) } - } - } - - if {[string trim $r2]!=""} { - wapp-trim { -
    - WARNING: Uncommitted changes in checkout - - } - } -} - -# If the application is in "config" state, set the contents of the -# ::G(test_array) global to reflect the tests that will be run. If the -# app is in some other state ("running" or "stopped"), this command -# is a no-op. -# -proc set_test_array {} { - global G - if { $G(state)=="config" } { - set G(test_array) [list] - set debug "-debug" - if {$G(debug)==0} { set debug "-nodebug"} - foreach {config target} [releasetest_data tests $debug $G(platform)] { - - # All configuration names must match $g(cfgglob), which defaults to * - # - if {![string match -nocase $G(cfgglob) $config]} continue - - # If using MSVC, do not run sanitize or valgrind tests. Or the - # checksymbols test. - if {$G(msvc) && ( - "Sanitize" == $config - || "checksymbols" in $target - || "valgrindtest" in $target - )} { - continue - } - - # If the test mode is not "Normal", override the target. - # - if {$target!="checksymbols" && $G(platform)!="Failure-Detection"} { - switch -- $G(test) { - Veryquick { set target quicktest } - Smoketest { set target smoketest } - Build-Only { - set target testfixture - if {$::tcl_platform(platform)=="windows"} { - set target testfixture.exe - } - } - } - } - - lappend G(test_array) [dict create config $config target $target] - } - } -} - -proc count_tests_and_errors {name logfile} { - global G - - set fd [open $logfile rb] - set seen 0 - while {![eof $fd]} { - set line [gets $fd] - if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} { - incr G(test.$name.nError) $nerr - incr G(test.$name.nTest) $ntest - set seen 1 - if {$nerr>0} { - set G(test.$name.errmsg) $line - } - } - if {[regexp {runtime error: +(.*)} $line all msg]} { - # skip over "value is outside range" errors - if {[regexp {.* is outside the range of representable} $line]} { - # noop - } else { - incr G(test.$name.nError) - if {$G(test.$name.errmsg)==""} { - set G(test.$name.errmsg) $msg - } - } - } - if {[regexp {fatal error +(.*)} $line all msg]} { - incr G(test.$name.nError) - if {$G(test.$name.errmsg)==""} { - set G(test.$name.errmsg) $msg - } - } - if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} { - incr G(test.$name.nError) - if {$G(test.$name.errmsg)==""} { - set G(test.$name.errmsg) $all - } - } - if {[regexp {^VERSION: 3\.\d+.\d+} $line]} { - set v [string range $line 9 end] - if {$G(sqlite_version) eq "unknown"} { - set G(sqlite_version) $v - } elseif {$G(sqlite_version) ne $v} { - set G(test.$name.errmsg) "version conflict: {$G(sqlite_version)} vs. {$v}" - } - } - } - close $fd - if {$G(test) == "Build-Only"} { - incr G(test.$name.nTest) - if {$G(test.$name.nError)>0} { - set errmsg "Build failed" - } - } elseif {!$seen} { - set G(test.$name.errmsg) "Test did not complete" - if {[file readable core]} { - append G(test.$name.errmsg) " - core file exists" - } - } -} - -proc wapptest_output {str} { - global G - if {$G(stdout)} { puts $str } - if {[info exists G(log)]} { - puts $G(log) $str - flush $G(log) - } -} -proc wapptest_openlog {} { - global G - set G(log) [open wapptest-out.txt w+] -} -proc wapptest_closelog {} { - global G - close $G(log) - unset G(log) -} - -proc format_seconds {seconds} { - set min [format %.2d [expr ($seconds / 60) % 60]] - set hr [format %.2d [expr $seconds / 3600]] - set sec [format %.2d [expr $seconds % 60]] - return "$hr:$min:$sec" -} - -# This command is invoked once a slave process has finished running its -# tests, successfully or otherwise. Parameter $name is the name of the -# test, $rc the exit code returned by the slave process. -# -proc slave_test_done {name rc} { - global G - set G(test.$name.done) [clock seconds] - set G(test.$name.nError) 0 - set G(test.$name.nTest) 0 - set G(test.$name.errmsg) "" - if {$rc} { - incr G(test.$name.nError) - } - if {[file exists $G(test.$name.log)]} { - count_tests_and_errors $name $G(test.$name.log) - } - - # If the "keep files" checkbox is clear, delete all files except for - # the executables and test logs. And any core file that is present. - if {$G(keep)==0} { - set keeplist { - testfixture testfixture.exe - sqlite3 sqlite3.exe - test.log test-out.txt - core - wapptest_make.sh - wapptest_configure.sh - wapptest_run.tcl - } - foreach f [glob -nocomplain [file join $G(test.$name.dir) *]] { - set t [file tail $f] - if {[lsearch $keeplist $t]<0} { - catch { file delete -force $f } - } - } - } - - # Format a message regarding the success or failure of hte test. - set t [format_seconds [expr $G(test.$name.done) - $G(test.$name.start)]] - set res "OK" - if {$G(test.$name.nError)} { set res "FAILED" } - set dots [string repeat . [expr 60 - [string length $name]]] - set msg "$name $dots $res ($t)" - - wapptest_output $msg - if {[info exists G(test.$name.errmsg)] && $G(test.$name.errmsg)!=""} { - wapptest_output " $G(test.$name.errmsg)" - } -} - -# This is a fileevent callback invoked each time a file-descriptor that -# connects this process to a slave process is readable. -# -proc slave_fileevent {name} { - global G - set fd $G(test.$name.channel) - - if {[eof $fd]} { - fconfigure $fd -blocking 1 - set rc [catch { close $fd }] - unset G(test.$name.channel) - slave_test_done $name $rc - } else { - set line [gets $fd] - if {[string trim $line] != ""} { puts "Trace : $name - \"$line\"" } - } - - do_some_stuff -} - -# Return the contents of the "slave script" - the script run by slave -# processes to actually perform the test. All it does is execute the -# test script already written to disk (wapptest_cmd.sh or wapptest_cmd.bat). -# -proc wapptest_slave_script {} { - global G - if {$G(msvc)==0} { - set dir [file join .. $G(srcdir)] - set res [subst -nocommands { - set rc [catch "exec sh wapptest_cmd.sh {$dir} >>& test.log" ] - exit [set rc] - }] - } else { - set dir [file nativename [file normalize $G(srcdir)]] - set dir [string map [list "\\" "\\\\"] $dir] - set res [subst -nocommands { - set rc [catch "exec wapptest_cmd.bat {$dir} >>& test.log" ] - exit [set rc] - }] - } - - set res -} - - -# Launch a slave process to run a test. -# -proc slave_launch {name target dir} { - global G - - catch { file mkdir $dir } msg - foreach f [glob -nocomplain [file join $dir *]] { - catch { file delete -force $f } - } - set G(test.$name.dir) $dir - - # Write the test command to wapptest_cmd.sh|bat. - # - set ext sh - if {$G(msvc)} { set ext bat } - set fd1 [open [file join $dir wapptest_cmd.$ext] w] - if {$G(msvc)} { - puts $fd1 [releasetest_data script -msvc $name $target] - } else { - puts $fd1 [releasetest_data script $name $target] - } - close $fd1 - - # Write the wapptest_run.tcl script to the test directory. To run the - # commands in the other two files. - # - set fd3 [open [file join $dir wapptest_run.tcl] w] - puts $fd3 [wapptest_slave_script] - close $fd3 - - set pwd [pwd] - cd $dir - set fd [open "|[info nameofexecutable] wapptest_run.tcl" r+] - cd $pwd - - set G(test.$name.channel) $fd - fconfigure $fd -blocking 0 - fileevent $fd readable [list slave_fileevent $name] -} - -proc do_some_stuff {} { - global G - - # Count the number of running jobs. A running job has an entry named - # "channel" in its dictionary. - set nRunning 0 - set bFinished 1 - foreach j $G(test_array) { - set name [dict get $j config] - if { [info exists G(test.$name.channel)]} { incr nRunning } - if {![info exists G(test.$name.done)]} { set bFinished 0 } - } - - if {$bFinished} { - set nError 0 - set nTest 0 - set nConfig 0 - foreach j $G(test_array) { - set name [dict get $j config] - incr nError $G(test.$name.nError) - incr nTest $G(test.$name.nTest) - incr nConfig - } - set G(result) "$nError errors from $nTest tests in $nConfig configurations." - wapptest_output [string repeat * 70] - wapptest_output $G(result) - catch { - append G(result) " SQLite version $G(sqlite_version)" - wapptest_output " SQLite version $G(sqlite_version)" - } - set G(state) "stopped" - wapptest_closelog - if {$G(noui)} { exit 0 } - } else { - set nLaunch [expr $G(jobs) - $nRunning] - foreach j $G(test_array) { - if {$nLaunch<=0} break - set name [dict get $j config] - if { ![info exists G(test.$name.channel)] - && ![info exists G(test.$name.done)] - } { - - set target [dict get $j target] - set dir [string tolower [string map {" " _ "-" _} $name]] - set G(test.$name.start) [clock seconds] - set G(test.$name.log) [file join $dir test.log] - - slave_launch $name $target $dir - - incr nLaunch -1 - } - } - } -} - -proc generate_select_widget {label id lOpt opt} { - wapp-trim { - - } -} - -proc generate_main_page {{extra {}}} { - global G - set_test_array - - set hostname $G(hostname) - wapp-trim { - - - %html($hostname): wapptest.tcl - - - - } - - set host $G(host) - wapp-trim { -
    %string($host) - } - generate_fossil_info - wapp-trim { -
    -
    -
    - } - - # Build the "platform" select widget. - set lOpt [releasetest_data platforms] - generate_select_widget Platform control_platform $lOpt $G(platform) - - # Build the "test" select widget. - set lOpt [list Normal Veryquick Smoketest Build-Only] - generate_select_widget Test control_test $lOpt $G(test) - - # Build the "jobs" select widget. Options are 1 to 8. - generate_select_widget Jobs control_jobs {1 2 3 4 5 6 7 8 12 16} $G(jobs) - - switch $G(state) { - config { - set txt "Run Tests!" - set id control_run - } - running { - set txt "STOP Tests!" - set id control_stop - } - stopped { - set txt "Reset!" - set id control_reset - } - } - wapp-trim { -
    - - -
    - } - - wapp-trim { -

    - - - - - - - - - - - } - wapp-trim { -
    - } - wapp-trim { -
    -
    - } - wapp-page-tests - - set script "script/$G(state).js" - wapp-trim { -
    - - - - } -} - -proc wapp-default {} { - generate_main_page -} - -proc wapp-page-tests {} { - global G - wapp-trim { } - foreach t $G(test_array) { - set config [dict get $t config] - set target [dict get $t target] - - set class "testwait" - set seconds "" - - if {[info exists G(test.$config.log)]} { - if {[info exists G(test.$config.channel)]} { - set class "testrunning" - set seconds [expr [clock seconds] - $G(test.$config.start)] - } elseif {[info exists G(test.$config.done)]} { - if {$G(test.$config.nError)>0} { - set class "testfail" - } else { - set class "testdone" - } - set seconds [expr $G(test.$config.done) - $G(test.$config.start)] - } - set seconds [format_seconds $seconds] - } - - wapp-trim { - - -
    %html($config) - %html($target) - %html($seconds) - - } - if {[info exists G(test.$config.log)]} { - set log $G(test.$config.log) - set uri "log/$log" - wapp-trim { - %html($log) - } - } - if {[info exists G(test.$config.errmsg)] && $G(test.$config.errmsg)!=""} { - set errmsg $G(test.$config.errmsg) - wapp-trim { -
    %html($errmsg) - } - } - } - - wapp-trim {
    } - - if {[info exists G(result)]} { - set res $G(result) - wapp-trim { -
    %string($res)
    - } - } -} - -# URI: /control -# -# Whenever the form at the top of the application page is submitted, it -# is submitted here. -# -proc wapp-page-control {} { - global G - if {$::G(state)=="config"} { - set lControls [list platform test tcl jobs keep msvc debug] - set G(msvc) 0 - set G(keep) 0 - set G(debug) 0 - } else { - set lControls [list jobs] - } - foreach v $lControls { - if {[wapp-param-exists control_$v]} { - set G($v) [wapp-param control_$v] - } - } - - if {[wapp-param-exists control_run]} { - # This is a "run test" command. - wapptest_run - } - - if {[wapp-param-exists control_stop]} { - # A "STOP tests" command. - set G(state) "stopped" - set G(result) "Test halted by user" - foreach j $G(test_array) { - set name [dict get $j config] - if { [info exists G(test.$name.channel)] } { - close $G(test.$name.channel) - unset G(test.$name.channel) - slave_test_done $name 1 - } - } - wapptest_closelog - } - - if {[wapp-param-exists control_reset]} { - # A "reset app" command. - set G(state) "config" - wapptest_init - } - - if {$::G(state) == "running"} { - do_some_stuff - } - wapp-redirect / -} - -# URI: /style.css -# -# Return the stylesheet for the application main page. -# -proc wapp-page-style.css {} { - wapp-subst { - - /* The boxes with black borders use this class */ - .border { - border: 3px groove #444444; - padding: 1em; - margin-top: 1em; - margin-bottom: 1em; - } - - /* Float to the right (used for the Run/Stop/Reset button) */ - .right { float: right; } - - /* Style for the large red warning at the top of the page */ - .warning { - color: red; - font-weight: bold; - } - - /* Styles used by cells in the test table */ - .padleft { padding-left: 5ex; } - .nowrap { white-space: nowrap; } - - /* Styles for individual tests, depending on the outcome */ - .testwait { } - .testrunning { color: blue } - .testdone { color: green } - .testfail { color: red } - } -} - -# URI: /script/${state}.js -# -# The last part of this URI is always "config.js", "running.js" or -# "stopped.js", depending on the state of the application. It returns -# the javascript part of the front-end for the requested state to the -# browser. -# -proc wapp-page-script {} { - regexp {[^/]*$} [wapp-param REQUEST_URI] script - - set tcl $::G(tcl) - set keep $::G(keep) - set msvc $::G(msvc) - set debug $::G(debug) - - wapp-subst { - var lElem = \["control_platform", "control_test", "control_msvc", - "control_jobs", "control_debug" - \]; - lElem.forEach(function(e) { - var elem = document.getElementById(e); - elem.addEventListener("change", function() { control.submit() } ); - }) - - elem = document.getElementById("control_tcl"); - elem.value = "%string($tcl)" - - elem = document.getElementById("control_keep"); - elem.checked = %string($keep); - - elem = document.getElementById("control_msvc"); - elem.checked = %string($msvc); - - elem = document.getElementById("control_debug"); - elem.checked = %string($debug); - } - - if {$script != "config.js"} { - wapp-subst { - var lElem = \["control_platform", "control_test", - "control_tcl", "control_keep", "control_msvc", - "control_debug" - \]; - lElem.forEach(function(e) { - var elem = document.getElementById(e); - elem.disabled = true; - }) - } - } - - if {$script == "running.js"} { - wapp-subst { - function reload_tests() { - fetch('tests') - .then( data => data.text() ) - .then( data => { - document.getElementById("tests").innerHTML = data; - }) - .then( data => { - if( document.getElementById("result") ){ - document.location = document.location; - } else { - setTimeout(reload_tests, 1000) - } - }); - } - - setTimeout(reload_tests, 1000) - } - } -} - -# URI: /env -# -# This is for debugging only. Serves no other purpose. -# -proc wapp-page-env {} { - wapp-allow-xorigin-params - wapp-trim { -

    Wapp Environment

    \n
    -    
    %html([wapp-debug-env])
    - } -} - -# URI: /log/dirname/test.log -# -# This URI reads file "dirname/test.log" from disk, wraps it in a
    -# block, and returns it to the browser. Use for viewing log files.
    -#
    -proc wapp-page-log {} {
    -  set log [string range [wapp-param REQUEST_URI] 5 end]
    -  set fd [open $log]
    -  set data [read $fd]
    -  close $fd
    -  wapp-trim {
    -    
    -    %html($data)
    -    
    - } -} - -# Print out a usage message. Then do [exit 1]. -# -proc wapptest_usage {} { - puts stderr { -This Tcl script is used to test various configurations of SQLite. By -default it uses "wapp" to provide an interactive interface. Supported -command line options (all optional) are: - - --platform PLATFORM (which tests to run) - --config GLOB (only run configurations matching GLOB) - --smoketest (run "make smoketest" only) - --veryquick (run veryquick.test only) - --buildonly (build executables, do not run tests) - --jobs N (number of concurrent jobs) - --tcl DIR (where to find tclConfig.sh) - --deletefiles (delete extra files after each test) - --msvc (Use MS Visual C) - --debug (Also run [n]debugging versions of tests) - --noui (do not use wapp) - } - exit 1 -} - -# Sort command line arguments into two groups: those that belong to wapp, -# and those that belong to the application. -set WAPPARG(-server) 1 -set WAPPARG(-local) 1 -set WAPPARG(-scgi) 1 -set WAPPARG(-remote-scgi) 1 -set WAPPARG(-fromip) 1 -set WAPPARG(-nowait) 0 -set WAPPARG(-cgi) 0 -set lWappArg [list] -set lTestArg [list] -for {set i 0} {$i < [llength $argv]} {incr i} { - set arg [lindex $argv $i] - if {[string range $arg 0 1]=="--"} { - set arg [string range $arg 1 end] - } - if {[info exists WAPPARG($arg)]} { - lappend lWappArg $arg - if {$WAPPARG($arg)} { - incr i - lappend lWappArg [lindex $argv $i] - } - } else { - lappend lTestArg $arg - } -} - -wapptest_init -for {set i 0} {$i < [llength $lTestArg]} {incr i} { - set opt [lindex $lTestArg $i] - if {[string range $opt 0 1]=="--"} { - set opt [string range $opt 1 end] - } - switch -- $opt { - -platform { - if {$i==[llength $lTestArg]-1} { wapptest_usage } - incr i - set arg [lindex $lTestArg $i] - set lPlatform [releasetest_data platforms] - if {[lsearch $lPlatform $arg]<0} { - puts stderr "No such platform: $arg. Platforms are: $lPlatform" - exit -1 - } - set G(platform) $arg - } - - -smoketest { set G(test) Smoketest } - -veryquick { set G(test) Veryquick } - -buildonly { set G(test) Build-Only } - -jobs { - if {$i==[llength $lTestArg]-1} { wapptest_usage } - incr i - set G(jobs) [lindex $lTestArg $i] - } - - -tcl { - if {$i==[llength $lTestArg]-1} { wapptest_usage } - incr i - set G(tcl) [lindex $lTestArg $i] - } - - -deletefiles { - set G(keep) 0 - } - - -msvc { - set G(msvc) 1 - } - - -debug { - set G(debug) 1 - } - - -noui { - set G(noui) 1 - set G(stdout) 1 - } - - -config { - if {$i==[llength $lTestArg]-1} { wapptest_usage } - incr i - set G(cfgglob) [lindex $lTestArg $i] - } - - -stdout { - set G(stdout) 1 - } - - default { - puts stderr "Unrecognized option: [lindex $lTestArg $i]" - wapptest_usage - } - } -} - -if {$G(noui)==0} { - wapp-start $lWappArg -} else { - wapptest_run - do_some_stuff - vwait forever -} From 6234b334782b41db9aff8f8b688aba4e3f2b59bc Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 8 Nov 2023 15:56:41 +0000 Subject: [PATCH 147/347] Have the shell tool emit a warning if the user attempts to use ".scanstats vm" in a non-SQLITE_ENABLE_BYTECODE_VTAB build. FossilOrigin-Name: 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 8 +++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index f8b0d56fd2..ea62075a91 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sold\sfiles\srelated\sto\swapptest.tcl\sfrom\stest/\sdirectory. -D 2023-11-08T15:51:42.810 +C Have\sthe\sshell\stool\semit\sa\swarning\sif\sthe\suser\sattempts\sto\suse\s".scanstats\svm"\sin\sa\snon-SQLITE_ENABLE_BYTECODE_VTAB\sbuild. +D 2023-11-08T15:56:41.847 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -725,7 +725,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2 +F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2139,8 +2139,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 e5ecc404cae1ce8b639d0263fa07571c066f11bfc62f5ba331ad7ae138e78572 -R ce83d55831eeaab82518466b14d3660d +P dd3e7b5bcad122ac1e7e19ec547f4486ce90a6a2aa89a64e36bea13a216492fe +R 6732486a2aaa0526e06e22748aa04499 U dan -Z aaf04791c4ef545a121de15f3eab560c +Z 4a970381aa1757bde9016fd89b987339 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 198e99f2b4..8a3f265a0b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dd3e7b5bcad122ac1e7e19ec547f4486ce90a6a2aa89a64e36bea13a216492fe \ No newline at end of file +3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 3cfe1a94a4..8d201bbbda 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -10074,8 +10074,14 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_db_config( p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 ); -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS +#if !defined(SQLITE_ENABLE_STMT_SCANSTATUS) raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); +#elif !defined(SQLITE_ENABLE_BYTECODE_VTAB) + if( p->scanstatsOn==3 ){ + raw_printf(stderr, + "Warning: \".scanstats vm\" not available in this build.\n" + ); + } #endif }else{ raw_printf(stderr, "Usage: .scanstats on|off|est\n"); From b494366370f5f9698e574150c5a4309d7c3dc78b Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Nov 2023 16:37:12 +0000 Subject: [PATCH 148/347] More precise characterization of JSON functions. Indicate when functions might return JSON (subtype 'J') and when they make use of the function argument cache. FossilOrigin-Name: b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/json.c | 43 +++++++++++++++++++++++-------------------- src/sqliteInt.h | 6 +++--- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/manifest b/manifest index ea62075a91..d580ab0fb8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Have\sthe\sshell\stool\semit\sa\swarning\sif\sthe\suser\sattempts\sto\suse\s".scanstats\svm"\sin\sa\snon-SQLITE_ENABLE_BYTECODE_VTAB\sbuild. -D 2023-11-08T15:56:41.847 +C More\sprecise\scharacterization\sof\sJSON\sfunctions.\s\sIndicate\swhen\sfunctions\smight\nreturn\sJSON\s(subtype\s'J')\sand\swhen\sthey\smake\suse\sof\sthe\sfunction\sargument\scache. +D 2023-11-08T16:37:12.211 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -684,7 +684,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d69c6e28ff7b602877bda68cd20583b8487c059759aa4d154dd21b3fd99c6238 +F src/json.c 82d1237899d5d06dc9722f9a0d9305711ff563c68221b479f0788b33cb114e16 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c @@ -729,7 +729,7 @@ F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1 F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h b9f6cfac999b60def4d3523897dc1f5453ab9c24195e462fd346476541cf659a +F src/sqliteInt.h 90bbf1ba8f47753c84f15cdb0e5dc6267d3f391cc66b9fbd6f441881d1bf44a1 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2139,8 +2139,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 dd3e7b5bcad122ac1e7e19ec547f4486ce90a6a2aa89a64e36bea13a216492fe -R 6732486a2aaa0526e06e22748aa04499 -U dan -Z 4a970381aa1757bde9016fd89b987339 +P 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a +R 2a4b96e0d45f1ec3b63228c066f1d6da +U drh +Z 7680e031e38ed1a9d942d233bb859fec # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8a3f265a0b..8ca67c6ccf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a \ No newline at end of file +b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f \ No newline at end of file diff --git a/src/json.c b/src/json.c index c2129a026e..407ca2d0eb 100644 --- a/src/json.c +++ b/src/json.c @@ -3804,27 +3804,30 @@ static sqlite3_module jsonTreeModule = { void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - JFUNCTION(json, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 0, jsonValidFunc), + /* Uses cache ------, ,---- Might return JSON (subtype J) */ + /* Num args ________ | | ___ Flags */ + /* \ | | / */ + JFUNCTION(json, 1, 1, 1, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, 1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 1, 0, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 1, 0, 0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1, 1, 0, 0, jsonErrorFunc), + JFUNCTION(json_extract, -1, 1, 1, 0, jsonExtractFunc), + JFUNCTION(->, 2, 1, 1, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, 1, 0, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 1, 1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, 1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 1, 1, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 1, 1, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 1, 1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, 1, 1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 1, 0, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 1, 0, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 1, 0, 0, jsonValidFunc), #if SQLITE_DEBUG - JFUNCTION(json_parse, 1, 0, jsonParseFunc), - JFUNCTION(json_test1, 1, 0, jsonTest1Func), + JFUNCTION(json_parse, 1, 1, 0, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 1, 0, 0, jsonTest1Func), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1a22500539..20e23ed2bc 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2113,9 +2113,9 @@ struct FuncDestructor { #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } -#define JFUNCTION(zName, nArg, iArg, xFunc) \ - {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\ - SQLITE_FUNC_CONSTANT|SQLITE_UTF8|SQLITE_FUNC_RUNONLY, \ +#define JFUNCTION(zName, nArg, bUseCache, bSubtype, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ + SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|((bSubtype)*SQLITE_SUBTYPE), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ From 2cbe14098b156838153f194df1ea41d9b390935b Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Nov 2023 18:08:07 +0000 Subject: [PATCH 149/347] Do not cover expressions using an indexed expression if the indexed expression is a function that might set a subtype. FossilOrigin-Name: e908b26a990929996b3c16f0429e8313cd8fcefe7c883c77f66ea69f4059d6e2 --- manifest | 17 ++++++++++------- manifest.uuid | 2 +- src/where.c | 11 +++++++++++ test/indexexpr1.test | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index d580ab0fb8..6996862b81 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\sprecise\scharacterization\sof\sJSON\sfunctions.\s\sIndicate\swhen\sfunctions\smight\nreturn\sJSON\s(subtype\s'J')\sand\swhen\sthey\smake\suse\sof\sthe\sfunction\sargument\scache. -D 2023-11-08T16:37:12.211 +C Do\snot\scover\sexpressions\susing\san\sindexed\sexpression\sif\sthe\sindexed\sexpression\nis\sa\sfunction\sthat\smight\sset\sa\ssubtype. +D 2023-11-08T18:08:07.513 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -809,7 +809,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c bba7db5dae3ffe2c6b9c173fc10be4b570b125e985cb5b95a6c22716213adde4 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c 313ce81270d2a414672370e1ee74e65949ad620519193d4cac2986d073cbc8a0 +F src/where.c 431309d7920383671b05d43454351230eb9c01d963a6f7d4a516334cbbcce1d4 F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 @@ -1269,7 +1269,7 @@ F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a91 F test/index9.test 2ac891806a4136ef3e91280477e23114e67575207dc331e6797fa0ed9379f997 F test/indexA.test 11d84f6995e6e5b9d8315953fb1b6d29772ee7c7803ee9112715e7e4dd3e4974 F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0 -F test/indexexpr1.test 62558b1cfd7ccbe7bc015849cc6d1a13ef124e80cbd5b3a98dc66c3c9cce0cf4 +F test/indexexpr1.test 833f511213a5e26549186813f0566bd72f978177a7e6e98a2d2dd695de3c670d F test/indexexpr2.test 1c382e81ef996d8ae8b834a74f2a9013dddf59214c32201d7c8a656d739f999a F test/indexfault.test 98d78a8ff1f5335628b62f886a1cb7c7dac1ef6d48fa39c51ec871c87dce9811 F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 @@ -2139,8 +2139,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a -R 2a4b96e0d45f1ec3b63228c066f1d6da +P b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f +R 3699dfa06af6d8dfe43187d2c78cc578 +T *branch * idx-expr-fix +T *sym-idx-expr-fix * +T -sym-trunk * U drh -Z 7680e031e38ed1a9d942d233bb859fec +Z 99b5085d0aa63df2f84ecba864ce3e16 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ca67c6ccf..303299d818 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f \ No newline at end of file +e908b26a990929996b3c16f0429e8313cd8fcefe7c883c77f66ea69f4059d6e2 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 05ae24f7bc..cfee45f879 100644 --- a/src/where.c +++ b/src/where.c @@ -5810,6 +5810,17 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( continue; } if( sqlite3ExprIsConstant(pExpr) ) continue; + if( pExpr->op==TK_FUNCTION ){ + int n; + FuncDef *pDef; + sqlite3 *db = pParse->db; + assert( ExprUseXList(pExpr) ); + n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_SUBTYPE)!=0 ){ + continue; + } + } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; p->pIENext = pParse->pIdxEpr; diff --git a/test/indexexpr1.test b/test/indexexpr1.test index 51ef73bbf5..0316ee9d42 100644 --- a/test/indexexpr1.test +++ b/test/indexexpr1.test @@ -616,4 +616,18 @@ do_execsql_test indexexpr1-2200 { ) v ON v.type = 0 AND v.tag = u.tag; } {7 100 8 101} +# 2023-11-08 Forum post https://sqlite.org/forum/forumpost/68d284c86b082c3e +# +# Functions that return subtypes and that are indexed cannot be used to +# cover function calls from the main table, since the indexed value does +# not know the subtype. +# +reset_db +do_execsql_test indexexpr1-2300 { + CREATE TABLE t1(x INT, y TEXT); + INSERT INTO t1(x,y) VALUES(1,'{b:5}'); + CREATE INDEX t1j ON t1(json(y)); + SELECT json_insert('{}', '$.a', json(y)) FROM t1; +} {{{"a":{"b":5}}}} + finish_test From 243f2ec6a15201fdcabc791fd7618d85332fe83f Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 8 Nov 2023 21:38:30 +0000 Subject: [PATCH 150/347] Make a distinction between functions that consume subtypes and functions that generate subtypes. FossilOrigin-Name: 48a92e3ad855227188a4c5afe4abbb7171761cf6fc930660084d9abeecfd91d9 --- manifest | 21 +++++++++++-------- manifest.uuid | 2 +- src/json.c | 56 +++++++++++++++++++++++++++---------------------- src/sqlite.h.in | 33 ++++++++++++++++++++++------- src/sqliteInt.h | 10 +++++---- src/window.c | 2 +- 6 files changed, 76 insertions(+), 48 deletions(-) diff --git a/manifest b/manifest index d580ab0fb8..bf5ccb0dc0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\sprecise\scharacterization\sof\sJSON\sfunctions.\s\sIndicate\swhen\sfunctions\smight\nreturn\sJSON\s(subtype\s'J')\sand\swhen\sthey\smake\suse\sof\sthe\sfunction\sargument\scache. -D 2023-11-08T16:37:12.211 +C Make\sa\sdistinction\sbetween\sfunctions\sthat\sconsume\ssubtypes\sand\sfunctions\nthat\sgenerate\ssubtypes. +D 2023-11-08T21:38:30.966 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -684,7 +684,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 82d1237899d5d06dc9722f9a0d9305711ff563c68221b479f0788b33cb114e16 +F src/json.c 7fe43f314870b0c351b3b62fadd75ca74a4bd848074aa87989256667b16b4ba8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c @@ -726,10 +726,10 @@ F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f -F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd +F src/sqlite.h.in bf3c06da94f5857ab49f9573d2c5f095210526eca948e51c931db63c431d5f8d F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 90bbf1ba8f47753c84f15cdb0e5dc6267d3f391cc66b9fbd6f441881d1bf44a1 +F src/sqliteInt.h b2a8e6ec78aaed2443966d374b1e8fd623dc6c1a149736363d9cbe2af824950e F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -813,7 +813,7 @@ F src/where.c 313ce81270d2a414672370e1ee74e65949ad620519193d4cac2986d073cbc8a0 F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 -F src/window.c ad21e2b73ec75acc79dde2576c573f54a338b0c49e9de847ce984f9b9595b5e2 +F src/window.c 6f46006904c53783c46aaae1a5ec30c42b632e624c6c30f2b6688e8703aa404d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9 @@ -2139,8 +2139,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a -R 2a4b96e0d45f1ec3b63228c066f1d6da +P b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f +R 18013744e98b85d7b88e0e681a0596bb +T *branch * func-rw-subtype +T *sym-func-rw-subtype * +T -sym-trunk * U drh -Z 7680e031e38ed1a9d942d233bb859fec +Z 7575eb9b47eda5f1a399d46eb9d53d9f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ca67c6ccf..0c5a28466d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f \ No newline at end of file +48a92e3ad855227188a4c5afe4abbb7171761cf6fc930660084d9abeecfd91d9 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 407ca2d0eb..ab6939d029 100644 --- a/src/json.c +++ b/src/json.c @@ -3804,37 +3804,43 @@ static sqlite3_module jsonTreeModule = { void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - /* Uses cache ------, ,---- Might return JSON (subtype J) */ - /* Num args ________ | | ___ Flags */ - /* \ | | / */ - JFUNCTION(json, 1, 1, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, 1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 1, 0, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 1, 0, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 1, 0, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 1, 1, 0, jsonExtractFunc), - JFUNCTION(->, 2, 1, 1, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, 1, 0, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 1, 1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, 1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 1, 1, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 1, 1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 1, 1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, 1, 1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 1, 0, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 1, 0, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 1, 0, 0, jsonValidFunc), + /* calls sqlite3_result_subtype() */ + /* | */ + /* Uses cache ______ | __ calls sqlite3_value_subtype() */ + /* | | | */ + /* Num args _________ | | | ___ Flags */ + /* | | | | | */ + /* | | | | | */ + JFUNCTION(json, 1, 1, 1, 0, 0, jsonRemoveFunc), + JFUNCTION(json_array, -1, 0, 1, 1, 0, jsonArrayFunc), + JFUNCTION(json_array_length, 1, 1, 0, 0, 0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2, 1, 0, 0, 0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1, 1, 0, 0, 0, jsonErrorFunc), + JFUNCTION(json_extract, -1, 1, 1, 0, 0, jsonExtractFunc), + JFUNCTION(->, 2, 1, 1, 0, JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2, 1, 0, 0, JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1, 1, 1, 1, 0, jsonSetFunc), + JFUNCTION(json_object, -1, 0, 1, 1, 0, jsonObjectFunc), + JFUNCTION(json_patch, 2, 1, 1, 0, 0, jsonPatchFunc), + JFUNCTION(json_quote, 1, 0, 1, 1, 0, jsonQuoteFunc), + JFUNCTION(json_remove, -1, 1, 1, 0, 0, jsonRemoveFunc), + JFUNCTION(json_replace, -1, 1, 1, 1, 0, jsonReplaceFunc), + JFUNCTION(json_set, -1, 1, 1, 1, JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1, 1, 0, 0, 0, jsonTypeFunc), + JFUNCTION(json_type, 2, 1, 0, 0, 0, jsonTypeFunc), + JFUNCTION(json_valid, 1, 1, 0, 0, 0, jsonValidFunc), #if SQLITE_DEBUG - JFUNCTION(json_parse, 1, 1, 0, 0, jsonParseFunc), - JFUNCTION(json_test1, 1, 1, 0, 0, jsonTest1Func), + JFUNCTION(json_parse, 1, 1, 1, 0, 0, jsonParseFunc), + JFUNCTION(json_test1, 1, 1, 0, 1, 0, jsonTest1Func), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, - SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + SQLITE_VALUE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_DETERMINISTIC), WAGGREGATE(json_group_object, 2, 0, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, - SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC) + SQLITE_VALUE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_DETERMINISTIC) }; sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); #endif diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 2317d98f79..2853289fcd 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5572,21 +5572,38 @@ int sqlite3_create_window_function( ** security-adverse side-effects and information-leaks. ** ** -** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    -** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call +** [[SQLITE_VALUE_SUBTYPE]]
    SQLITE_VALUE_SUBTYPE
    +** The SQLITE_VALUE_SUBTYPE flag indicates to SQLite that a function may call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. -** Specifying this flag makes no difference for scalar or aggregate user -** functions. However, if it is not specified for a user-defined window -** function, then any sub-types belonging to arguments passed to the window -** function may be discarded before the window function is called (i.e. -** sqlite3_value_subtype() will always return 0). +** Every function that invokes [sqlite3_value_subtype()] should have this +** property. If it does not, then the query planner might generate +** incorrect code for queries that use that function. This property +** used to be called [SQLITE_SUBTYPE]. Its name was changed to disinguish +** it from [SQLITE_RESULT_SUBTYPE]. The old name is also retained for +** backwards compatibility. +** +** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    +** The SQLITE_SUBTYPE flag is an alias for [SQLITE_VALUE_SUBTYPE] used for +** backwards compatibility. The newer [SQLITE_VALUE_SUBTYPE] +** symbol is preferred. +** +** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    +** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call +** [sqlite3_result_subtype()] to cause a sub-type to be associated with its +** result. +** Every function that invokes [sqlite3_result_subtype()] should have this +** property. If it does not, then the call to [sqlite3_result_subtype()] +** might become a no-op if the function is used as term in an +** [expression index]. **
    ** */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 -#define SQLITE_SUBTYPE 0x000100000 +#define SQLITE_SUBTYPE 0x000100000 /* Deprecated name */ +#define SQLITE_VALUE_SUBTYPE 0x000100000 /* Use instead of SQLITE_SUBTYPE */ #define SQLITE_INNOCUOUS 0x000200000 +#define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 20e23ed2bc..1a1e1268a4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2018,10 +2018,11 @@ struct FuncDestructor { #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ -#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ +/* SQLITE_VALUE_SUBTYPE 0x00100000 // Consumer of subtypes */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ #define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ +/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */ #define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ @@ -2113,9 +2114,10 @@ struct FuncDestructor { #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } -#define JFUNCTION(zName, nArg, bUseCache, bSubtype, iArg, xFunc) \ +#define JFUNCTION(zName, nArg, bUseCache, bRS, bWS, iArg, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ - SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|((bSubtype)*SQLITE_SUBTYPE), \ + SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ + ((bRS)*SQLITE_VALUE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ @@ -4452,7 +4454,7 @@ struct Window { int regStartRowid; int regEndRowid; u8 bExprArgs; /* Defer evaluation of window function arguments - ** due to the SQLITE_SUBTYPE flag */ + ** due to the SQLITE_VALUE_SUBTYPE flag */ }; #ifndef SQLITE_OMIT_WINDOWFUNC diff --git a/src/window.c b/src/window.c index 2c449592d7..edbfb6c7e0 100644 --- a/src/window.c +++ b/src/window.c @@ -1038,7 +1038,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ assert( ExprUseXList(pWin->pOwner) ); assert( pWin->pWFunc!=0 ); pArgs = pWin->pOwner->x.pList; - if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ + if( pWin->pWFunc->funcFlags & SQLITE_VALUE_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pWin->bExprArgs = 1; From c060b5f3a880e487e4ee6008317bef593fb6e5cf Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 01:54:26 +0000 Subject: [PATCH 151/347] JSON5 bug fix: Escape double-quotes that occur inside of single-quoted strings. [forum:/forumpost/ddcad3e884|Forum post ddcad3e884]. FossilOrigin-Name: 1c98d46d60ef1494bd8b7561c7d0cd5aafc178201a6f1f0da25dea6140b91cd0 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 8 +++++++- test/json501.test | 5 +++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index d580ab0fb8..6414fd487b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\sprecise\scharacterization\sof\sJSON\sfunctions.\s\sIndicate\swhen\sfunctions\smight\nreturn\sJSON\s(subtype\s'J')\sand\swhen\sthey\smake\suse\sof\sthe\sfunction\sargument\scache. -D 2023-11-08T16:37:12.211 +C JSON5\sbug\sfix:\s\sEscape\sdouble-quotes\sthat\soccur\sinside\sof\ssingle-quoted\sstrings.\n[forum:/forumpost/ddcad3e884|Forum\spost\sddcad3e884]. +D 2023-11-09T01:54:26.529 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -684,7 +684,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 82d1237899d5d06dc9722f9a0d9305711ff563c68221b479f0788b33cb114e16 +F src/json.c 78559ef0eb3929c693ee2cd69fe5187fba8b123edb97cbd1c6a8dcd0589310ac F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c @@ -1325,7 +1325,7 @@ F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d -F test/json501.test f71710f60fa45b19dc336fbaac9e8362f70f80cf81badefdb845ed3f7c7c2ccc +F test/json501.test c419deb835b70c1a2c8532936927bcc1146730328edd2052276715bfd209724d F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c 6e0228409ea7ca0497dad503fbd109badb5e59545d131014b6aaac68b56f484a @@ -2139,8 +2139,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 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a -R 2a4b96e0d45f1ec3b63228c066f1d6da +P b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f +R bc8e127289d5f1279e134e74ad50d503 U drh -Z 7680e031e38ed1a9d942d233bb859fec +Z 7d193c1105cecb8c78a50905b1669eb7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ca67c6ccf..17aa7a9f38 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f \ No newline at end of file +1c98d46d60ef1494bd8b7561c7d0cd5aafc178201a6f1f0da25dea6140b91cd0 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 407ca2d0eb..91b96df376 100644 --- a/src/json.c +++ b/src/json.c @@ -432,13 +432,19 @@ static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ zIn++; N -= 2; while( N>0 ){ - for(i=0; i0 ){ jsonAppendRawNZ(p, zIn, i); zIn += i; N -= i; if( N==0 ) break; } + if( zIn[0]=='"' ){ + jsonAppendRawNZ(p, "\\\"", 2); + zIn++; + N--; + continue; + } assert( zIn[0]=='\\' ); switch( (u8)zIn[1] ){ case '\'': diff --git a/test/json501.test b/test/json501.test index a37326973a..3318eea7f2 100644 --- a/test/json501.test +++ b/test/json501.test @@ -300,5 +300,10 @@ do_execsql_test 12.4 { || ' "xyz"}')->>'a'; } xyz +# 2023-11-08 forum/forumpost/ddcad3e884 +# +do_execsql_test 13.1 { + SELECT json('{x:''a "b" c''}'); +} {{{"x":"a \"b\" c"}}} finish_test From ec427813ac5ac9857fbfa0073875cc940dc066d1 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 9 Nov 2023 12:01:02 +0000 Subject: [PATCH 152/347] Two more JNI build fixes for Windows/MinGW, reported in [forum:4f949edc312d2a75|forum post 4f949edc312d2a75]. FossilOrigin-Name: a3f9c39086e582e16ca15647961956b3c28d038655d3b43d4b94bd306fbec1a4 --- ext/jni/GNUmakefile | 2 ++ .../src/org/sqlite/jni/capi/sqlite3_blob.java | 2 +- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 61c816194f..dc93f49085 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -35,6 +35,8 @@ $(dir.bld.c): javac.flags ?= -Xlint:unchecked -Xlint:deprecation java.flags ?= +javac.flags += -encoding utf8 +# -------------^^^^^^^^^^^^^^ required for Windows builds jnicheck ?= 1 ifeq (1,$(jnicheck)) java.flags += -Xcheck:jni diff --git a/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java b/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java index 1b96c18b06..4bca3363ff 100644 --- a/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java +++ b/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java @@ -25,7 +25,7 @@ public final class sqlite3_blob extends NativePointerHolder private sqlite3_blob(){} @Override public void close(){ - CApi.sqlite3_blob_close(this.clearNativePointer()); + CApi.sqlite3_blob_close(this); } } diff --git a/manifest b/manifest index 6414fd487b..f6390adef5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JSON5\sbug\sfix:\s\sEscape\sdouble-quotes\sthat\soccur\sinside\sof\ssingle-quoted\sstrings.\n[forum:/forumpost/ddcad3e884|Forum\spost\sddcad3e884]. -D 2023-11-09T01:54:26.529 +C Two\smore\sJNI\sbuild\sfixes\sfor\sWindows/MinGW,\sreported\sin\s[forum:4f949edc312d2a75|forum\spost\s4f949edc312d2a75]. +D 2023-11-09T12:01:02.729 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -238,7 +238,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 -F ext/jni/GNUmakefile df91212d772011e3d39712a0e38586856c42528b6ee3d507a5bb3b3248c0ecbc +F ext/jni/GNUmakefile d984ea9c4e3536188f9d663120db8fb97b83329f4b8864bd88f75ebe581b8b8b F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 6b95974189d7cc394afbe15507050f1d174170a65be5a4dad201ab11f0a9777a @@ -278,7 +278,7 @@ F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba90943 F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e F ext/jni/src/org/sqlite/jni/capi/sqlite3.java c6a5c555d163d76663534f2b2cce7cab15325b9852d0f58c6688a85e73ae52f0 F ext/jni/src/org/sqlite/jni/capi/sqlite3_backup.java 6742b431cd4d77e8000c1f92ec66265a58414c86bf3b0b5fbcb1164e08477227 -F ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java f204ab6ab1263e119fe43730141a00662d80972129a5351dfb11aae5d282df36 +F ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java e8c799ed89a7725ba779ff7d4b1d941c5aaae1b1c3888a68a10ee9cca5c71e7d F ext/jni/src/org/sqlite/jni/capi/sqlite3_context.java f0ef982009c335c4393ffcb68051809ca1711e4f47bcb8d1d46952f22c01bc22 F ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java 293b5fa7d5b5724c87de544654aca1103d76f3092bc2c8f4360102a65ba25dff F ext/jni/src/org/sqlite/jni/capi/sqlite3_value.java e1d62a257c13504b46d39d5c21c49cf157ad73fda00cc5f34c931aa008c37049 @@ -2139,8 +2139,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 b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f -R bc8e127289d5f1279e134e74ad50d503 -U drh -Z 7d193c1105cecb8c78a50905b1669eb7 +P 1c98d46d60ef1494bd8b7561c7d0cd5aafc178201a6f1f0da25dea6140b91cd0 +R 17c07891ab0f75a8281c2b918fd5c523 +U stephan +Z 19240fed0f90b5e1367576a460d156b0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 17aa7a9f38..300175f17c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1c98d46d60ef1494bd8b7561c7d0cd5aafc178201a6f1f0da25dea6140b91cd0 \ No newline at end of file +a3f9c39086e582e16ca15647961956b3c28d038655d3b43d4b94bd306fbec1a4 \ No newline at end of file From 194b8d514bc8ba594e88c93cfaba66ff24d00ae8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 12:08:16 +0000 Subject: [PATCH 153/347] Omit the new SQLITE_VALUE_SUBTYPE name. Stay with legacy SQLTIE_SUBTYPE. Add extra documentation to sqlite3_value_subtype() and sqlite3_result_subtype() indicating that the SQLITE_SUBTYPE and SQLITE_RESULT_SUBTYPE properties are required on functions that use those interfaces. FossilOrigin-Name: 563ad3be60d22c45f1c5b9a3e67738593f8b38f137147c56514166fbabf95365 --- manifest | 21 +++++++++------------ manifest.uuid | 2 +- src/json.c | 4 ++-- src/sqlite.h.in | 37 +++++++++++++++++++++++-------------- src/sqliteInt.h | 6 +++--- src/window.c | 2 +- 6 files changed, 39 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index bf5ccb0dc0..f9b734176e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sa\sdistinction\sbetween\sfunctions\sthat\sconsume\ssubtypes\sand\sfunctions\nthat\sgenerate\ssubtypes. -D 2023-11-08T21:38:30.966 +C Omit\sthe\snew\sSQLITE_VALUE_SUBTYPE\sname.\s\sStay\swith\slegacy\sSQLTIE_SUBTYPE.\nAdd\sextra\sdocumentation\sto\ssqlite3_value_subtype()\sand\ssqlite3_result_subtype()\nindicating\sthat\sthe\sSQLITE_SUBTYPE\sand\sSQLITE_RESULT_SUBTYPE\sproperties\sare\nrequired\son\sfunctions\sthat\suse\sthose\sinterfaces. +D 2023-11-09T12:08:16.633 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -684,7 +684,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7fe43f314870b0c351b3b62fadd75ca74a4bd848074aa87989256667b16b4ba8 +F src/json.c bf117324d2efff47b392c29f7d3e611a7508933b91491c7ad349101e6d5f5e05 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c @@ -726,10 +726,10 @@ F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f -F src/sqlite.h.in bf3c06da94f5857ab49f9573d2c5f095210526eca948e51c931db63c431d5f8d +F src/sqlite.h.in 8da45c84e79cde72c73fcb4260addcc7c00fac3bc6f5594b81a3792c1b196264 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h b2a8e6ec78aaed2443966d374b1e8fd623dc6c1a149736363d9cbe2af824950e +F src/sqliteInt.h fe1bc1d3ee5302bb8e6b7284cd900bed2d4dd402d08d2893c055afe7297249a8 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -813,7 +813,7 @@ F src/where.c 313ce81270d2a414672370e1ee74e65949ad620519193d4cac2986d073cbc8a0 F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 -F src/window.c 6f46006904c53783c46aaae1a5ec30c42b632e624c6c30f2b6688e8703aa404d +F src/window.c 5b1387d59df30d481ed14cceef5f4d1dab1f8752aa106ba72c8b62777bd139d2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9 @@ -2139,11 +2139,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 b2b62546c4a5e9dccb8aa0cb8eda228d662c69159e320b01a377317bc909e89f -R 18013744e98b85d7b88e0e681a0596bb -T *branch * func-rw-subtype -T *sym-func-rw-subtype * -T -sym-trunk * +P 48a92e3ad855227188a4c5afe4abbb7171761cf6fc930660084d9abeecfd91d9 +R 1125c8580bcae0bd5fcad566907cd268 U drh -Z 7575eb9b47eda5f1a399d46eb9d53d9f +Z b355d7c6c1259a41f882f80ab53c48da # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0c5a28466d..74e16885c4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -48a92e3ad855227188a4c5afe4abbb7171761cf6fc930660084d9abeecfd91d9 \ No newline at end of file +563ad3be60d22c45f1c5b9a3e67738593f8b38f137147c56514166fbabf95365 \ No newline at end of file diff --git a/src/json.c b/src/json.c index ab6939d029..132a274cd3 100644 --- a/src/json.c +++ b/src/json.c @@ -3835,11 +3835,11 @@ void sqlite3RegisterJsonFunctions(void){ #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, - SQLITE_VALUE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| SQLITE_DETERMINISTIC), WAGGREGATE(json_group_object, 2, 0, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, - SQLITE_VALUE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| SQLITE_DETERMINISTIC) }; sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 2853289fcd..09e6f47653 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5572,20 +5572,12 @@ int sqlite3_create_window_function( ** security-adverse side-effects and information-leaks. ** ** -** [[SQLITE_VALUE_SUBTYPE]]
    SQLITE_VALUE_SUBTYPE
    -** The SQLITE_VALUE_SUBTYPE flag indicates to SQLite that a function may call +** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    +** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. ** Every function that invokes [sqlite3_value_subtype()] should have this ** property. If it does not, then the query planner might generate -** incorrect code for queries that use that function. This property -** used to be called [SQLITE_SUBTYPE]. Its name was changed to disinguish -** it from [SQLITE_RESULT_SUBTYPE]. The old name is also retained for -** backwards compatibility. -** -** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    -** The SQLITE_SUBTYPE flag is an alias for [SQLITE_VALUE_SUBTYPE] used for -** backwards compatibility. The newer [SQLITE_VALUE_SUBTYPE] -** symbol is preferred. +** incorrect code for queries that use that function. ** ** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call @@ -5594,14 +5586,16 @@ int sqlite3_create_window_function( ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an -** [expression index]. +** [expression index]. On the other hand, SQL functions that never invoke +** [sqlite3_result_subtype()] should avoid setting this property, as the +** purpose of this property is to disable certain optimizations that are +** incompatible with subtypes. **
    ** */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 -#define SQLITE_SUBTYPE 0x000100000 /* Deprecated name */ -#define SQLITE_VALUE_SUBTYPE 0x000100000 /* Use instead of SQLITE_SUBTYPE */ +#define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 @@ -5800,6 +5794,13 @@ int sqlite3_value_encoding(sqlite3_value*); ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. +** +** Every [application-defined SQL function] that invoke this interface +** must include the [SQLITE_SUBTYPE] property in the text +** encoding argument when the function is [sqlite3_create_function|registered]. +** The sqlite3_value_subtype() can return an incorrect answer if it +** is invoked from within an application-defined SQL function that does +** not have the [SQLITE_SUBTYPE] property. */ unsigned int sqlite3_value_subtype(sqlite3_value*); @@ -6219,6 +6220,14 @@ int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** higher order bits are discarded. ** The number of subtype bytes preserved by SQLite might increase ** in future releases of SQLite. +** +** Every [application-defined SQL function] that invokes this interface +** must include the [SQLITE_RESULT_SUBTYPE] property in its +** text encoding argument when the SQL function is +** [sqlite3_create_function|registered]. If the sqlite3_result_subtype() +** interface is invoked within an SQL function that does not have the +** SQLITE_RESULT_SUBTYPE property, then sqlite3_result_subtype() +** might fail to set the result subtype. */ void sqlite3_result_subtype(sqlite3_context*,unsigned int); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1a1e1268a4..35c4d403e6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2018,7 +2018,7 @@ struct FuncDestructor { #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ -/* SQLITE_VALUE_SUBTYPE 0x00100000 // Consumer of subtypes */ +/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ #define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ @@ -2117,7 +2117,7 @@ struct FuncDestructor { #define JFUNCTION(zName, nArg, bUseCache, bRS, bWS, iArg, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ - ((bRS)*SQLITE_VALUE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ + ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ @@ -4454,7 +4454,7 @@ struct Window { int regStartRowid; int regEndRowid; u8 bExprArgs; /* Defer evaluation of window function arguments - ** due to the SQLITE_VALUE_SUBTYPE flag */ + ** due to the SQLITE_SUBTYPE flag */ }; #ifndef SQLITE_OMIT_WINDOWFUNC diff --git a/src/window.c b/src/window.c index edbfb6c7e0..62df349fb3 100644 --- a/src/window.c +++ b/src/window.c @@ -1038,7 +1038,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ assert( ExprUseXList(pWin->pOwner) ); assert( pWin->pWFunc!=0 ); pArgs = pWin->pOwner->x.pList; - if( pWin->pWFunc->funcFlags & SQLITE_VALUE_SUBTYPE ){ + if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pWin->bExprArgs = 1; From b9050dcec70bb0767b7ac8419700b16365c338c5 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 9 Nov 2023 12:48:54 +0000 Subject: [PATCH 154/347] Add some notes about the JNI pointer-passing approach and convert a couple of potential NullPointerExceptions into appropriate C result codes. Clarify that invocation of undefined behaviour from the Java API does not (due to the addition of defensive code) mean the same thing as it does in C (e.g. no NULL pointer dereferences). FossilOrigin-Name: 19c4778f45261006368b2d9460350fed1e55fed314c8b3e1af34cd8c3c73b7d8 --- ext/jni/src/c/sqlite3-jni.c | 15 +++++++-- ext/jni/src/c/sqlite3-jni.h | 4 +-- .../org/sqlite/jni/annotation/NotNull.java | 31 +++++++++++++------ ext/jni/src/org/sqlite/jni/capi/CApi.java | 10 ++++-- manifest | 18 +++++------ manifest.uuid | 2 +- 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 9384fb9d21..fafb2ab5fd 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -185,6 +185,8 @@ ** ** This use of intptr_t is the _only_ reason we require ** which, in turn, requires building with -std=c99 (or later). +** +** See also: the notes for LongPtrGet_T. */ #define S3JniCast_L2P(JLongAsPtr) (void*)((intptr_t)(JLongAsPtr)) #define S3JniCast_P2L(PTR) (jlong)((intptr_t)(PTR)) @@ -1493,6 +1495,15 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, ** the C side, because it's reportedly significantly faster. The ** intptr_t part here is necessary for compatibility with (at least) ** ARM32. +** +** 2023-11-09: testing has not revealed any measurable performance +** difference between the approach of passing type T to C compared to +** passing pointer-to-T to C, and adding support for the latter +** everywhere requires sigificantly more code. As of this writing, the +** older/simpler approach is being applied except for (A) where the +** newer approach has already been applied and (B) hot-spot APIs where +** a difference of microseconds (i.e. below our testing measurement +** threshold) might add up. */ #define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) #define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,JLongAsPtr) @@ -4674,9 +4685,9 @@ S3JniApi(sqlite3_sql(),jstring,1sql)( } S3JniApi(sqlite3_step(),jint,1step)( - JniArgsEnvClass,jobject jStmt + JniArgsEnvClass, jlong jpStmt ){ - sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt); + sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index f160b6453f..e655a71f63 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1872,10 +1872,10 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1status64 /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_step - * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;)I + * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1step - (JNIEnv *, jclass, jobject); + (JNIEnv *, jclass, jlong); /* * Class: org_sqlite_jni_capi_CApi diff --git a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java index 3b4c1c7af1..57639fa0d9 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java +++ b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java @@ -18,12 +18,12 @@ package org.sqlite.jni.annotation; null or point to closed/finalized C-side resources.

    In the case of Java types which map directly to C struct types - (e.g. {@link org.sqlite.jni.sqlite3}, {@link - org.sqlite.jni.sqlite3_stmt}, and {@link - org.sqlite.jni.sqlite3_context}), a closed/finalized resource is - also considered to be null for purposes this annotation because the - C-side effect of passing such a handle is the same as if null is - passed.

    + (e.g. {@link org.sqlite.jni.capi.sqlite3}, {@link + org.sqlite.jni.capi.sqlite3_stmt}, and {@link + org.sqlite.jni.capi.sqlite3_context}), a closed/finalized resource + is also considered to be null for purposes this annotation because + the C-side effect of passing such a handle is the same as if null + is passed.

    When used in the context of Java interfaces which are called from the C APIs, this annotation communicates that the C API will @@ -31,12 +31,23 @@ package org.sqlite.jni.annotation;

    Passing a null, for this annotation's definition of null, for any parameter marked with this annoation specifically invokes - undefined behavior.

    + undefined behavior (see below).

    Passing 0 (i.e. C NULL) or a negative value for any long-type parameter marked with this annoation specifically invokes undefined - behavior. Such values are treated as C pointers in the JNI - layer.

    + behavior (see below). Such values are treated as C pointers in the + JNI layer.

    + +

    Undefined behaviour: the JNI build uses the {@code + SQLITE_ENABLE_API_ARMOR} build flag, meaning that the C code + invoked with invalid NULL pointers and the like will not invoke + undefined behavior in the conventional C sense, but may, for + example, return result codes which are not documented for the + affected APIs or may otherwise behave unpredictably. In no known + cases will such arguments result in C-level code dereferencing a + NULL pointer or accessing out-of-bounds (or otherwise invalid) + memory. In other words, they may cause unexpected behavior but + should never cause an outright crash or security issue.

    Note that the C-style API does not throw any exceptions on its own because it has a no-throw policy in order to retain its C-style @@ -48,7 +59,7 @@ package org.sqlite.jni.annotation; code.

    This annotation is solely for the use by the classes in the - org.sqlite package and subpackages, but is made public so that + org.sqlite.jni package and subpackages, but is made public so that javadoc will link to it from the annotated functions. It is not part of the public API and client-level code must not rely on it.

    diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 8e0cb8f4aa..c14772353d 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -1734,20 +1734,24 @@ public final class CApi { @NotNull OutputPointer.Int64 pHighwater, boolean reset ); - public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); + private static native int sqlite3_step(@NotNull long ptrToStmt); + + public static int sqlite3_step(@NotNull sqlite3_stmt stmt){ + return null==stmt ? SQLITE_MISUSE : sqlite3_step(stmt.getNativePointer()); + } public static native boolean sqlite3_stmt_busy(@NotNull sqlite3_stmt stmt); private static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op); public static int sqlite3_stmt_explain(@NotNull sqlite3_stmt stmt, int op){ - return sqlite3_stmt_explain(stmt.getNativePointer(), op); + return null==stmt ? SQLITE_MISUSE : sqlite3_stmt_explain(stmt.getNativePointer(), op); } private static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt); public static int sqlite3_stmt_isexplain(@NotNull sqlite3_stmt stmt){ - return sqlite3_stmt_isexplain(stmt.getNativePointer()); + return null==stmt ? 0 : sqlite3_stmt_isexplain(stmt.getNativePointer()); } public static native boolean sqlite3_stmt_readonly(@NotNull sqlite3_stmt stmt); diff --git a/manifest b/manifest index f6390adef5..65ac65ba7a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Two\smore\sJNI\sbuild\sfixes\sfor\sWindows/MinGW,\sreported\sin\s[forum:4f949edc312d2a75|forum\spost\s4f949edc312d2a75]. -D 2023-11-09T12:01:02.729 +C Add\ssome\snotes\sabout\sthe\sJNI\spointer-passing\sapproach\sand\sconvert\sa\scouple\sof\spotential\sNullPointerExceptions\sinto\sappropriate\sC\sresult\scodes.\sClarify\sthat\sinvocation\sof\sundefined\sbehaviour\sfrom\sthe\sJava\sAPI\sdoes\snot\s(due\sto\sthe\saddition\sof\sdefensive\scode)\smean\sthe\ssame\sthing\sas\sit\sdoes\sin\sC\s(e.g.\sno\sNULL\spointer\sdereferences). +D 2023-11-09T12:48:54.107 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,9 +241,9 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d984ea9c4e3536188f9d663120db8fb97b83329f4b8864bd88f75ebe581b8b8b F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 6b95974189d7cc394afbe15507050f1d174170a65be5a4dad201ab11f0a9777a -F ext/jni/src/c/sqlite3-jni.h 18925c56d6664fdec081c56daf3b2ffa0e0ff6b9b128b9f39b84862f34ba0601 -F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2 +F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26 +F ext/jni/src/c/sqlite3-jni.h 489044eae9fc6c2d62c1621e41594adf7bfcd4049b514a202c4aa6fe5c1ef405 +F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 16a28138c3c25f33356193970644389ff8ebc0720499549653934b2529c8d1dd +F ext/jni/src/org/sqlite/jni/capi/CApi.java 2917e2c608ac52ebe30fbcde2b520c6ea3bc99e734619dfdedd072b8e956b84f F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -2139,8 +2139,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 1c98d46d60ef1494bd8b7561c7d0cd5aafc178201a6f1f0da25dea6140b91cd0 -R 17c07891ab0f75a8281c2b918fd5c523 +P a3f9c39086e582e16ca15647961956b3c28d038655d3b43d4b94bd306fbec1a4 +R d00dc0a6ea6feccf2aee95fe4c483ba0 U stephan -Z 19240fed0f90b5e1367576a460d156b0 +Z 4aa2d438a8d23b9c44b1a48729f3cdde # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 300175f17c..7b8b377c39 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a3f9c39086e582e16ca15647961956b3c28d038655d3b43d4b94bd306fbec1a4 \ No newline at end of file +19c4778f45261006368b2d9460350fed1e55fed314c8b3e1af34cd8c3c73b7d8 \ No newline at end of file From 6eb381ff4a2c430335f5eb6f43915456ef4cbadd Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 12:58:03 +0000 Subject: [PATCH 155/347] Add the SQLITE_STRICT_SUBTYPE compile-time option. This change reveals that the current SQLITE_RESULT_SUBTYPE design does not work unless we tag the ->> operator with SQLITE_RESULT_SUBTYPE. But that will disable an important optimization. FossilOrigin-Name: e98a9a65dd309f72c240e280c7bebabc58af664fae9ee0d30c3fa1c78db5bae9 --- manifest | 15 +++++++-------- manifest.uuid | 2 +- src/sqlite.h.in | 6 ++++++ src/vdbeapi.c | 10 ++++++++++ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 50b7a2513d..b8e1b6dbb4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sreplace\sexpressions\sthat\sreturn\ssubtypes\swith\svalues\staken\sfrom\san\nindex. -D 2023-11-09T12:17:57.430 +C Add\sthe\sSQLITE_STRICT_SUBTYPE\scompile-time\soption.\s\sThis\schange\sreveals\sthat\nthe\scurrent\sSQLITE_RESULT_SUBTYPE\sdesign\sdoes\snot\swork\sunless\swe\stag\sthe\s->>\noperator\swith\sSQLITE_RESULT_SUBTYPE.\s\sBut\sthat\swill\sdisable\san\simportant\noptimization. +D 2023-11-09T12:58:03.925 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -726,7 +726,7 @@ F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f -F src/sqlite.h.in 8da45c84e79cde72c73fcb4260addcc7c00fac3bc6f5594b81a3792c1b196264 +F src/sqlite.h.in b6eac8ba5956af95269875a27273ab4c318d16ce4143f1d944962defce9accc6 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 F src/sqliteInt.h cd171cba32c7a553e7623fbd82b68b36a1b6c81079ab963260777ea9b3abe4d9 @@ -797,7 +797,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 7034cf3eec0c905df753368efbcdd96377fca0245584e66766ec47a29fe468c8 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c db190d007bdf5b9165edeb12369f4c59a459f88fd652c1671c1238862e662cc3 +F src/vdbeapi.c 2fdec801f959512b4f752eaeb4bf926e2363f7f16b34675634eac059b6874279 F src/vdbeaux.c f3997b5956c8d97bd2fc3392db42caecddfa6549e9df82e0a7e5804653ca475a F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 @@ -2139,9 +2139,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 563ad3be60d22c45f1c5b9a3e67738593f8b38f137147c56514166fbabf95365 e908b26a990929996b3c16f0429e8313cd8fcefe7c883c77f66ea69f4059d6e2 -R 9822cb01a0a25ade383ea6b744dcbd50 -T +closed e908b26a990929996b3c16f0429e8313cd8fcefe7c883c77f66ea69f4059d6e2 +P a35d13db09e32ee339f3983fe36b073714753ee3d39f577ae8d20596d7adc3eb +R 6425b1b4c4c82163cf87cfe5bb4306d2 U drh -Z 9cbc66987b5c47a4fc137a8fa57a64ca +Z 98ed3058dfd2554eb2316e0ec6804b54 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f5689a65a6..5aaf6a985d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a35d13db09e32ee339f3983fe36b073714753ee3d39f577ae8d20596d7adc3eb \ No newline at end of file +e98a9a65dd309f72c240e280c7bebabc58af664fae9ee0d30c3fa1c78db5bae9 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 09e6f47653..0932a2cb25 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -6228,6 +6228,12 @@ int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** interface is invoked within an SQL function that does not have the ** SQLITE_RESULT_SUBTYPE property, then sqlite3_result_subtype() ** might fail to set the result subtype. +** +** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any +** SQL function that invokes the sqlite3_result_subtype() interface +** and that does not have the SQLITE_RESULT_SUBTYPE property will raise +** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 +** by default. */ void sqlite3_result_subtype(sqlite3_context*,unsigned int); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 6724035fd5..af717734e0 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -539,6 +539,16 @@ void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ #ifdef SQLITE_ENABLE_API_ARMOR if( pCtx==0 ) return; #endif +#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0 + if( (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 ){ + char zErr[200]; + sqlite3_snprintf(sizeof(zErr), zErr, + "misuse of sqlite3_result_subtype() by %s()", + pCtx->pFunc->zName); + sqlite3_result_error(pCtx, zErr, -1); + return; + } +#endif /* SQLITE_STRICT_SUBTYPE */ pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); pOut->eSubtype = eSubtype & 0xff; From 752722e8d1907522c1b4cb8e8c33edcbb14325fb Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 9 Nov 2023 13:00:33 +0000 Subject: [PATCH 156/347] A .class file build dependencies fix in the JNI build. FossilOrigin-Name: 8cc32915165efd7c261f008bb6fa4cc6581ee7bd73ea5da47513742b9e6d34e4 --- ext/jni/GNUmakefile | 4 +++- ext/jni/src/org/sqlite/jni/capi/CApi.java | 4 ++-- .../src/org/sqlite/jni/capi/sqlite3_blob.java | 1 - manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index dc93f49085..25dc1596a4 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -112,6 +112,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\ WindowFunction.java \ XDestroyCallback.java \ sqlite3.java \ + sqlite3_blob.java \ sqlite3_context.java \ sqlite3_stmt.java \ sqlite3_value.java \ @@ -162,12 +163,13 @@ endif CLASS_FILES := define CLASSFILE_DEPS all: $(1).class +$(1).class: $(1).java CLASS_FILES += $(1).class endef $(foreach B,$(basename \ $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\ $(eval $(call CLASSFILE_DEPS,$(B)))) -$(CLASS_FILES): $(JAVA_FILES) $(MAKEFILE) +$(CLASS_FILES): $(MAKEFILE) $(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES) #.PHONY: classfiles diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index c14772353d..1f99b8e4a8 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -1772,7 +1772,7 @@ public final class CApi { signature is the public-facing one. */ private static native int sqlite3_strglob( - @NotNull byte[] glob, @NotNull byte[] nullTerminatedUtf8 + @NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8 ); public static int sqlite3_strglob( @@ -1786,7 +1786,7 @@ public final class CApi { The LIKE counterpart of the private sqlite3_strglob() method. */ private static native int sqlite3_strlike( - @NotNull byte[] glob, @NotNull byte[] nullTerminatedUtf8, + @NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8, int escChar ); diff --git a/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java b/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java index 4bca3363ff..bdc0200af4 100644 --- a/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java +++ b/ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java @@ -27,5 +27,4 @@ public final class sqlite3_blob extends NativePointerHolder @Override public void close(){ CApi.sqlite3_blob_close(this); } - } diff --git a/manifest b/manifest index 65ac65ba7a..306eb3b70f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssome\snotes\sabout\sthe\sJNI\spointer-passing\sapproach\sand\sconvert\sa\scouple\sof\spotential\sNullPointerExceptions\sinto\sappropriate\sC\sresult\scodes.\sClarify\sthat\sinvocation\sof\sundefined\sbehaviour\sfrom\sthe\sJava\sAPI\sdoes\snot\s(due\sto\sthe\saddition\sof\sdefensive\scode)\smean\sthe\ssame\sthing\sas\sit\sdoes\sin\sC\s(e.g.\sno\sNULL\spointer\sdereferences). -D 2023-11-09T12:48:54.107 +C A\s.class\sfile\sbuild\sdependencies\sfix\sin\sthe\sJNI\sbuild. +D 2023-11-09T13:00:33.039 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -238,7 +238,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 -F ext/jni/GNUmakefile d984ea9c4e3536188f9d663120db8fb97b83329f4b8864bd88f75ebe581b8b8b +F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9cd020d4 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 2917e2c608ac52ebe30fbcde2b520c6ea3bc99e734619dfdedd072b8e956b84f +F ext/jni/src/org/sqlite/jni/capi/CApi.java ca7b783c24b40ceac6f20ef51636682577230e463ccc91ed83a0645ba1a745ba F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -278,7 +278,7 @@ F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba90943 F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e F ext/jni/src/org/sqlite/jni/capi/sqlite3.java c6a5c555d163d76663534f2b2cce7cab15325b9852d0f58c6688a85e73ae52f0 F ext/jni/src/org/sqlite/jni/capi/sqlite3_backup.java 6742b431cd4d77e8000c1f92ec66265a58414c86bf3b0b5fbcb1164e08477227 -F ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java e8c799ed89a7725ba779ff7d4b1d941c5aaae1b1c3888a68a10ee9cca5c71e7d +F ext/jni/src/org/sqlite/jni/capi/sqlite3_blob.java 59e26ca5254cd4771f467237bcfe2d8deed30a77152fabcd4574fd406c301d63 F ext/jni/src/org/sqlite/jni/capi/sqlite3_context.java f0ef982009c335c4393ffcb68051809ca1711e4f47bcb8d1d46952f22c01bc22 F ext/jni/src/org/sqlite/jni/capi/sqlite3_stmt.java 293b5fa7d5b5724c87de544654aca1103d76f3092bc2c8f4360102a65ba25dff F ext/jni/src/org/sqlite/jni/capi/sqlite3_value.java e1d62a257c13504b46d39d5c21c49cf157ad73fda00cc5f34c931aa008c37049 @@ -2139,8 +2139,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 a3f9c39086e582e16ca15647961956b3c28d038655d3b43d4b94bd306fbec1a4 -R d00dc0a6ea6feccf2aee95fe4c483ba0 +P 19c4778f45261006368b2d9460350fed1e55fed314c8b3e1af34cd8c3c73b7d8 +R cd252fb625d9fc6706ab257c76ca795a U stephan -Z 4aa2d438a8d23b9c44b1a48729f3cdde +Z de48d509d9df78c4a1c1c2968a76e682 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7b8b377c39..922f6f0dc8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -19c4778f45261006368b2d9460350fed1e55fed314c8b3e1af34cd8c3c73b7d8 \ No newline at end of file +8cc32915165efd7c261f008bb6fa4cc6581ee7bd73ea5da47513742b9e6d34e4 \ No newline at end of file From b10c3d32e05d88288e106681d4e08d7ffa719dd2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 15:01:56 +0000 Subject: [PATCH 157/347] Fixes: (1) In the ->> function, instead of setting a subtype and clearing it, do not set it in the first place, as doing the set would trigger an error under SQLITE_STRICT_SUBTYPE. (2) Allow the SQLITE_STRICT_SUBTYPE through the property filter on sqlite3_create_function(). FossilOrigin-Name: 6195468b14f6f17ea072cf191c9ef1bd0713acd314bc6dc128be7322bfd612cc --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/json.c | 33 +++++++++++++++++---------------- src/main.c | 2 +- src/vdbeapi.c | 4 +++- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/manifest b/manifest index b8e1b6dbb4..23f1a35a4e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_STRICT_SUBTYPE\scompile-time\soption.\s\sThis\schange\sreveals\sthat\nthe\scurrent\sSQLITE_RESULT_SUBTYPE\sdesign\sdoes\snot\swork\sunless\swe\stag\sthe\s->>\noperator\swith\sSQLITE_RESULT_SUBTYPE.\s\sBut\sthat\swill\sdisable\san\simportant\noptimization. -D 2023-11-09T12:58:03.925 +C Fixes:\s\s(1)\sIn\sthe\s->>\sfunction,\sinstead\sof\ssetting\sa\ssubtype\sand\sclearing\sit,\ndo\snot\sset\sit\sin\sthe\sfirst\splace,\sas\sdoing\sthe\sset\swould\strigger\san\serror\nunder\sSQLITE_STRICT_SUBTYPE.\s\s(2)\sAllow\sthe\sSQLITE_STRICT_SUBTYPE\sthrough\nthe\sproperty\sfilter\son\ssqlite3_create_function(). +D 2023-11-09T15:01:56.971 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -684,10 +684,10 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c bf117324d2efff47b392c29f7d3e611a7508933b91491c7ad349101e6d5f5e05 +F src/json.c afbda7533099b951f6e7fbe5c050d12fb7b736bf377b263f824f64bbc6af7f0c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 -F src/main.c e1bc8864834697503d370d94613be945d05ca1c5ebdda43e7d5c8ee8c48d433c +F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 F src/malloc.c f016922435dc7d1f1f5083a03338a3e91f8c67ce2c5bdcfa4cdef62e612f5fcc F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -797,7 +797,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 7034cf3eec0c905df753368efbcdd96377fca0245584e66766ec47a29fe468c8 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c 2fdec801f959512b4f752eaeb4bf926e2363f7f16b34675634eac059b6874279 +F src/vdbeapi.c b07df805110dc6e81f2a3f9cd4e83f56ea523277a59bcec489a12b740c1079e7 F src/vdbeaux.c f3997b5956c8d97bd2fc3392db42caecddfa6549e9df82e0a7e5804653ca475a F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 @@ -2139,8 +2139,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 a35d13db09e32ee339f3983fe36b073714753ee3d39f577ae8d20596d7adc3eb -R 6425b1b4c4c82163cf87cfe5bb4306d2 +P e98a9a65dd309f72c240e280c7bebabc58af664fae9ee0d30c3fa1c78db5bae9 +R e019c72939d4c624411f804ee942841e U drh -Z 98ed3058dfd2554eb2316e0ec6804b54 +Z 80a4b0d4e614ee4844ff727ed0551cab # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5aaf6a985d..d8dd13b107 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e98a9a65dd309f72c240e280c7bebabc58af664fae9ee0d30c3fa1c78db5bae9 \ No newline at end of file +6195468b14f6f17ea072cf191c9ef1bd0713acd314bc6dc128be7322bfd612cc \ No newline at end of file diff --git a/src/json.c b/src/json.c index 132a274cd3..864504386f 100644 --- a/src/json.c +++ b/src/json.c @@ -833,7 +833,8 @@ static void jsonReturnJson( JsonParse *pParse, /* The complete JSON */ JsonNode *pNode, /* Node to return */ sqlite3_context *pCtx, /* Return value for this function */ - int bGenerateAlt /* Also store the rendered text in zAlt */ + int bGenerateAlt, /* Also store the rendered text in zAlt */ + int omitSubtype /* Do not call sqlite3_result_subtype() */ ){ JsonString s; if( pParse->oom ){ @@ -848,7 +849,7 @@ static void jsonReturnJson( pParse->nAlt = s.nUsed; } jsonResult(&s); - sqlite3_result_subtype(pCtx, JSON_SUBTYPE); + if( !omitSubtype ) sqlite3_result_subtype(pCtx, JSON_SUBTYPE); } } @@ -889,7 +890,8 @@ static u32 jsonHexToInt4(const char *z){ static void jsonReturn( JsonParse *pParse, /* Complete JSON parse tree */ JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx /* Return value for this function */ + sqlite3_context *pCtx, /* Return value for this function */ + int omitSubtype /* Do not call sqlite3_result_subtype() */ ){ switch( pNode->eType ){ default: { @@ -1035,7 +1037,7 @@ static void jsonReturn( } case JSON_ARRAY: case JSON_OBJECT: { - jsonReturnJson(pParse, pNode, pCtx, 0); + jsonReturnJson(pParse, pNode, pCtx, 0, omitSubtype); break; } } @@ -2387,7 +2389,7 @@ static void jsonParseFunc( printf("iSubst = %u\n", p->iSubst); printf("iHold = %u\n", p->iHold); jsonDebugPrintNodeEntries(p->aNode, p->nNode); - jsonReturnJson(p, p->aNode, ctx, 1); + jsonReturnJson(p, p->aNode, ctx, 1, 0); } /* @@ -2573,15 +2575,14 @@ static void jsonExtractFunc( } if( pNode ){ if( flags & JSON_JSON ){ - jsonReturnJson(p, pNode, ctx, 0); + jsonReturnJson(p, pNode, ctx, 0, 0); }else{ - jsonReturn(p, pNode, ctx); - sqlite3_result_subtype(ctx, 0); + jsonReturn(p, pNode, ctx, 1); } } }else{ pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx); + if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx, 0); } }else{ /* Two or more PATH arguments results in a JSON array with each @@ -2707,7 +2708,7 @@ static void jsonPatchFunc( if( pResult && pX->oom==0 ){ jsonDebugPrintParse(pX); jsonDebugPrintNode(pResult); - jsonReturnJson(pX, pResult, ctx, 0); + jsonReturnJson(pX, pResult, ctx, 0, 0); }else{ sqlite3_result_error_nomem(ctx); } @@ -2786,7 +2787,7 @@ static void jsonRemoveFunc( } } if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0); } remove_done: jsonDebugPrintParse(p); @@ -2915,7 +2916,7 @@ static void jsonReplaceFunc( jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0); replace_err: jsonDebugPrintParse(pParse); jsonParseFree(pParse); @@ -2969,7 +2970,7 @@ static void jsonSetFunc( } } jsonDebugPrintParse(pParse); - jsonReturnJson(pParse, pParse->aNode, ctx, 1); + jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0); jsonSetDone: jsonParseFree(pParse); } @@ -3484,7 +3485,7 @@ static int jsonEachColumn( case JEACH_KEY: { if( p->i==0 ) break; if( p->eType==JSON_OBJECT ){ - jsonReturn(&p->sParse, pThis, ctx); + jsonReturn(&p->sParse, pThis, ctx, 0); }else if( p->eType==JSON_ARRAY ){ u32 iKey; if( p->bRecursive ){ @@ -3500,7 +3501,7 @@ static int jsonEachColumn( } case JEACH_VALUE: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturn(&p->sParse, pThis, ctx); + jsonReturn(&p->sParse, pThis, ctx, 0); break; } case JEACH_TYPE: { @@ -3511,7 +3512,7 @@ static int jsonEachColumn( case JEACH_ATOM: { if( pThis->jnFlags & JNODE_LABEL ) pThis++; if( pThis->eType>=JSON_ARRAY ) break; - jsonReturn(&p->sParse, pThis, ctx); + jsonReturn(&p->sParse, pThis, ctx, 0); break; } case JEACH_ID: { diff --git a/src/main.c b/src/main.c index fbe00f5fa9..6acfdc325d 100644 --- a/src/main.c +++ b/src/main.c @@ -1914,7 +1914,7 @@ int sqlite3CreateFunc( assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY ); extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY| - SQLITE_SUBTYPE|SQLITE_INNOCUOUS); + SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE); enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY); /* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But diff --git a/src/vdbeapi.c b/src/vdbeapi.c index af717734e0..570cb3d8b6 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -540,7 +540,9 @@ void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ if( pCtx==0 ) return; #endif #if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0 - if( (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 ){ + if( pCtx->pFunc!=0 + && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 + ){ char zErr[200]; sqlite3_snprintf(sizeof(zErr), zErr, "misuse of sqlite3_result_subtype() by %s()", From cb1f190ed2fd7ee0f0ea788a32d06bf843b4b177 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 16:52:44 +0000 Subject: [PATCH 158/347] Futher documentation refinements. FossilOrigin-Name: 311c2eba93097bbecfa286bbeaff9bd6fc75a238e20cd2b6f834e594032d8c59 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/sqlite.h.in | 29 ++++++++++++++++------------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index 23f1a35a4e..f7930ccff9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes:\s\s(1)\sIn\sthe\s->>\sfunction,\sinstead\sof\ssetting\sa\ssubtype\sand\sclearing\sit,\ndo\snot\sset\sit\sin\sthe\sfirst\splace,\sas\sdoing\sthe\sset\swould\strigger\san\serror\nunder\sSQLITE_STRICT_SUBTYPE.\s\s(2)\sAllow\sthe\sSQLITE_STRICT_SUBTYPE\sthrough\nthe\sproperty\sfilter\son\ssqlite3_create_function(). -D 2023-11-09T15:01:56.971 +C Futher\sdocumentation\srefinements. +D 2023-11-09T16:52:44.766 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -726,7 +726,7 @@ F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f -F src/sqlite.h.in b6eac8ba5956af95269875a27273ab4c318d16ce4143f1d944962defce9accc6 +F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 F src/sqliteInt.h cd171cba32c7a553e7623fbd82b68b36a1b6c81079ab963260777ea9b3abe4d9 @@ -2139,8 +2139,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 e98a9a65dd309f72c240e280c7bebabc58af664fae9ee0d30c3fa1c78db5bae9 -R e019c72939d4c624411f804ee942841e +P 6195468b14f6f17ea072cf191c9ef1bd0713acd314bc6dc128be7322bfd612cc +R d70a69270dd45a660decb50b1e7c2d52 U drh -Z 80a4b0d4e614ee4844ff727ed0551cab +Z 8b58d9d66b776c4e0e8bd09f1958be56 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d8dd13b107..5faebb1c2d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6195468b14f6f17ea072cf191c9ef1bd0713acd314bc6dc128be7322bfd612cc \ No newline at end of file +311c2eba93097bbecfa286bbeaff9bd6fc75a238e20cd2b6f834e594032d8c59 \ No newline at end of file diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 0932a2cb25..53c037c3ba 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5573,11 +5573,15 @@ int sqlite3_create_window_function( ** ** ** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    -** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call +** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. -** Every function that invokes [sqlite3_value_subtype()] should have this -** property. If it does not, then the query planner might generate -** incorrect code for queries that use that function. +** This flag instructs SQLite to omit some corner-case optimizations that +** might disrupt the operation of the [sqlite3_value_subtype()] function, +** causing it to return zero rather than the correct subtype(). +** SQL functions that invokes [sqlite3_value_subtype()] should have this +** property. If the SQLITE_SUBTYPE property is omitted, then the return +** value from [sqlite3_value_subtype()] might sometimes be zero even though +** a non-zero subtype was specified by the function argument expression. ** ** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call @@ -5796,11 +5800,10 @@ int sqlite3_value_encoding(sqlite3_value*); ** routine to set the subtype for the return value of an SQL function. ** ** Every [application-defined SQL function] that invoke this interface -** must include the [SQLITE_SUBTYPE] property in the text +** should include the [SQLITE_SUBTYPE] property in the text ** encoding argument when the function is [sqlite3_create_function|registered]. -** The sqlite3_value_subtype() can return an incorrect answer if it -** is invoked from within an application-defined SQL function that does -** not have the [SQLITE_SUBTYPE] property. +** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() +** might return zero instead of the upstream subtype in some corner cases. */ unsigned int sqlite3_value_subtype(sqlite3_value*); @@ -6222,12 +6225,12 @@ int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** in future releases of SQLite. ** ** Every [application-defined SQL function] that invokes this interface -** must include the [SQLITE_RESULT_SUBTYPE] property in its +** should include the [SQLITE_RESULT_SUBTYPE] property in its ** text encoding argument when the SQL function is -** [sqlite3_create_function|registered]. If the sqlite3_result_subtype() -** interface is invoked within an SQL function that does not have the -** SQLITE_RESULT_SUBTYPE property, then sqlite3_result_subtype() -** might fail to set the result subtype. +** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] +** property is omitted from the function that invokes sqlite3_result_subtype(), +** then in some cases the sqlite3_result_subtype() might fail to set +** the result subtype. ** ** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any ** SQL function that invokes the sqlite3_result_subtype() interface From beb06e6b0a17b732ca0ab7e416891175eb87d3af Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 9 Nov 2023 17:26:39 +0000 Subject: [PATCH 159/347] Put an ALWAYS on an true branch. FossilOrigin-Name: 1e039b6eb59c0001a9efdd2f9928a34d4e9e01972ee76aa04a1279369dc03840 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/where.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index f7930ccff9..bf13040ece 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Futher\sdocumentation\srefinements. -D 2023-11-09T16:52:44.766 +C Put\san\sALWAYS\son\san\strue\sbranch. +D 2023-11-09T17:26:39.395 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -809,7 +809,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c bba7db5dae3ffe2c6b9c173fc10be4b570b125e985cb5b95a6c22716213adde4 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c 8718d58065745a4b7203a6b5d7c77b00d67eaf31a66d37c54a00f47c0cd23ac5 +F src/where.c d6ac0f598e725412af5b4484fe98187a129f3c8778c7dbc18ac7477294e190ca F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 @@ -2139,8 +2139,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 6195468b14f6f17ea072cf191c9ef1bd0713acd314bc6dc128be7322bfd612cc -R d70a69270dd45a660decb50b1e7c2d52 +P 311c2eba93097bbecfa286bbeaff9bd6fc75a238e20cd2b6f834e594032d8c59 +R 017a5657e59f3d0e3637d7716ee24315 U drh -Z 8b58d9d66b776c4e0e8bd09f1958be56 +Z a5d8bd3105f63db4fe25f071664830bd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5faebb1c2d..fc5a44f74d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -311c2eba93097bbecfa286bbeaff9bd6fc75a238e20cd2b6f834e594032d8c59 \ No newline at end of file +1e039b6eb59c0001a9efdd2f9928a34d4e9e01972ee76aa04a1279369dc03840 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 448fe27c11..555bd6a349 100644 --- a/src/where.c +++ b/src/where.c @@ -5818,7 +5818,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( FuncDef *pDef; sqlite3 *db = pParse->db; assert( ExprUseXList(pExpr) ); - n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; + n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ continue; From 2009a5acba967eab68304cdb8feeb340f5a45782 Mon Sep 17 00:00:00 2001 From: mistachkin Date: Thu, 9 Nov 2023 17:28:31 +0000 Subject: [PATCH 160/347] Fix compilation issue seen with MSVC. FossilOrigin-Name: 0dfe790d8118ff1bacc9e7c97d4f8ff9e5789f6cda8ec6fd981ea38b4da3905d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 306eb3b70f..184cdce6c6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C A\s.class\sfile\sbuild\sdependencies\sfix\sin\sthe\sJNI\sbuild. -D 2023-11-09T13:00:33.039 +C Fix\scompilation\sissue\sseen\swith\sMSVC. +D 2023-11-09T17:28:31.730 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -725,7 +725,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 -F src/shell.c.in 7312c571ebf518fc8927bbb5aeb4fa67e5b0dfb2adae4258dcd1ccae42c11e1f +F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 F src/sqlite.h.in a0fce680a40fe81b13eae3749d001134d9fe0a43aecc09a8986520d5119acfcd F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2139,8 +2139,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 19c4778f45261006368b2d9460350fed1e55fed314c8b3e1af34cd8c3c73b7d8 -R cd252fb625d9fc6706ab257c76ca795a -U stephan -Z de48d509d9df78c4a1c1c2968a76e682 +P 8cc32915165efd7c261f008bb6fa4cc6581ee7bd73ea5da47513742b9e6d34e4 +R 8854825d1f1736b898c17995440cc740 +U mistachkin +Z 0431c54643f1f1f0b5b6ac8fca652889 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 922f6f0dc8..1bd2f05fab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8cc32915165efd7c261f008bb6fa4cc6581ee7bd73ea5da47513742b9e6d34e4 \ No newline at end of file +0dfe790d8118ff1bacc9e7c97d4f8ff9e5789f6cda8ec6fd981ea38b4da3905d \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 8d201bbbda..0e0dbd36c7 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1311,7 +1311,11 @@ static void shellDtostr( char z[400]; if( n<1 ) n = 1; if( n>350 ) n = 350; +#if defined(_MSC_VER) + _snprintf(z, sizeof(z)-2, "%#+.*e", n, r); +#else snprintf(z, sizeof(z)-1, "%#+.*e", n, r); +#endif sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); } From 6db0b11e078f4b651f0cf00f845f3d77700c1a3a Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 9 Nov 2023 17:53:44 +0000 Subject: [PATCH 161/347] Expose SQLITE_RESULT_SUBTYPE to wasm. FossilOrigin-Name: 6d2fe9848beb35a8206e49c2ffae29a3eb2fe6411d77f366e962ced3f83e4749 --- ext/wasm/api/sqlite3-wasm.c | 1 + manifest | 15 +++++++-------- manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 18d27bdf0c..38e235d1aa 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -901,6 +901,7 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_DETERMINISTIC); DefInt(SQLITE_DIRECTONLY); DefInt(SQLITE_INNOCUOUS); + DefInt(SQLITE_RESULT_SUBTYPE); } _DefGroup; DefGroup(version) { diff --git a/manifest b/manifest index 29fbba28e4..ec49407a50 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_RESULT_SUBTYPE\sflag\sfor\sapplication-defined\sfunctions.\s\sAdd\nthe\s-DSQLITE_STRICT_SUBTYPE=1\scompile-time\soption\sthat\sraises\san\serror\sif\nany\sfunction\sinvokes\ssqlite3_result_subtype()\swithout\sthe\sSQLITE_RESULT_SUBTYPE\nflag.\s\sSQLITE_RESULT_SUBTYPE\sprevents\san\sindexed\svalue\sof\sthat\sfunction\sfrom\nbeing\sused\sto\sreplace\san\sequivalent\sexpression,\ssince\sthe\sindexed\sexpression\ndoes\snot\scarry\sthe\ssubtype.\s\sFix\sfor\sthe\sproblem\sdescribed\sat\n[forum:/forumpost/68d284c86b082c3e|forum\spost\s68d284c86b082c3e]. -D 2023-11-09T17:36:37.321 +C Expose\sSQLITE_RESULT_SUBTYPE\sto\swasm. +D 2023-11-09T17:53:44.655 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -591,7 +591,7 @@ F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 595953994aa3ae2287c889c4da39ab3d6f17b6461ecf4bec334b7a3faafddb02 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91ccfafc38b400fe36e90bdda88e -F ext/wasm/api/sqlite3-wasm.c 038de1b6d40b2cc0f41a143a0451db60b2a6f1b5bc06de67da255c54ea1661b7 +F ext/wasm/api/sqlite3-wasm.c d1b6c7264e28f6825d970321d589c2e35ecfe778dea28252a165d2bd1a407a83 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js a541112aa51e16705f13a99bb943c64efe178aa28c86704a955f8fd9afe4ba37 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -2139,9 +2139,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 0dfe790d8118ff1bacc9e7c97d4f8ff9e5789f6cda8ec6fd981ea38b4da3905d 1e039b6eb59c0001a9efdd2f9928a34d4e9e01972ee76aa04a1279369dc03840 -R 43a4ac60d204fdc6ba9347ecb0c1fc0b -T +closed 1e039b6eb59c0001a9efdd2f9928a34d4e9e01972ee76aa04a1279369dc03840 -U drh -Z b1e7abc144feb80d39f106f6377a94b7 +P ba789a7804ab96d81b15d6ef6fed1f802fa69db47cf91d368933e55289fa1d6e +R 41023b666496061ded61da7b2b1efc39 +U stephan +Z edb2bf000d7c6b4a58ab9adf26347d73 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 86ff0f4395..35c2e08c33 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ba789a7804ab96d81b15d6ef6fed1f802fa69db47cf91d368933e55289fa1d6e \ No newline at end of file +6d2fe9848beb35a8206e49c2ffae29a3eb2fe6411d77f366e962ced3f83e4749 \ No newline at end of file From af65e7d21f6d54474e60f8dc65db879bd400fb81 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 10 Nov 2023 14:15:07 +0000 Subject: [PATCH 162/347] Expose SQLITE_RESULT_SUBTYPE to JNI. FossilOrigin-Name: 3bf75875b8be8d2e878681506fb35f2062d59f07fa23f571c58cd92f270bb197 --- ext/jni/src/c/sqlite3-jni.h | 2 ++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 7 ++++--- .../src/org/sqlite/jni/wrapper1/SqlFunction.java | 8 ++------ manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index e655a71f63..7201372640 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -705,6 +705,8 @@ extern "C" { #define org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC 2048L #undef org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY #define org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY 524288L +#undef org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE +#define org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE 16777216L #undef org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS #define org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS 2097152L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 1f99b8e4a8..9b51bcf926 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -2445,9 +2445,10 @@ public final class CApi { public static final int SQLITE_TXN_WRITE = 2; // udf flags - public static final int SQLITE_DETERMINISTIC = 0x000000800; - public static final int SQLITE_DIRECTONLY = 0x000080000; - public static final int SQLITE_INNOCUOUS = 0x000200000; + public static final int SQLITE_DETERMINISTIC = 0x000000800; + public static final int SQLITE_DIRECTONLY = 0x000080000; + public static final int SQLITE_RESULT_SUBTYPE = 0x001000000; + public static final int SQLITE_INNOCUOUS = 0x000200000; // virtual tables public static final int SQLITE_INDEX_SCAN_UNIQUE = 1; diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index b3317029c7..6d583aaa48 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -25,13 +25,9 @@ public interface SqlFunction { public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC; public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS; public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY; + public static final int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE; public static final int UTF8 = CApi.SQLITE_UTF8; public static final int UTF16 = CApi.SQLITE_UTF16; - // /** - // For Window functions only and is not currently bound because - // doing so may require exposing sqlite3_value for effective use. - // */ - // public static final int SUBTYPE = CApi.SQLITE_SUBTYPE; /** The Arguments type is an abstraction on top of the lower-level @@ -167,7 +163,7 @@ public interface SqlFunction { } /** - Wrapper for a single SqlFunction argument. Primarily intended + Represents a single SqlFunction argument. Primarily intended for use with the Arguments class's Iterable interface. */ public final static class Arg { diff --git a/manifest b/manifest index ec49407a50..ba395a8b8d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expose\sSQLITE_RESULT_SUBTYPE\sto\swasm. -D 2023-11-09T17:53:44.655 +C Expose\sSQLITE_RESULT_SUBTYPE\sto\sJNI. +D 2023-11-10T14:15:07.897 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -242,7 +242,7 @@ F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9c F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26 -F ext/jni/src/c/sqlite3-jni.h 489044eae9fc6c2d62c1621e41594adf7bfcd4049b514a202c4aa6fe5c1ef405 +F ext/jni/src/c/sqlite3-jni.h a69394f018b40135d4198d0d6553d93dcfc7b76d2256877aaa7d12f4fbd5d218 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java ca7b783c24b40ceac6f20ef51636682577230e463ccc91ed83a0645ba1a745ba +F ext/jni/src/org/sqlite/jni/capi/CApi.java cd99604b6cb2fc5f1bb3c715afc05059f8748aba34a0edae21f9ed7b963cbdbb F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -295,7 +295,7 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7 +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java cf01f04548860e56d484e175976e10d145621b0c7c5119dfa271e8acf2f83a0e F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0ef62b43b1d6a9f044e106b56c9ea42bc7150b82ebeb79cff58f5be08cb9a435 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 40806dbbf8e120f115e33255d1813db13b40f0a598869e299a947a580429939b @@ -2139,8 +2139,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 ba789a7804ab96d81b15d6ef6fed1f802fa69db47cf91d368933e55289fa1d6e -R 41023b666496061ded61da7b2b1efc39 +P 6d2fe9848beb35a8206e49c2ffae29a3eb2fe6411d77f366e962ced3f83e4749 +R 90e12dd1bb83d03b6e6ef1578e525a93 U stephan -Z edb2bf000d7c6b4a58ab9adf26347d73 +Z b0d483f5215eeb51133ba8f1f724a33e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 35c2e08c33..e143455a4b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6d2fe9848beb35a8206e49c2ffae29a3eb2fe6411d77f366e962ced3f83e4749 \ No newline at end of file +3bf75875b8be8d2e878681506fb35f2062d59f07fa23f571c58cd92f270bb197 \ No newline at end of file From 828a00c287fc739edbc1a9864caf823f085860ec Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 10 Nov 2023 15:00:11 +0000 Subject: [PATCH 163/347] Expose the missing SQLITE_SUBTYPE to JNI. FossilOrigin-Name: 0f92f4c90eb9397325f1a86836e356862108e2e850c1801e0bec4a7030dea271 --- ext/jni/src/c/sqlite3-jni.h | 6 ++++-- ext/jni/src/org/sqlite/jni/capi/CApi.java | 3 ++- .../src/org/sqlite/jni/wrapper1/SqlFunction.java | 1 + manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 7201372640..51d49bba3c 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -705,10 +705,12 @@ extern "C" { #define org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC 2048L #undef org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY #define org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY 524288L -#undef org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE -#define org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE 16777216L +#undef org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE +#define org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE 1048576L #undef org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS #define org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS 2097152L +#undef org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE +#define org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE 16777216L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE #define org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE 1L #undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 9b51bcf926..07ca851ce5 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -2447,8 +2447,9 @@ public final class CApi { // udf flags public static final int SQLITE_DETERMINISTIC = 0x000000800; public static final int SQLITE_DIRECTONLY = 0x000080000; - public static final int SQLITE_RESULT_SUBTYPE = 0x001000000; + public static final int SQLITE_SUBTYPE = 0x000100000; public static final int SQLITE_INNOCUOUS = 0x000200000; + public static final int SQLITE_RESULT_SUBTYPE = 0x001000000; // virtual tables public static final int SQLITE_INDEX_SCAN_UNIQUE = 1; diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java index 6d583aaa48..dcfc2ebebd 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java @@ -25,6 +25,7 @@ public interface SqlFunction { public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC; public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS; public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY; + public static final int SUBTYPE = CApi.SQLITE_SUBTYPE; public static final int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE; public static final int UTF8 = CApi.SQLITE_UTF8; public static final int UTF16 = CApi.SQLITE_UTF16; diff --git a/manifest b/manifest index ba395a8b8d..2efc6960bd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expose\sSQLITE_RESULT_SUBTYPE\sto\sJNI. -D 2023-11-10T14:15:07.897 +C Expose\sthe\smissing\sSQLITE_SUBTYPE\sto\sJNI. +D 2023-11-10T15:00:11.915 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -242,7 +242,7 @@ F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9c F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26 -F ext/jni/src/c/sqlite3-jni.h a69394f018b40135d4198d0d6553d93dcfc7b76d2256877aaa7d12f4fbd5d218 +F ext/jni/src/c/sqlite3-jni.h 891444578550a7aa69fe5e0dedb3e6dedad752501ba99801f17797be51796934 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java cd99604b6cb2fc5f1bb3c715afc05059f8748aba34a0edae21f9ed7b963cbdbb +F ext/jni/src/org/sqlite/jni/capi/CApi.java 92d443b08175c798e132a312f71b1a42140c60d473d35c149e3d95a45b6550f3 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -295,7 +295,7 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ad F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 -F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java cf01f04548860e56d484e175976e10d145621b0c7c5119dfa271e8acf2f83a0e +F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 27b141f5914c7cb0e40e90a301d5e05b77f3bd42236834a68031b7086381fafd F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0ef62b43b1d6a9f044e106b56c9ea42bc7150b82ebeb79cff58f5be08cb9a435 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 40806dbbf8e120f115e33255d1813db13b40f0a598869e299a947a580429939b @@ -2139,8 +2139,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 6d2fe9848beb35a8206e49c2ffae29a3eb2fe6411d77f366e962ced3f83e4749 -R 90e12dd1bb83d03b6e6ef1578e525a93 +P 3bf75875b8be8d2e878681506fb35f2062d59f07fa23f571c58cd92f270bb197 +R 1f0c514b861aa02e5ce12be54a134415 U stephan -Z b0d483f5215eeb51133ba8f1f724a33e +Z a3fd8bb69210abdca1305b9c9a0a5e7d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e143455a4b..214f2b3b2a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3bf75875b8be8d2e878681506fb35f2062d59f07fa23f571c58cd92f270bb197 \ No newline at end of file +0f92f4c90eb9397325f1a86836e356862108e2e850c1801e0bec4a7030dea271 \ No newline at end of file From 9d60c3c256feae4d48d833c4eafcb83d8da0c695 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 10 Nov 2023 15:00:26 +0000 Subject: [PATCH 164/347] Expose the missing SQLITE_SUBTYPE to wasm. FossilOrigin-Name: ac9534b2ceb8185b1fc03282f881cd3e4aea64af75a02ebded1e07d4d8278739 --- ext/wasm/api/sqlite3-wasm.c | 3 ++- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 38e235d1aa..300307e5ea 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -877,7 +877,7 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_STMTSTATUS_FILTER_HIT); DefInt(SQLITE_STMTSTATUS_MEMUSED); } _DefGroup; - + DefGroup(syncFlags) { DefInt(SQLITE_SYNC_NORMAL); DefInt(SQLITE_SYNC_FULL); @@ -901,6 +901,7 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_DETERMINISTIC); DefInt(SQLITE_DIRECTONLY); DefInt(SQLITE_INNOCUOUS); + DefInt(SQLITE_SUBTYPE); DefInt(SQLITE_RESULT_SUBTYPE); } _DefGroup; diff --git a/manifest b/manifest index 2efc6960bd..1dba8fccae 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expose\sthe\smissing\sSQLITE_SUBTYPE\sto\sJNI. -D 2023-11-10T15:00:11.915 +C Expose\sthe\smissing\sSQLITE_SUBTYPE\sto\swasm. +D 2023-11-10T15:00:26.011 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -591,7 +591,7 @@ F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 595953994aa3ae2287c889c4da39ab3d6f17b6461ecf4bec334b7a3faafddb02 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91ccfafc38b400fe36e90bdda88e -F ext/wasm/api/sqlite3-wasm.c d1b6c7264e28f6825d970321d589c2e35ecfe778dea28252a165d2bd1a407a83 +F ext/wasm/api/sqlite3-wasm.c d0e09eb5ed3743c00294e30019e591c3aa150572ae7ffe8a8994568a7377589f F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js a541112aa51e16705f13a99bb943c64efe178aa28c86704a955f8fd9afe4ba37 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -2139,8 +2139,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 3bf75875b8be8d2e878681506fb35f2062d59f07fa23f571c58cd92f270bb197 -R 1f0c514b861aa02e5ce12be54a134415 +P 0f92f4c90eb9397325f1a86836e356862108e2e850c1801e0bec4a7030dea271 +R 38810c4503b5945c0ad84d8e7829e79f U stephan -Z a3fd8bb69210abdca1305b9c9a0a5e7d +Z 7eb27f62c37852223a1d4389da1ef1c0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 214f2b3b2a..c5efbb3408 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f92f4c90eb9397325f1a86836e356862108e2e850c1801e0bec4a7030dea271 \ No newline at end of file +ac9534b2ceb8185b1fc03282f881cd3e4aea64af75a02ebded1e07d4d8278739 \ No newline at end of file From 0eed27d38bdd23c596f46d24dd81be9af470dcd6 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 10 Nov 2023 15:03:18 +0000 Subject: [PATCH 165/347] Fix an obscure problem with the join-strength-reduction optimization that could occur when mixing LEFT and RIGHT joins in the same query. [forum:/forumpost/7f74ce0bee|Forum post 7f74ce0bee]. FossilOrigin-Name: 530d10e93a5f63b71aaa94a2b89102d012a2cda815997066beb0f585fe823536 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/select.c | 5 ++--- test/joinH.test | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 1dba8fccae..853509f8ea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expose\sthe\smissing\sSQLITE_SUBTYPE\sto\swasm. -D 2023-11-10T15:00:26.011 +C Fix\san\sobscure\sproblem\swith\sthe\sjoin-strength-reduction\soptimization\sthat\scould\soccur\swhen\smixing\sLEFT\sand\sRIGHT\sjoins\sin\sthe\ssame\squery.\s[forum:/forumpost/7f74ce0bee|Forum\spost\s7f74ce0bee]. +D 2023-11-10T15:03:18.325 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -724,7 +724,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515 +F src/select.c 47797c57c5ee2ad183b34a2e5d643ec7519366686bbe44a9a81df9fe304f28a7 F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1309,7 +1309,7 @@ F test/joinC.test 1f1a602c2127f55f136e2cbd3bf2d26546614bf8cffe5902ec1ac9c07f87f2 F test/joinD.test 2ce62e7353a0702ca5e70008faf319c1d4686aa19fba34275c6d1da0e960be28 F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127 -F test/joinH.test 84198ea42bf78b79fe399c0567218cd6df36c50c6dd27d9c4aab221acaad929e +F test/joinH.test c4301c738b05b845f273b0d94de74e953626d809dc945352909aedb199b42e5f F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4 F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e @@ -2139,8 +2139,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 0f92f4c90eb9397325f1a86836e356862108e2e850c1801e0bec4a7030dea271 -R 38810c4503b5945c0ad84d8e7829e79f -U stephan -Z 7eb27f62c37852223a1d4389da1ef1c0 +P ac9534b2ceb8185b1fc03282f881cd3e4aea64af75a02ebded1e07d4d8278739 +R a50d2c9cbe89dc0abaae6ac9eaaaf01b +U dan +Z c2b696ca5d071695417bed8717dba32c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c5efbb3408..e7c169124b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac9534b2ceb8185b1fc03282f881cd3e4aea64af75a02ebded1e07d4d8278739 \ No newline at end of file +530d10e93a5f63b71aaa94a2b89102d012a2cda815997066beb0f585fe823536 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 7a79385e0b..c2685b370e 100644 --- a/src/select.c +++ b/src/select.c @@ -7397,6 +7397,7 @@ int sqlite3Select( TREETRACE(0x1000,pParse,p, ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + unsetJoinExpr(p->pWhere, pItem->iCursor, 0); } } if( pItem->fg.jointype & JT_LTORJ ){ @@ -7411,6 +7412,7 @@ int sqlite3Select( TREETRACE(0x1000,pParse,p, ("RIGHT-JOIN simplifies to JOIN on term %d\n",j)); pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER); + unsetJoinExpr(p->pWhere, pI2->iCursor, 1); } } } @@ -7419,9 +7421,6 @@ int sqlite3Select( if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; } } - assert( pItem->iCursor>=0 ); - unsetJoinExpr(p->pWhere, pItem->iCursor, - pTabList->a[0].fg.jointype & JT_LTORJ); } /* No further action if this term of the FROM clause is not a subquery */ diff --git a/test/joinH.test b/test/joinH.test index 0fed7f2aa0..9f61002cb7 100644 --- a/test/joinH.test +++ b/test/joinH.test @@ -250,5 +250,45 @@ do_catchsql_test 9.11 { SELECT oid FROM wo2 JOIN (wo3 JOIN x3) } {0 99} +reset_db +do_execsql_test 10.0 { + CREATE TABLE rt0 (c0 INTEGER, c1 INTEGER, c2 INTEGER, c3 INTEGER, c4 INTEGER); + CREATE TABLE rt3 (c3 INTEGER); + + INSERT INTO rt0(c3, c1) VALUES (x'', '1'); + INSERT INTO rt0(c3, c1) VALUES ('-1', -1e500); + INSERT INTO rt0(c3, c1) VALUES (1, x''); + + CREATE VIEW v6(c0, c1, c2) AS SELECT 0, 0, 0; +} + +do_execsql_test 10.1 { + SELECT COUNT(*) FROM rt0 LEFT JOIN rt3 JOIN v6 ON ((CASE v6.c0 WHEN rt0.c4 THEN rt3.c3 END) NOT BETWEEN (rt0.c4) AND (NULL)) WHERE (rt0.c1); -- 2 +} {0} + +do_execsql_test 10.2 { + SELECT COUNT(*) FROM rt0 LEFT JOIN rt3 RIGHT OUTER JOIN v6 ON ((CASE v6.c0 WHEN rt0.c4 THEN rt3.c3 END) NOT BETWEEN (rt0.c4) AND (NULL)) WHERE (rt0.c1); -- 2 +} {0} + +#------------------------------------------------------------------------- + +do_execsql_test 11.1 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE TABLE t3(e, f); + + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t2 VALUES(2, 2); + INSERT INTO t3 VALUES(3, 3); +} + +do_execsql_test 11.2 { + SELECT * FROM t1 LEFT JOIN t2 RIGHT JOIN t3 ON (t2.c=10) +} {{} {} {} {} 3 3} + +do_execsql_test 11.3 { + SELECT * FROM t1 LEFT JOIN t2 RIGHT JOIN t3 ON (t2.c=10) WHERE t1.a=1 +} {} + finish_test From 53381132be7ca901d821922f31bd62d4558fc489 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 10 Nov 2023 16:29:02 +0000 Subject: [PATCH 166/347] Remove a NEVER() from whereAddIndexedExpr() that is reachable if there is an unknown indexed function in the schema. FossilOrigin-Name: a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/where.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 853509f8ea..18ac9d03d9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sobscure\sproblem\swith\sthe\sjoin-strength-reduction\soptimization\sthat\scould\soccur\swhen\smixing\sLEFT\sand\sRIGHT\sjoins\sin\sthe\ssame\squery.\s[forum:/forumpost/7f74ce0bee|Forum\spost\s7f74ce0bee]. -D 2023-11-10T15:03:18.325 +C Remove\sa\sNEVER()\sfrom\swhereAddIndexedExpr()\sthat\sis\sreachable\sif\sthere\nis\san\sunknown\sindexed\sfunction\sin\sthe\sschema. +D 2023-11-10T16:29:02.318 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -809,7 +809,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c bba7db5dae3ffe2c6b9c173fc10be4b570b125e985cb5b95a6c22716213adde4 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c d6ac0f598e725412af5b4484fe98187a129f3c8778c7dbc18ac7477294e190ca +F src/where.c 5b14ccd10ed4cfa3d62fa83bfa623aeda4d26dbc9f451c895a21797f0a024436 F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 @@ -2139,8 +2139,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 ac9534b2ceb8185b1fc03282f881cd3e4aea64af75a02ebded1e07d4d8278739 -R a50d2c9cbe89dc0abaae6ac9eaaaf01b -U dan -Z c2b696ca5d071695417bed8717dba32c +P 530d10e93a5f63b71aaa94a2b89102d012a2cda815997066beb0f585fe823536 +R cdb270c91f1e1a38ee4163db25142a2e +U drh +Z 1057cabad660e84de38d4c1b258b7805 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e7c169124b..109d74f0b0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -530d10e93a5f63b71aaa94a2b89102d012a2cda815997066beb0f585fe823536 \ No newline at end of file +a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f \ No newline at end of file diff --git a/src/where.c b/src/where.c index 555bd6a349..48722a8a82 100644 --- a/src/where.c +++ b/src/where.c @@ -5820,7 +5820,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( assert( ExprUseXList(pExpr) ); n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); - if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ + if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ continue; } } From 5a81e6e5cedb29be1ce8d41053c27e5b4a4f82fd Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 10 Nov 2023 17:49:26 +0000 Subject: [PATCH 167/347] Ensure 8-byte alignment of data structues in sqlite3_database_file_object(). This should have appeared on trunk originally and then be cherry-picked onto the branch. Oh well.... FossilOrigin-Name: ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30 --- manifest | 13 +++++++------ manifest.uuid | 2 +- src/pager.c | 5 ++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 18ac9d03d9..fadd914708 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sNEVER()\sfrom\swhereAddIndexedExpr()\sthat\sis\sreachable\sif\sthere\nis\san\sunknown\sindexed\sfunction\sin\sthe\sschema. -D 2023-11-10T16:29:02.318 +C Ensure\s8-byte\salignment\sof\sdata\sstructues\sin\ssqlite3_database_file_object().\nThis\sshould\shave\sappeared\son\strunk\soriginally\sand\sthen\sbe\scherry-picked\sonto\nthe\sbranch.\s\sOh\swell.... +D 2023-11-10T17:49:26.551 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -711,7 +711,7 @@ F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d87210 F src/os_unix.c 0a33005e6426702c7e76f3d451f296c088693a95b2be28ba9ef59c8d8529ce6b F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 699aab8dfc88056d796b03b40c0ab979040d58dfc3ae9db207f1be91e4880bbf +F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334 F src/pager.h f4d33fec8052603758792045493423b8871a996da2d0973927b7d36cd6070473 F src/parse.y 020d80386eb216ec9520549106353c517d2bbc89be28752ffdca649a9eaf56ec F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 @@ -2139,8 +2139,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 530d10e93a5f63b71aaa94a2b89102d012a2cda815997066beb0f585fe823536 -R cdb270c91f1e1a38ee4163db25142a2e +P a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f +Q +3cfcaafaff181c7945cc659ff6d58a0d2232d49830a259f0510d833a7a5a824b +R 1028456d182afa02f9e623f04c8760e0 U drh -Z 1057cabad660e84de38d4c1b258b7805 +Z a0dd8db0b2162f7029aa06a0eb66b266 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 109d74f0b0..fc184b9086 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f \ No newline at end of file +ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index e547504243..4687ab0f19 100644 --- a/src/pager.c +++ b/src/pager.c @@ -5062,10 +5062,13 @@ act_like_temp_file: */ sqlite3_file *sqlite3_database_file_object(const char *zName){ Pager *pPager; + const char *p; while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ zName--; } - pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); + p = zName - 4 - sizeof(Pager*); + assert( EIGHT_BYTE_ALIGNMENT(p) ); + pPager = *(Pager**)p; return pPager->fd; } From 10c815a55d7c85dbb44972446c180a928a72dc73 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 10 Nov 2023 20:35:59 +0000 Subject: [PATCH 168/347] Remove an incorrect ALWAYS() that was inserted yesterday [1e039b6eb59c0001]. FossilOrigin-Name: 12885e298b9d3f977f1de11a194692dfb5fbb7daeabd958674f884a5575ddd24 --- manifest | 13 ++++++------- manifest.uuid | 2 +- src/where.c | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index fadd914708..38fe16f21c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\s8-byte\salignment\sof\sdata\sstructues\sin\ssqlite3_database_file_object().\nThis\sshould\shave\sappeared\son\strunk\soriginally\sand\sthen\sbe\scherry-picked\sonto\nthe\sbranch.\s\sOh\swell.... -D 2023-11-10T17:49:26.551 +C Remove\san\sincorrect\sALWAYS()\sthat\swas\sinserted\syesterday\s[1e039b6eb59c0001]. +D 2023-11-10T20:35:59.820 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -809,7 +809,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c bba7db5dae3ffe2c6b9c173fc10be4b570b125e985cb5b95a6c22716213adde4 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c 5b14ccd10ed4cfa3d62fa83bfa623aeda4d26dbc9f451c895a21797f0a024436 +F src/where.c 45b2239e127beaaae2367e503ca4c82868d8fef707c7f7f4a6c0528e7d5f65ff F src/whereInt.h 4b38c5889514e3aead3f27d0ee9a26e47c3f150efc59e2a8b4e3bc8835e4d7a1 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00 @@ -2139,9 +2139,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 a976b7208ff8603d7353ce9a0bdfba8e681cbb2ed3de6cfb5f0e8b07312ab86f -Q +3cfcaafaff181c7945cc659ff6d58a0d2232d49830a259f0510d833a7a5a824b -R 1028456d182afa02f9e623f04c8760e0 +P ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30 +R ab374843bab00dc7e7d0efa052218f4c U drh -Z a0dd8db0b2162f7029aa06a0eb66b266 +Z 6634464e9b33a08cf926f1b7b363008f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fc184b9086..a6c6e97165 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30 \ No newline at end of file +12885e298b9d3f977f1de11a194692dfb5fbb7daeabd958674f884a5575ddd24 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 48722a8a82..cfd5d5e1a5 100644 --- a/src/where.c +++ b/src/where.c @@ -5818,7 +5818,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( FuncDef *pDef; sqlite3 *db = pParse->db; assert( ExprUseXList(pExpr) ); - n = ALWAYS(pExpr->x.pList) ? pExpr->x.pList->nExpr : 0; + n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ continue; From 88dcfe56dab431ef7c1bad9b3f55cfcefc28e022 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 10 Nov 2023 20:46:58 +0000 Subject: [PATCH 169/347] Additional debugging information on the tree-dump of the BETWEEN operator. FossilOrigin-Name: aca31e49d1d25043769544ccf2a07980c5f162a8eb2486e393bf9d9d1a394a60 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/treeview.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 38fe16f21c..0f91f19c68 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sincorrect\sALWAYS()\sthat\swas\sinserted\syesterday\s[1e039b6eb59c0001]. -D 2023-11-10T20:35:59.820 +C Additional\sdebugging\sinformation\son\sthe\stree-dump\sof\sthe\sBETWEEN\soperator. +D 2023-11-10T20:46:58.376 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -787,7 +787,7 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 23d9f4539880b40226254ad9072f4ecf12eb1902e62aea47aac29928afafcfd5 -F src/treeview.c 62fafcd31eea60b718f8daf448116b7b19f90134ebc6c20777ddbb07f56a3d28 +F src/treeview.c c6fc972683fd00f975d8b32a81c1f25d2fb7d4035366bf45c9f5622d3ccd70ee F src/trigger.c 0905b96b04bb6658509f711a8207287f1315cdbc3df1a1b13ba6483c8e341c81 F src/update.c 6904814dd62a7a93bbb86d9f1419c7f134a9119582645854ab02b36b676d9f92 F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 @@ -2139,8 +2139,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 ac39800bb2685fa287c7d834faed75f0bc61320ef986de314392d6eadb574d30 -R ab374843bab00dc7e7d0efa052218f4c +P 12885e298b9d3f977f1de11a194692dfb5fbb7daeabd958674f884a5575ddd24 +R c3f7aa166734d4220835a7bad8ba4679 U drh -Z 6634464e9b33a08cf926f1b7b363008f +Z 09fad8e886637b8c34b5cfcae694bf8a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a6c6e97165..e81340ae83 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -12885e298b9d3f977f1de11a194692dfb5fbb7daeabd958674f884a5575ddd24 \ No newline at end of file +aca31e49d1d25043769544ccf2a07980c5f162a8eb2486e393bf9d9d1a394a60 \ No newline at end of file diff --git a/src/treeview.c b/src/treeview.c index 1fad8673dd..2576532b65 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -781,7 +781,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ assert( pExpr->x.pList->nExpr==2 ); pY = pExpr->x.pList->a[0].pExpr; pZ = pExpr->x.pList->a[1].pExpr; - sqlite3TreeViewLine(pView, "BETWEEN"); + sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs); sqlite3TreeViewExpr(pView, pX, 1); sqlite3TreeViewExpr(pView, pY, 1); sqlite3TreeViewExpr(pView, pZ, 0); From fb28a93f75af10d3cf305e1a8c1cb6656d781bdd Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 10 Nov 2023 20:55:20 +0000 Subject: [PATCH 170/347] Fix another problem with mixed join types and the RIGHT JOIN strength-reduction optimization. [forum:/forumpost/befdab472d | Forum post befdab472d]. FossilOrigin-Name: f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/select.c | 2 +- test/joinH.test | 18 ++++++++++++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 0f91f19c68..f1683dfa58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Additional\sdebugging\sinformation\son\sthe\stree-dump\sof\sthe\sBETWEEN\soperator. -D 2023-11-10T20:46:58.376 +C Fix\sanother\sproblem\swith\smixed\sjoin\stypes\sand\sthe\sRIGHT\sJOIN\sstrength-reduction\soptimization.\s[forum:/forumpost/befdab472d\s|\sForum\spost\sbefdab472d]. +D 2023-11-10T20:55:20.957 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -724,7 +724,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 47797c57c5ee2ad183b34a2e5d643ec7519366686bbe44a9a81df9fe304f28a7 +F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1309,7 +1309,7 @@ F test/joinC.test 1f1a602c2127f55f136e2cbd3bf2d26546614bf8cffe5902ec1ac9c07f87f2 F test/joinD.test 2ce62e7353a0702ca5e70008faf319c1d4686aa19fba34275c6d1da0e960be28 F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127 -F test/joinH.test c4301c738b05b845f273b0d94de74e953626d809dc945352909aedb199b42e5f +F test/joinH.test f69e5b53b7d887914e854b6a131efbed4ea9f5ca52bdab81788bfc3e79299f43 F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4 F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e @@ -2139,8 +2139,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 12885e298b9d3f977f1de11a194692dfb5fbb7daeabd958674f884a5575ddd24 -R c3f7aa166734d4220835a7bad8ba4679 -U drh -Z 09fad8e886637b8c34b5cfcae694bf8a +P aca31e49d1d25043769544ccf2a07980c5f162a8eb2486e393bf9d9d1a394a60 +R 97cf5703d60c0dd519713f02ba70db03 +U dan +Z e3c451988b970e006792a61910aa1f15 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e81340ae83..873b025efc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aca31e49d1d25043769544ccf2a07980c5f162a8eb2486e393bf9d9d1a394a60 \ No newline at end of file +f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 \ No newline at end of file diff --git a/src/select.c b/src/select.c index c2685b370e..2b28d9ca5e 100644 --- a/src/select.c +++ b/src/select.c @@ -7416,7 +7416,7 @@ int sqlite3Select( } } } - for(j=pTabList->nSrc-1; j>=i; j--){ + for(j=pTabList->nSrc-1; j>=0; j--){ pTabList->a[j].fg.jointype &= ~JT_LTORJ; if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; } diff --git a/test/joinH.test b/test/joinH.test index 9f61002cb7..3702266804 100644 --- a/test/joinH.test +++ b/test/joinH.test @@ -290,5 +290,23 @@ do_execsql_test 11.3 { SELECT * FROM t1 LEFT JOIN t2 RIGHT JOIN t3 ON (t2.c=10) WHERE t1.a=1 } {} +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 12.1 { + CREATE TABLE t1(a1 INT, b1 TEXT); + INSERT INTO t1 VALUES(88,''); + CREATE TABLE t2(c2 INT, d2 TEXT); + INSERT INTO t2 VALUES(88,''); + CREATE TABLE t3(e3 TEXT PRIMARY KEY); + INSERT INTO t3 VALUES(''); +} + +do_execsql_test 12.2 { + SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; +} +do_execsql_test 12.3 { + SELECT * FROM t1 LEFT JOIN t2 ON true RIGHT JOIN t3 ON d2=e3 WHERE c2 BETWEEN NULL AND a1; +} finish_test From d2c86580246ea8103670ffd0c1949ad348f57418 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sat, 11 Nov 2023 13:09:09 +0000 Subject: [PATCH 171/347] Fix malf with redirected input due to bad assumption that stdin is a console in the fgets() replacement. FossilOrigin-Name: 79d1f2c1019964dd154fbdd3f349822cb946a2600883994523ed145047f0a9ea --- ext/consio/console_io.c | 6 +++--- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index 5d38feef37..620200a950 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -131,7 +131,6 @@ typedef struct ConsoleInfo { PerStreamTags pstSetup[3]; PerStreamTags pstDesignated[3]; StreamsAreConsole sacSetup; - StreamsAreConsole sacDesignated; } ConsoleInfo; static short isValidStreamInfo(PerStreamTags *ppst){ @@ -141,7 +140,7 @@ static short isValidStreamInfo(PerStreamTags *ppst){ static ConsoleInfo consoleInfo = { { /* pstSetup */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, { /* pstDesignated[] */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, - SAC_NoConsole, SAC_NoConsole /* sacSetup, sacDesignated */ + SAC_NoConsole /* sacSetup */ }; #undef SHELL_INVALID_FILE_PTR #undef CI_INITIALIZER @@ -520,7 +519,8 @@ static int mbcsToUtf8InPlaceIfValid(char *pc, int nci, int nco, UINT codePage){ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; #if SHELL_CON_TRANSLATE - if( pfIn == consoleInfo.pstSetup[0].pf ){ + if( pfIn == consoleInfo.pstSetup[0].pf + && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ # if SHELL_CON_TRANSLATE==1 # define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ WCHAR wcBuf[SHELL_GULP+1]; diff --git a/manifest b/manifest index c56d84fa85..2068eb4fbc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Pervasive\schanges\sto\sconsole_io.{c,h}\sin\ssupport\sof\ssimplifying\subiquitous\semit\sops\sin\sshell,\sand\sto\sget\sbetter\scontrol\sof\sconsole\sstreams\sthat\smight\sbe\sopened\sonly\svia\s.read\sor\s.output\scommands.\sChanges\sto\sshell\sto\suse\s{s,o,e}put{f,z}(...)\scalls\sfor\sinitial\stesting,\sbut\sthis\scheck-in\shas\sfew\ssuch\sconversions\sso\sthat\smost\swill\sbe\sin\sa\sseparate\scheck-in.\sMany\srenames\sto\sbetter\sfollow\srecent\scoding\sconvention.\sThis\scode\sseems\sto\sbe\sworking,\sbut\shas\snot\sbeen\stested\son\sdifficult\splatforms\sor\swith\smultiple\sconsole\shosts\syet.\sSo\sit\sis\sa\sWIP. -D 2023-11-11T06:20:38.614 +C Fix\smalf\swith\sredirected\sinput\sdue\sto\sbad\sassumption\sthat\sstdin\sis\sa\sconsole\sin\sthe\sfgets()\sreplacement. +D 2023-11-11T13:09:09.686 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,7 +50,7 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 19344e149e5c939c3c421823033abd4179d276ec1dae5f99e37d5bcd68ddbd59 x +F ext/consio/console_io.c c1e16d6c41a04952784abc4b055c5739c1ee3163aa475859725b66d1ccc656bd x F ext/consio/console_io.h c64d51d9f4e387679027d3b0977893390652f40c2e50a3c797506a9abb4856dc F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 @@ -2141,8 +2141,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 d5e88fcde53ca7ba05bb164943a9f57bd92080bb7e5eebbbed64b9886ac97338 3978c084a509c3c739fbe87e20feec9ddf1325e35170329987af197ca9fd731a f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 -R 13fdfe04f23cf8d5bcfae91b277b730e +P 14762a004cdf37d1e12f26aadff8ed3824893278f22ff141de86dd44d9b250f3 +R 5b544ddbaeeb17ecd11dc0a538d27e36 U larrybr -Z 7767a4710aecdf1826b87f96aec24468 +Z d3728cfdee8358558d79b5ead14c0ffb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 340d950f96..f798d85bff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -14762a004cdf37d1e12f26aadff8ed3824893278f22ff141de86dd44d9b250f3 \ No newline at end of file +79d1f2c1019964dd154fbdd3f349822cb946a2600883994523ed145047f0a9ea \ No newline at end of file From 307f95c1e4a2bd4de776c94880e4ae4152288146 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 11 Nov 2023 14:43:50 +0000 Subject: [PATCH 172/347] Do not cache a statement's column count in the JNI wrapper1 API because an ALTER TABLE via another statement may invalidate it, as reported in [forum:6d80efd58d4591c7|forum post 6d80efd58d4591c7]. FossilOrigin-Name: a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd --- ext/jni/src/org/sqlite/jni/capi/CApi.java | 2 +- .../src/org/sqlite/jni/wrapper1/Sqlite.java | 29 +++++++++---------- manifest | 16 +++++----- manifest.uuid | 2 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 07ca851ce5..2dc238ce3e 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -69,7 +69,7 @@ import java.util.Arrays; NUL-terminated, and conversion to a Java byte array must sometimes be careful to add one. Functions which take a length do not require this so long as the length is provided. Search the CApi class - for "\0" for many examples. + for "\0" for examples. diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index e61b7e59dd..0d7e2e3922 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -937,21 +937,11 @@ public final class Sqlite implements AutoCloseable { public static final class Stmt implements AutoCloseable { private Sqlite _db = null; private sqlite3_stmt stmt = null; - /** - We save the result column count in order to prevent having to - call into C to fetch that value every time we need to check - that value for the columnXyz() methods. - - Design note: if this is final then we cannot zero it in - finalizeStmt(). - */ - private int resultColCount; /** Only called by the prepare() factory functions. */ Stmt(Sqlite db, sqlite3_stmt stmt){ this._db = db; this.stmt = stmt; - this.resultColCount = CApi.sqlite3_column_count(stmt); synchronized(nativeToWrapper){ nativeToWrapper.put(this.stmt, this); } @@ -986,10 +976,10 @@ public final class Sqlite implements AutoCloseable { return stmt; } - /** Throws if n is out of range of this.resultColCount. Intended - to be used by the columnXyz() methods. */ + /** Throws if n is out of range of this statement's result column + count. Intended to be used by the columnXyz() methods. */ private sqlite3_stmt checkColIndex(int n){ - if(n<0 || n>=this.resultColCount){ + if(null==stmt || n<0 || n>=columnCount()){ throw new IllegalArgumentException("Column index "+n+" is out of range."); } return thisStmt(); @@ -1013,7 +1003,6 @@ public final class Sqlite implements AutoCloseable { CApi.sqlite3_finalize(stmt); stmt = null; _db = null; - resultColCount = 0; } return rc; } @@ -1184,8 +1173,18 @@ public final class Sqlite implements AutoCloseable { public String columnDeclType(int ndx){ return CApi.sqlite3_column_decltype( checkColIndex(ndx), ndx ); } + /** + Analog to sqlite3_column_count() but throws if this statement + has been finalized. + */ public int columnCount(){ - return resultColCount; + /* We cannot reliably cache the column count in a class + member because an ALTER TABLE from a separate statement + can invalidate that count and we have no way, short of + installing a COMMIT handler or the like, of knowing when + to re-read it. We cannot install such a handler without + interfering with a client's ability to do so. */ + return CApi.sqlite3_column_count(thisStmt()); } public int columnDataCount(){ return CApi.sqlite3_data_count( thisStmt() ); diff --git a/manifest b/manifest index f1683dfa58..8a365b54e0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sanother\sproblem\swith\smixed\sjoin\stypes\sand\sthe\sRIGHT\sJOIN\sstrength-reduction\soptimization.\s[forum:/forumpost/befdab472d\s|\sForum\spost\sbefdab472d]. -D 2023-11-10T20:55:20.957 +C Do\snot\scache\sa\sstatement's\scolumn\scount\sin\sthe\sJNI\swrapper1\sAPI\sbecause\san\sALTER\sTABLE\svia\sanother\sstatement\smay\sinvalidate\sit,\sas\sreported\sin\s[forum:6d80efd58d4591c7|forum\spost\s6d80efd58d4591c7]. +D 2023-11-11T14:43:50.704 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 92d443b08175c798e132a312f71b1a42140c60d473d35c149e3d95a45b6550f3 +F ext/jni/src/org/sqlite/jni/capi/CApi.java bd4a6490548f913bf9719443dee3d8a233f920ed1614b622738527d746e00f5d F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -296,7 +296,7 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 27b141f5914c7cb0e40e90a301d5e05b77f3bd42236834a68031b7086381fafd -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 0ef62b43b1d6a9f044e106b56c9ea42bc7150b82ebeb79cff58f5be08cb9a435 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 08a5037aaa5bc83d87f5389adce39c747835c210c445ff02669280e5cc7ffed1 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 40806dbbf8e120f115e33255d1813db13b40f0a598869e299a947a580429939b F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af @@ -2139,8 +2139,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 aca31e49d1d25043769544ccf2a07980c5f162a8eb2486e393bf9d9d1a394a60 -R 97cf5703d60c0dd519713f02ba70db03 -U dan -Z e3c451988b970e006792a61910aa1f15 +P f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 +R 6d2e0ef8621cb43b22b4fb0195114a8a +U stephan +Z 23f634cb68b0506eca8a1295175f53ed # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 873b025efc..d45a1f15b1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 \ No newline at end of file +a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd \ No newline at end of file From 56e1610f7a6be7ca5c6039c03a863f1ec58b8ba4 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 11 Nov 2023 14:50:01 +0000 Subject: [PATCH 173/347] JNI wrapper1: when checking for an out-of-bounds statement column index, perform the is-statement-finalized check before the range check so that the former exception trumps the latter. FossilOrigin-Name: 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d --- ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java | 2 +- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 0d7e2e3922..79ed32700c 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -979,7 +979,7 @@ public final class Sqlite implements AutoCloseable { /** Throws if n is out of range of this statement's result column count. Intended to be used by the columnXyz() methods. */ private sqlite3_stmt checkColIndex(int n){ - if(null==stmt || n<0 || n>=columnCount()){ + if(n<0 || n>=columnCount()){ throw new IllegalArgumentException("Column index "+n+" is out of range."); } return thisStmt(); diff --git a/manifest b/manifest index 8a365b54e0..a6ae666a58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\scache\sa\sstatement's\scolumn\scount\sin\sthe\sJNI\swrapper1\sAPI\sbecause\san\sALTER\sTABLE\svia\sanother\sstatement\smay\sinvalidate\sit,\sas\sreported\sin\s[forum:6d80efd58d4591c7|forum\spost\s6d80efd58d4591c7]. -D 2023-11-11T14:43:50.704 +C JNI\swrapper1:\swhen\schecking\sfor\san\sout-of-bounds\sstatement\scolumn\sindex,\sperform\sthe\sis-statement-finalized\scheck\sbefore\sthe\srange\scheck\sso\sthat\sthe\sformer\sexception\strumps\sthe\slatter. +D 2023-11-11T14:50:01.933 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -296,7 +296,7 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 27b141f5914c7cb0e40e90a301d5e05b77f3bd42236834a68031b7086381fafd -F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 08a5037aaa5bc83d87f5389adce39c747835c210c445ff02669280e5cc7ffed1 +F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java aeaec95323a8186d0b8e741affff067fe893849a2d862acd443373035c7b73a0 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 982538ddb4c0719ef87dfa664cd137b09890b546029a7477810bd64d4c47ee35 F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 40806dbbf8e120f115e33255d1813db13b40f0a598869e299a947a580429939b F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af @@ -2139,8 +2139,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 f1eae192315335d7e385b0a801a17700a9718d245bda6628518c5df9a1e9d3d6 -R 6d2e0ef8621cb43b22b4fb0195114a8a +P a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd +R 78a63cfa87011ebb24c6cac260db14c9 U stephan -Z 23f634cb68b0506eca8a1295175f53ed +Z ad92dad6ace48bd20f7cf67cbd6f4f40 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d45a1f15b1..4865106033 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd \ No newline at end of file +0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d \ No newline at end of file From ea80462c10860b64e57153cdd4a2e61e4d337c63 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sat, 11 Nov 2023 22:53:55 +0000 Subject: [PATCH 174/347] Remove SHELL_LEGACY_CONSOLE_IO PP symbol and code it made active. (It is in the repo if ever needed/wanted, but it is just inferior or dead code now.) FossilOrigin-Name: 27c5bf6563bc24ba7b47865e8d2f3e2d439666e19038d86dd8445fcdb9abe97a --- ext/consio/console_io.c | 112 +--------------------------------------- manifest | 12 ++--- manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 118 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index 8dd53dcf83..f4cf2cdeab 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -36,11 +36,7 @@ # define WIN32_LEAN_AND_MEAN # include # endif -# ifdef SHELL_LEGACY_CONSOLE_IO -# define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */ -# else -# define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ -# endif +# define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ #else # ifndef SHELL_NO_SYSINC # include @@ -152,9 +148,6 @@ static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ if( pstReachesConsole(ppst) ){ DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; SetConsoleMode(ppst->hx, cm); -# if SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(ppst->pf), _O_TEXT); -# endif } #else (void)ppst; @@ -186,9 +179,6 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ } consoleInfo.pstDesignated[ix] = *ppst; if( ix > 0 ) fflush(apf[ix]); -#if SHELL_CON_TRANSLATE == 2 - _setmode(_fileno(apf[ix]), _O_TEXT); -#endif } consoleInfo.sacSetup = rv; consoleRenewSetup(); @@ -203,15 +193,6 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ for( ix=0; ix<3; ++ix ){ if( pci->sacSetup & (SAC_InConsole<pstSetup[ix]; -# if SHELL_CON_TRANSLATE == 2 - static int tmode = _O_TEXT; - /* Consider: Read this mode in consoleClassifySetup somehow. - ** A _get_fmode() call almost works. But not with gcc, yet. - ** This has to be done to make the CLI a callable function - ** when legacy console I/O is done. (This may never happen.) - */ - _setmode(_fileno(pci->pstSetup[ix].pf), tmode); -# endif SetConsoleMode(ppst->hx, ppst->consMode); } } @@ -297,15 +278,6 @@ static int conioZstrOut(PerStreamTags *ppst, const char *z){ if( z!=NULL && *z!=0 ){ int nc; int nwc; -# if SHELL_CON_TRANSLATE == 2 - UINT cocp = GetConsoleOutputCP(); - FILE *pfO = ppst->pf; - if( cocp == CP_UTF8 ){ - /* This is not legacy action. But it can work better, - ** when the console putatively can handle UTF-8. */ - return fputs(z, pfO)<0 ? 0 : (int)strlen(z); - } -# endif nc = (int)strlen(z); nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, 0,0); if( nwc > 0 ){ @@ -313,24 +285,10 @@ static int conioZstrOut(PerStreamTags *ppst, const char *z){ if( zw!=NULL ){ nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, zw,nwc); if( nwc > 0 ){ -# if SHELL_CON_TRANSLATE == 2 - /* Legacy translation to active code page, then MBCS out. */ - rv = WideCharToMultiByte(cocp,0, zw,nwc, 0,0, 0,0); - if( rv != 0 ){ - char *zmb = sqlite3_malloc64(rv+1); - if( zmb != NULL ){ - rv = WideCharToMultiByte(cocp,0, zw,nwc, zmb,rv, 0,0); - zmb[rv] = 0; - if( fputs(zmb, pfO)<0 ) rv = 0; - sqlite3_free(zmb); - } - } -# elif SHELL_CON_TRANSLATE == 1 /* Translation from UTF-8 to UTF-16, then WCHARs out. */ if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ rv = nc; } -# endif } sqlite3_free(zw); } @@ -531,19 +489,6 @@ SQLITE_INTERNAL_LINKAGE int ePutcUtf8(int ch){ } #endif -#if SHELL_CON_TRANSLATE==2 -static int mbcsToUtf8InPlaceIfValid(char *pc, int nci, int nco, UINT codePage){ - WCHAR wcOneCode[2]; - int nuo = 0; - int nwConvert = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, - pc, nci, wcOneCode, 2); - if( nwConvert > 0 ){ - nuo = WideCharToMultiByte(CP_UTF8, 0, wcOneCode, nwConvert, pc, nco, 0,0); - } - return nuo; -} -#endif - SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; #if SHELL_CON_TRANSLATE @@ -600,61 +545,6 @@ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ cBuf[noc] = 0; return cBuf; }else return 0; -# elif SHELL_CON_TRANSLATE==2 - /* This is not done efficiently because it may never be used. - ** Also, it is interactive input so it need not be fast. */ - int nco = 0; - /* For converstion to WCHAR, or pre-test of same. */ - UINT cicp = GetConsoleCP(); /* For translation from mbcs. */ - /* If input code page is CP_UTF8, must bypass MBCS input - ** collection because getc() returns 0 for non-ASCII byte - ** Instead, use fgets() which repects character boundaries. */ - if( cicp == CP_UTF8 ) return fgets(cBuf, ncMax, pfIn); - while( ncMax-nco >= 5 ){ - /* Have space for max UTF-8 group and 0-term. */ - int nug = 0; - int c = getc(pfIn); - if( c < 0 ){ - if( nco > 0 ) break; - else return 0; - } - cBuf[nco] = (char)c; - if( c < 0x80 ){ - ++nco; - if( c == '\n' ) break; - continue; - } - /* Deal with possible mbcs lead byte. */ - nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1, ncMax-nco-1, cicp); - if( nug > 0 ){ - nco += nug; - }else{ - /* Must have just mbcs lead byte; get the trail byte(s). */ - int ntb = 1, ct; - while( ntb <= 3 ){ /* No more under any multi-byte code. */ - ct = getc(pfIn); - if( ct < 0 || ct == '\n' ){ - /* Just drop whatever garbage preceded the newline or. - ** EOF. It's not valid, should not happen, and there - ** is no good way to deal with it, short of bailing. */ - if( ct > 0 ){ - cBuf[nco++] = (int)ct; - } - break; - } - /* Treat ct as bona fide MBCS trailing byte, if valid. */ - cBuf[nco+ntb] = ct; - nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1+ntb, ncMax-nco-1, cicp); - if( nug > 0 ){ - nco += nug; - break; - } - } - if( ct < 0 ) break; - } - } - cBuf[nco] = 0; - return cBuf; # endif }else{ #endif diff --git a/manifest b/manifest index 390b40a3a6..646dce0563 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Complete\sshell\stransition\sto\susing\s{f,o,e}put{f,z}()\semit\sfunctions.\sThis\sfails\stest\s13.1\sin\sjson501.test,\sbut\sso\sdoes\strunk\sin\sthe\ssame\sway. -D 2023-11-11T20:46:12.670 +C Remove\sSHELL_LEGACY_CONSOLE_IO\sPP\ssymbol\sand\scode\sit\smade\sactive.\s(It\sis\sin\sthe\srepo\sif\sever\sneeded/wanted,\sbut\sit\sis\sjust\sinferior\sor\sdead\scode\snow.) +D 2023-11-11T22:53:55.698 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,7 +50,7 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 0b07826bc3cceb10b12f5a3701fc1540154916a845de003d87c447c8a68c5ba7 x +F ext/consio/console_io.c 20cd3ee900facdbe6ef39670b4135ede6822839beb7c858d8d6a3c1618be0012 x F ext/consio/console_io.h ec611fe8f08645d69cb18d46ab2a09c4653f2fc13ecb04c18e6012d8ea89c463 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 @@ -2141,8 +2141,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 79d1f2c1019964dd154fbdd3f349822cb946a2600883994523ed145047f0a9ea 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d -R fd4407aafdd7492d23f3b236cddd5186 +P 923c6b8b3a508c715b816c6bcd2ae9ac519bc37a62afc4ef813085c00f1e7cb6 +R 0bbf95d43c90eb82b70dc5a47c05dada U larrybr -Z cce7495ad383169e7382f36018db3c19 +Z 6b063716f7dfd90dd364e5381d9a3551 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5509cbc990..96fb87c997 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -923c6b8b3a508c715b816c6bcd2ae9ac519bc37a62afc4ef813085c00f1e7cb6 \ No newline at end of file +27c5bf6563bc24ba7b47865e8d2f3e2d439666e19038d86dd8445fcdb9abe97a \ No newline at end of file From cc2b3c20510843742acd74c00fb4de1b6836e1f2 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 12 Nov 2023 00:43:36 +0000 Subject: [PATCH 175/347] Write BOM without fwrite(), using sputz() so that if it goes to the console, it is translated "properly". FossilOrigin-Name: 06ef96a8233c6b6ca2653a0b780b3a401cab2b77594125f4f1c54b269c22a9f9 --- ext/consio/console_io.c | 10 ++++++++-- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 13 +++++-------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index f4cf2cdeab..b882a66b36 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -44,6 +44,11 @@ # define SHELL_CON_TRANSLATE 0 /* Use plain C library stream I/O at console */ #endif +#if SHELL_CON_TRANSLATE +/* Character used to represent a known-incomplete UTF-8 char group (�) */ +static WCHAR cBadGroup = 0xfffd; +#endif + #if SHELL_CON_TRANSLATE static HANDLE handleOfFile(FILE *pf){ int fileDesc = _fileno(pf); @@ -58,6 +63,7 @@ typedef struct PerStreamTags { #if SHELL_CON_TRANSLATE HANDLE hx; DWORD consMode; + char acIncomplete[4]; #else short reachesConsole; #endif @@ -71,8 +77,8 @@ typedef struct PerStreamTags { #endif #if SHELL_CON_TRANSLATE -# define CI_INITIALIZER \ - { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, SHELL_INVALID_FILE_PTR } +# define CI_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ + {0,0,0,0}, SHELL_INVALID_FILE_PTR } #else # define CI_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } #endif diff --git a/manifest b/manifest index 646dce0563..042109f19f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sSHELL_LEGACY_CONSOLE_IO\sPP\ssymbol\sand\scode\sit\smade\sactive.\s(It\sis\sin\sthe\srepo\sif\sever\sneeded/wanted,\sbut\sit\sis\sjust\sinferior\sor\sdead\scode\snow.) -D 2023-11-11T22:53:55.698 +C Write\sBOM\swithout\sfwrite(),\susing\ssputz()\sso\sthat\sif\sit\sgoes\sto\sthe\sconsole,\sit\sis\stranslated\s"properly". +D 2023-11-12T00:43:36.964 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,7 +50,7 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 20cd3ee900facdbe6ef39670b4135ede6822839beb7c858d8d6a3c1618be0012 x +F ext/consio/console_io.c d1f45b1380877d546adc24b339929b7d667117637e18a203c21aacf5d94ba430 x F ext/consio/console_io.h ec611fe8f08645d69cb18d46ab2a09c4653f2fc13ecb04c18e6012d8ea89c463 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in 1d689534130f9c242564b820726e4af37a8e1a347025355eb5a7e67a3c1a5306 +F src/shell.c.in 7254cf29e4fb08ff391bd74c083bbfa4d8ba48f1704218e90c1d1bafa9aaac0d F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2141,8 +2141,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 923c6b8b3a508c715b816c6bcd2ae9ac519bc37a62afc4ef813085c00f1e7cb6 -R 0bbf95d43c90eb82b70dc5a47c05dada +P 27c5bf6563bc24ba7b47865e8d2f3e2d439666e19038d86dd8445fcdb9abe97a +R 4e1a6116ca906eecf07c59b35b386a82 U larrybr -Z 6b063716f7dfd90dd364e5381d9a3551 +Z f176f092db188f8a58e05510b1dae671 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 96fb87c997..70573ed28c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -27c5bf6563bc24ba7b47865e8d2f3e2d439666e19038d86dd8445fcdb9abe97a \ No newline at end of file +06ef96a8233c6b6ca2653a0b780b3a401cab2b77594125f4f1c54b269c22a9f9 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index e9c226ac77..af8e8db4a1 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -9399,9 +9399,9 @@ static int do_meta_command(char *zLine, ShellState *p){ int i; int eMode = 0; int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ - unsigned char zBOM[4]; /* Byte-order mark to using if --bom is present */ + static const char *zBomUtf8 = "\xef\xbb\xbf"; + const char *zBom = 0; - zBOM[0] = 0; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( c=='e' ){ eMode = 'x'; @@ -9414,10 +9414,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[0]=='-' ){ if( z[1]=='-' ) z++; if( cli_strcmp(z,"-bom")==0 ){ - zBOM[0] = 0xef; - zBOM[1] = 0xbb; - zBOM[2] = 0xbf; - zBOM[3] = 0; + zBom = zBomUtf8; }else if( c!='e' && cli_strcmp(z,"-x")==0 ){ eMode = 'x'; /* spreadsheet */ }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ @@ -9484,7 +9481,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->out = stdout; rc = 1; }else{ - if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); + if( zBom ) sputz(p->out, zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif @@ -9497,7 +9494,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->out = stdout; rc = 1; } else { - if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); + if( zBom ) sputz(p->out, zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } From 782c9f2e9e2751a6a2745442fa2cbfab62969c1c Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 12 Nov 2023 03:58:15 +0000 Subject: [PATCH 176/347] Use setOutputStream() to designate implicit output for oput{z,f}() emit functions, and use them extensively. FossilOrigin-Name: 7850fb98a19d0ae3535367de3bca9e50408a2c21504c5772947fc39f287aa830 --- manifest | 12 +- manifest.uuid | 2 +- src/shell.c.in | 793 ++++++++++++++++++++++++------------------------- 3 files changed, 398 insertions(+), 409 deletions(-) diff --git a/manifest b/manifest index 042109f19f..f6dcc5d4ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Write\sBOM\swithout\sfwrite(),\susing\ssputz()\sso\sthat\sif\sit\sgoes\sto\sthe\sconsole,\sit\sis\stranslated\s"properly". -D 2023-11-12T00:43:36.964 +C Use\ssetOutputStream()\sto\sdesignate\simplicit\soutput\sfor\soput{z,f}()\semit\sfunctions,\sand\suse\sthem\sextensively. +D 2023-11-12T03:58:15.704 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -727,7 +727,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in 7254cf29e4fb08ff391bd74c083bbfa4d8ba48f1704218e90c1d1bafa9aaac0d +F src/shell.c.in 3169bab2f1f67ec01912b6b5e4ed8d629e5b8aece9a1a6fd7e88643b6c3abe18 F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2141,8 +2141,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 27c5bf6563bc24ba7b47865e8d2f3e2d439666e19038d86dd8445fcdb9abe97a -R 4e1a6116ca906eecf07c59b35b386a82 +P 06ef96a8233c6b6ca2653a0b780b3a401cab2b77594125f4f1c54b269c22a9f9 +R 0d28bc41b168f5a19d7cddd544d794a5 U larrybr -Z f176f092db188f8a58e05510b1dae671 +Z 3d0184f630f762e14e234f872c761cb8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 70573ed28c..ee60b43346 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -06ef96a8233c6b6ca2653a0b780b3a401cab2b77594125f4f1c54b269c22a9f9 \ No newline at end of file +7850fb98a19d0ae3535367de3bca9e50408a2c21504c5772947fc39f287aa830 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index af8e8db4a1..884eb140a7 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -634,12 +634,12 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ #endif /* -** Output string zUtf to stream pOut as w characters. If w is negative, +** Output string zUtf to Out stream as w characters. If w is negative, ** then right-justify the text. W is the width in UTF-8 characters, not ** in bytes. This is different from the %*.*s specification in printf ** since with %*.*s the width is measured in bytes, not characters. */ -static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ +static void utf8_width_print(int w, const char *zUtf){ int i; int n; int aw = w<0 ? -w : w; @@ -654,11 +654,11 @@ static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ } } if( n>=aw ){ - sputf(pOut, "%.*s", i, zUtf); + oputf("%.*s", i, zUtf); }else if( w<0 ){ - sputf(pOut, "%*s%s", aw-n, "", zUtf); + oputf("%*s%s", aw-n, "", zUtf); }else{ - sputf(pOut, "%s%*s", zUtf, aw-n, ""); + oputf("%s%*s", zUtf, aw-n, ""); } } @@ -1485,7 +1485,7 @@ static void shellPutsFunc( ){ ShellState *p = (ShellState*)sqlite3_user_data(pCtx); (void)nVal; - sputf(p->out, "%s\n", sqlite3_value_text(apVal[0])); + oputf("%s\n", sqlite3_value_text(apVal[0])); sqlite3_result_value(pCtx, apVal[0]); } @@ -1672,7 +1672,7 @@ static void outputModePop(ShellState *p){ /* ** Output the given string as a hex-encoded blob (eg. X'1234' ) */ -static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ +static void output_hex_blob(const void *pBlob, int nBlob){ int i; unsigned char *aBlob = (unsigned char*)pBlob; @@ -1689,7 +1689,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ } zStr[i*2] = '\0'; - sputf(out,"X'%s'", zStr); + oputf("X'%s'", zStr); sqlite3_free(zStr); } @@ -1719,25 +1719,26 @@ static const char *unused_string( ** ** See also: output_quoted_escaped_string() */ -static void output_quoted_string(FILE *out, const char *z){ +static void output_quoted_string(const char *z){ int i; char c; - setBinaryMode(out, 1); + FILE *pfO = setOutputStream(invalidFileStream); + setBinaryMode(pfO, 1); if( z==0 ) return; for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c==0 ){ - sputf(out,"'%s'",z); + oputf("'%s'",z); }else{ - sputz(out, "'"); + oputz("'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ - sputf(out, "%.*s", i, z); + oputf("%.*s", i, z); z += i; } if( c=='\'' ){ - sputz(out, "'"); + oputz("'"); continue; } if( c==0 ){ @@ -1745,9 +1746,9 @@ static void output_quoted_string(FILE *out, const char *z){ } z++; } - sputz(out, "'"); + oputz("'"); } - setTextMode(out, 1); + setTextMode(pfO, 1); } /* @@ -1759,13 +1760,14 @@ static void output_quoted_string(FILE *out, const char *z){ ** This is like output_quoted_string() but with the addition of the \r\n ** escape mechanism. */ -static void output_quoted_escaped_string(FILE *out, const char *z){ +static void output_quoted_escaped_string(const char *z){ int i; char c; - setBinaryMode(out, 1); + FILE *pfO = setOutputStream(invalidFileStream); + setBinaryMode(pfO, 1); for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} if( c==0 ){ - sputf(out,"'%s'",z); + oputf("'%s'",z); }else{ const char *zNL = 0; const char *zCR = 0; @@ -1777,23 +1779,23 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ if( z[i]=='\r' ) nCR++; } if( nNL ){ - sputz(out, "replace("); + oputz("replace("); zNL = unused_string(z, "\\n", "\\012", zBuf1); } if( nCR ){ - sputz(out, "replace("); + oputz("replace("); zCR = unused_string(z, "\\r", "\\015", zBuf2); } - sputz(out, "'"); + oputz("'"); while( *z ){ for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} if( c=='\'' ) i++; if( i ){ - sputf(out, "%.*s", i, z); + oputf("%.*s", i, z); z += i; } if( c=='\'' ){ - sputz(out, "'"); + oputz("'"); continue; } if( c==0 ){ @@ -1801,31 +1803,31 @@ static void output_quoted_escaped_string(FILE *out, const char *z){ } z++; if( c=='\n' ){ - sputz(out, zNL); + oputz(zNL); continue; } - sputz(out, zCR); + oputz(zCR); } - sputz(out, "'"); + oputz("'"); if( nCR ){ - sputf(out, ",'%s',char(13))", zCR); + oputf(",'%s',char(13))", zCR); } if( nNL ){ - sputf(out, ",'%s',char(10))", zNL); + oputf(",'%s',char(10))", zNL); } } - setTextMode(out, 1); + setTextMode(pfO, 1); } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ -static void output_c_string(FILE *out, const char *z){ +static void output_c_string(const char *z){ unsigned int c; static const char *zq = "\""; char ace[3] = "\\?"; char cbsSay; - sputz(out, zq); + oputz(zq); while( (c = *(z++))!= 0 ){ switch( c ){ case '\\': case '"': @@ -1839,28 +1841,28 @@ static void output_c_string(FILE *out, const char *z){ } if( cbsSay ){ ace[1] = cbsSay; - sputz(out, ace); + oputz(ace); }else if( !isprint(c&0xff) ){ - sputf(out, "\\%03o", c&0xff); + oputf("\\%03o", c&0xff); }else{ ace[1] = (char)c; - sputz(out, ace+1); + oputz(ace+1); } } - sputz(out, zq); + oputz(zq); } /* ** Output the given string as a quoted according to JSON quoting rules. */ -static void output_json_string(FILE *out, const char *z, i64 n){ +static void output_json_string(const char *z, i64 n){ unsigned int c; static const char *zq = "\""; char ace[3] = "\\?"; char cbsSay; if( z==0 ) z = ""; if( n<0 ) n = strlen(z); - sputz(out, zq); + oputz(zq); while( n-- ){ c = *(z++); switch( c ){ @@ -1876,22 +1878,22 @@ static void output_json_string(FILE *out, const char *z, i64 n){ } if( cbsSay ){ ace[1] = cbsSay; - sputz(out, ace); + oputz(ace); }else if( c<=0x1f ){ - sputf(out, "u%04x", c); + oputf("u%04x", c); }else{ ace[1] = (char)c; - sputz(out, ace+1); + oputz(ace+1); } } - sputz(out, zq); + oputz(zq); } /* ** Output the given string with characters that are special to ** HTML escaped. */ -static void output_html_string(FILE *out, const char *z){ +static void output_html_string(const char *z){ int i; if( z==0 ) z = ""; while( *z ){ @@ -1903,18 +1905,18 @@ static void output_html_string(FILE *out, const char *z){ && z[i]!='\''; i++){} if( i>0 ){ - sputf(out,"%.*s",i,z); + oputf("%.*s",i,z); } if( z[i]=='<' ){ - sputz(out,"<"); + oputz("<"); }else if( z[i]=='&' ){ - sputz(out,"&"); + oputz("&"); }else if( z[i]=='>' ){ - sputz(out,">"); + oputz(">"); }else if( z[i]=='\"' ){ - sputz(out,"""); + oputz("""); }else if( z[i]=='\'' ){ - sputz(out,"'"); + oputz("'"); }else{ break; } @@ -1952,9 +1954,8 @@ static const char needCsvQuote[] = { ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ - FILE *out = p->out; if( z==0 ){ - sputf(out,"%s",p->nullValue); + oputf("%s",p->nullValue); }else{ unsigned i; for(i=0; z[i]; i++){ @@ -1966,14 +1967,14 @@ static void output_csv(ShellState *p, const char *z, int bSep){ if( i==0 || strstr(z, p->colSeparator)!=0 ){ char *zQuoted = sqlite3_mprintf("\"%w\"", z); shell_check_oom(zQuoted); - sputz(out, zQuoted); + oputz(zQuoted); sqlite3_free(zQuoted); }else{ - sputz(out, z); + oputz(z); } } if( bSep ){ - sputz(p->out, p->colSeparator); + oputz(p->colSeparator); } } @@ -2081,16 +2082,16 @@ static int shellAuth( az[1] = zA2; az[2] = zA3; az[3] = zA4; - sputf(p->out, "authorizer: %s", azAction[op]); + oputf("authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - sputz(p->out, " "); + oputz(" "); if( az[i] ){ - output_c_string(p->out, az[i]); + output_c_string(az[i]); }else{ - sputz(p->out, "NULL"); + oputz("NULL"); } } - sputz(p->out, "\n"); + oputz("\n"); if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } @@ -2106,7 +2107,7 @@ static int shellAuth( ** sqlite3_complete() returns false, try to terminate the comment before ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c */ -static void printSchemaLine(FILE *out, const char *z, const char *zTail){ +static void printSchemaLine(const char *z, const char *zTail){ char *zToFree = 0; if( z==0 ) return; if( zTail==0 ) return; @@ -2128,16 +2129,16 @@ static void printSchemaLine(FILE *out, const char *z, const char *zTail){ } } if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ - sputf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); + oputf("CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); }else{ - sputf(out, "%s%s", z, zTail); + oputf("%s%s", z, zTail); } sqlite3_free(zToFree); } -static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ +static void printSchemaLineN(char *z, int n, const char *zTail){ char c = z[n]; z[n] = 0; - printSchemaLine(out, z, zTail); + printSchemaLine(z, zTail); z[n] = c; } @@ -2165,7 +2166,7 @@ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ if( zText==0 ) return; nText = strlen(zText); if( p->autoEQPtest ){ - sputf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); + oputf("%d,%d,%s\n", iEqpId, p2, zText); } pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); shell_check_oom(pNew); @@ -2213,7 +2214,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){ for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ pNext = eqp_next_row(p, iEqpId, pRow); z = pRow->zText; - sputf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); + oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); eqp_render_level(p, pRow->iEqpId); @@ -2233,13 +2234,13 @@ static void eqp_render(ShellState *p, i64 nCycle){ eqp_reset(p); return; } - sputf(p->out, "%s\n", pRow->zText+3); + oputf("%s\n", pRow->zText+3); p->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); }else if( nCycle>0 ){ - sputf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); + oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle); }else{ - sputz(p->out, "QUERY PLAN\n"); + oputz("QUERY PLAN\n"); } p->sGraph.zPrefix[0] = 0; eqp_render_level(p, 0); @@ -2255,13 +2256,13 @@ static int progress_handler(void *pClientData) { ShellState *p = (ShellState*)pClientData; p->nProgress++; if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - sputf(p->out, "Progress limit reached (%u)\n", p->nProgress); + oputf("Progress limit reached (%u)\n", p->nProgress); if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; return 1; } if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - sputf(p->out, "Progress %u\n", p->nProgress); + oputf("Progress %u\n", p->nProgress); } return 0; } @@ -2270,14 +2271,14 @@ static int progress_handler(void *pClientData) { /* ** Print N dashes */ -static void print_dashes(FILE *out, int N){ +static void print_dashes(int N){ const char zDash[] = "--------------------------------------------------"; const int nDash = sizeof(zDash) - 1; while( N>nDash ){ - sputz(out, zDash); + oputz(zDash); N -= nDash; } - sputf(out, "%.*s", N, zDash); + oputf("%.*s", N, zDash); } /* @@ -2290,15 +2291,15 @@ static void print_row_separator( ){ int i; if( nArg>0 ){ - sputz(p->out, zSep); - print_dashes(p->out, p->actualWidth[0]+2); + oputz(zSep); + print_dashes(p->actualWidth[0]+2); for(i=1; iout, zSep); - print_dashes(p->out, p->actualWidth[i]+2); + oputz(zSep); + print_dashes(p->actualWidth[i]+2); } - sputz(p->out, zSep); + oputz(zSep); } - sputz(p->out, "\n"); + oputz("\n"); } /* @@ -2328,10 +2329,10 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) sputz(p->out, p->rowSeparator); + if( p->cnt++>0 ) oputz(p->rowSeparator); for(i=0; iout,"%*s = %s%s", w, azCol[i], - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); + oputf("%*s = %s%s", w, azCol[i], + azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } @@ -2358,12 +2359,12 @@ static int shell_callback( /* If this is the first row seen, print out the headers */ if( p->cnt++==0 ){ for(i=0; iout, aWidth[i], azCol[ aMap[i] ]); - fputs(i==nArg-1 ? "\n" : " ", p->out); + utf8_width_print(aWidth[i], azCol[ aMap[i] ]); + oputz(i==nArg-1 ? "\n" : " "); } for(i=0; iout, aWidth[i]); - fputs(i==nArg-1 ? "\n" : " ", p->out); + print_dashes(aWidth[i]); + oputz(i==nArg-1 ? "\n" : " "); } } @@ -2381,17 +2382,17 @@ static int shell_callback( } if( i==iIndent && p->aiIndent && p->pStmt ){ if( p->iIndentnIndent ){ - sputf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); + oputf("%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } - utf8_width_print(p->out, w, zVal ? zVal : p->nullValue); - fputs(i==nArg-1 ? "\n" : zSep, p->out); + utf8_width_print(w, zVal ? zVal : p->nullValue); + oputz(i==nArg-1 ? "\n" : zSep); } break; } case MODE_Semi: { /* .schema and .fullschema output */ - printSchemaLine(p->out, azArg[0], ";\n"); + printSchemaLine(azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ @@ -2406,7 +2407,7 @@ static int shell_callback( if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 ){ - sputf(p->out, "%s;\n", azArg[0]); + oputf("%s;\n", azArg[0]); break; } z = sqlite3_mprintf("%s", azArg[0]); @@ -2439,7 +2440,7 @@ static int shell_callback( }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ - printSchemaLineN(p->out, z, j, "\n"); + printSchemaLineN(z, j, "\n"); j = 0; } } @@ -2448,7 +2449,7 @@ static int shell_callback( && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) ){ if( c=='\n' ) j--; - printSchemaLineN(p->out, z, j, "\n "); + printSchemaLineN(z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } @@ -2456,64 +2457,59 @@ static int shell_callback( } z[j] = 0; } - printSchemaLine(p->out, z, ";\n"); + printSchemaLine(z, ";\n"); sqlite3_free(z); break; } case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], - i==nArg-1 ? p->rowSeparator : p->colSeparator); + oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; inullValue; - sputz(p->out, z); - if( iout, p->colSeparator); - }else{ - sputz(p->out, p->rowSeparator); - } + oputz(z); + oputz((icolSeparator : p->rowSeparator); } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ - sputz(p->out,""); + oputz(""); for(i=0; iout,""); - output_html_string(p->out, azCol[i]); - sputz(p->out,"\n"); + oputz(""); + output_html_string(azCol[i]); + oputz("\n"); } - sputz(p->out,"\n"); + oputz("\n"); } if( azArg==0 ) break; - sputz(p->out,""); + oputz(""); for(i=0; iout,""); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - sputz(p->out,"\n"); + oputz(""); + output_html_string(azArg[i] ? azArg[i] : p->nullValue); + oputz("\n"); } - sputz(p->out,"\n"); + oputz("\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, p->colSeparator); + output_c_string(azCol[i] ? azCol[i] : ""); + if(icolSeparator); } - sputz(p->out, p->rowSeparator); + oputz(p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); - if(iout, p->colSeparator); + output_c_string(azArg[i] ? azArg[i] : p->nullValue); + if(icolSeparator); } - sputz(p->out, p->rowSeparator); + oputz(p->rowSeparator); break; } case MODE_Csv: { @@ -2522,57 +2518,57 @@ static int shell_callback( for(i=0; iout, p->rowSeparator); + oputz(p->rowSeparator); } if( nArg>0 ){ for(i=0; iout, p->rowSeparator); + oputz(p->rowSeparator); } setTextMode(p->out, 1); break; } case MODE_Insert: { if( azArg==0 ) break; - sputf(p->out,"INSERT INTO %s",p->zDestTable); + oputf("INSERT INTO %s",p->zDestTable); if( p->showHeader ){ - sputz(p->out,"("); + oputz("("); for(i=0; i0 ) sputz(p->out, ","); + if( i>0 ) oputz(","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); - sputf(p->out, z); + oputz(z); sqlite3_free(z); }else{ - sputf(p->out, "%s", azCol[i]); + oputf("%s", azCol[i]); } } - sputz(p->out,")"); + oputz(")"); } p->cnt++; for(i=0; iout, i>0 ? "," : " VALUES("); + oputz(i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - sputz(p->out,"NULL"); + oputz("NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - sputz(p->out, azArg[i]); + oputz(azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - sputz(p->out, "9.0e+999"); + oputz("9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - sputz(p->out, "-9.0e+999"); + oputz("-9.0e+999"); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -2580,21 +2576,21 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - sputz(p->out, z); + oputz(z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + output_hex_blob(pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - sputz(p->out, azArg[i]); + oputz(azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(azArg[i]); } } - sputz(p->out,");\n"); + oputz(");\n"); break; } case MODE_Json: { @@ -2606,37 +2602,37 @@ static int shell_callback( } p->cnt++; for(i=0; iout, azCol[i], -1); - sputz(p->out, ":"); + output_json_string(azCol[i], -1); + oputz(":"); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - sputz(p->out, "null"); + oputz("null"); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - sputz(p->out, "9.0e+999"); + oputz("9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - sputz(p->out, "-9.0e+999"); + oputz("-9.0e+999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - sputz(p->out, z); + oputz(z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_json_string(p->out, pBlob, nBlob); + output_json_string(pBlob, nBlob); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_json_string(p->out, azArg[i], -1); + output_json_string(azArg[i], -1); }else{ - sputz(p->out, azArg[i]); + oputz(azArg[i]); } if( iout, ","); + oputz(","); } } - sputz(p->out, "}"); + oputz("}"); break; } case MODE_Quote: { @@ -2644,7 +2640,7 @@ static int shell_callback( if( p->cnt==0 && p->showHeader ){ for(i=0; i0 ) fputs(p->colSeparator, p->out); - output_quoted_string(p->out, azCol[i]); + output_quoted_string(azCol[i]); } fputs(p->rowSeparator, p->out); } @@ -2652,24 +2648,24 @@ static int shell_callback( for(i=0; i0 ) fputs(p->colSeparator, p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - sputz(p->out,"NULL"); + oputz("NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - sputz(p->out, azArg[i]); + oputz(azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - sputz(p->out, z); + oputz(z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + output_hex_blob(pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - sputz(p->out, azArg[i]); + oputz(azArg[i]); }else{ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); } } fputs(p->rowSeparator, p->out); @@ -2678,17 +2674,17 @@ static int shell_callback( case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i0 ) sputz(p->out, p->colSeparator); - sputz(p->out, azCol[i] ? azCol[i] : ""); + if( i>0 ) oputz(p->colSeparator); + oputz(azCol[i] ? azCol[i] : ""); } - sputz(p->out, p->rowSeparator); + oputz(p->rowSeparator); } if( azArg==0 ) break; for(i=0; i0 ) sputz(p->out, p->colSeparator); - sputz(p->out, azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) oputz(p->colSeparator); + oputz(azArg[i] ? azArg[i] : p->nullValue); } - sputz(p->out, p->rowSeparator); + oputz(p->rowSeparator); break; } case MODE_EQP: { @@ -2767,7 +2763,7 @@ static void createSelftestTable(ShellState *p){ "DROP TABLE [_shell$self];" ,0,0,&zErrMsg); if( zErrMsg ){ - sputf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); + eputf("SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); @@ -2870,8 +2866,8 @@ static int run_table_dump_query( rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ char *zContext = shell_error_context(zSelect, p->db); - sputf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc, - sqlite3_errmsg(p->db), zContext); + oputf("/**** ERROR: (%d) %s *****/\n%s", + rc, sqlite3_errmsg(p->db), zContext); sqlite3_free(zContext); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; return rc; @@ -2880,23 +2876,22 @@ static int run_table_dump_query( nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ z = (const char*)sqlite3_column_text(pSelect, 0); - sputf(p->out, "%s", z); + oputf("%s", z); for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i)); + oputf(",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - sputz(p->out, "\n;\n"); + oputz("\n;\n"); }else{ - sputz(p->out, ";\n"); + oputz(";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ - sputf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, - sqlite3_errmsg(p->db)); + oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } return rc; @@ -2932,7 +2927,7 @@ static char *save_err_msg( /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ -static void displayLinuxIoStats(FILE *out){ +static void displayLinuxIoStats(void){ FILE *in; char z[200]; sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); @@ -2955,7 +2950,7 @@ static void displayLinuxIoStats(FILE *out){ for(i=0; iout, "%-36s %s\n", zLabel, zLine); + oputf("%-36s %s\n", zLabel, zLine); } /* @@ -3000,30 +2995,28 @@ static int display_stats( ){ int iCur; int iHiwtr; - FILE *out; if( pArg==0 || pArg->out==0 ) return 0; - out = pArg->out; if( pArg->pStmt && pArg->statsOn==2 ){ int nCol, i, x; sqlite3_stmt *pStmt = pArg->pStmt; char z[100]; nCol = sqlite3_column_count(pStmt); - sputf(out, "%-36s %d\n", "Number of output columns:", nCol); + oputf("%-36s %d\n", "Number of output columns:", nCol); for(i=0; istatsOn==3 ){ if( pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); - sputf(pArg->out, "VM-steps: %d\n", iCur); + oputf("VM-steps: %d\n", iCur); } return 0; } @@ -3060,77 +3053,68 @@ static int display_stats( iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); - sputf(pArg->out, - "Lookaside Slots Used: %d (max %d)\n", - iCur, iHiwtr); + oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Successful lookaside attempts: %d\n", - iHiwtr); + oputf("Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Lookaside failures due to size: %d\n", - iHiwtr); + oputf("Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Lookaside failures due to OOM: %d\n", - iHiwtr); + oputf("Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Pager Heap Usage: %d bytes\n", - iCur); + oputf("Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - sputf(pArg->out, "Page cache hits: %d\n", iCur); + oputf("Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - sputf(pArg->out, "Page cache misses: %d\n", iCur); + oputf("Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - sputf(pArg->out, "Page cache writes: %d\n", iCur); + oputf("Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - sputf(pArg->out, "Page cache spills: %d\n", iCur); + oputf("Page cache spills: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Schema Heap Usage: %d bytes\n", - iCur); + oputf("Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - sputf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", - iCur); + oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg->pStmt ){ int iHit, iMiss; iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); - sputf(pArg->out, "Fullscan Steps: %d\n", iCur); + oputf("Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - sputf(pArg->out, "Sort Operations: %d\n", iCur); + oputf("Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - sputf(pArg->out, "Autoindex Inserts: %d\n", iCur); + oputf("Autoindex Inserts: %d\n", iCur); iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); if( iHit || iMiss ){ - sputf(pArg->out, "Bloom filter bypass taken: %d/%d\n", - iHit, iHit+iMiss); + oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); } iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - sputf(pArg->out, "Virtual Machine Steps: %d\n", iCur); + oputf("Virtual Machine Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - sputf(pArg->out, "Reprepare operations: %d\n", iCur); + oputf("Reprepare operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - sputf(pArg->out, "Number of times run: %d\n", iCur); + oputf("Number of times run: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - sputf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); + oputf("Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ - displayLinuxIoStats(pArg->out); + displayLinuxIoStats(); #endif /* Do not remove this machine readable comment: extra-stats-output-here */ @@ -3511,17 +3495,17 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ /* Draw horizontal line N characters long using unicode box ** characters */ -static void print_box_line(FILE *out, int N){ +static void print_box_line(int N){ const char zDash[] = BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; const int nDash = sizeof(zDash) - 1; N *= 3; while( N>nDash ){ - sputz(out, zDash); + oputz(zDash); N -= nDash; } - sputf(out, "%.*s", N, zDash); + oputf("%.*s", N, zDash); } /* @@ -3536,15 +3520,15 @@ static void print_box_row_separator( ){ int i; if( nArg>0 ){ - sputz(p->out, zSep1); - print_box_line(p->out, p->actualWidth[0]+2); + oputz(zSep1); + print_box_line(p->actualWidth[0]+2); for(i=1; iout, zSep2); - print_box_line(p->out, p->actualWidth[i]+2); + oputz(zSep2); + print_box_line(p->actualWidth[i]+2); } - sputz(p->out, zSep3); + oputz(zSep3); } - sputz(p->out, "\n"); + oputz("\n"); } /* @@ -3807,11 +3791,11 @@ static void exec_prepared_stmt_columnar( for(i=0; iactualWidth[i]; if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(p->out, w, azData[i]); + utf8_width_print(w, azData[i]); fputs(i==nColumn-1?"\n":" ", p->out); } for(i=0; iout, p->actualWidth[i]); + print_dashes(p->actualWidth[i]); fputs(i==nColumn-1?"\n":" ", p->out); } } @@ -3825,8 +3809,8 @@ static void exec_prepared_stmt_columnar( for(i=0; iactualWidth[i]; n = strlenChar(azData[i]); - sputf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - sputz(p->out, i==nColumn-1?" |\n":" | "); + oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + oputz(i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "+"); break; @@ -3838,8 +3822,8 @@ static void exec_prepared_stmt_columnar( for(i=0; iactualWidth[i]; n = strlenChar(azData[i]); - sputf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - sputz(p->out, i==nColumn-1?" |\n":" | "); + oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + oputz(i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "|"); break; @@ -3848,13 +3832,13 @@ static void exec_prepared_stmt_columnar( colSep = " " BOX_13 " "; rowSep = " " BOX_13 "\n"; print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - sputz(p->out, BOX_13 " "); + oputz(BOX_13 " "); for(i=0; iactualWidth[i]; n = strlenChar(azData[i]); - sputf(p->out, "%*s%s%*s%s", - (w-n)/2, "", azData[i], (w-n+1)/2, "", - i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); + oputf("%*s%s%*s%s", + (w-n)/2, "", azData[i], (w-n+1)/2, "", + i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); } print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); break; @@ -3862,28 +3846,28 @@ static void exec_prepared_stmt_columnar( } for(i=nColumn, j=0; icMode!=MODE_Column ){ - sputz(p->out, p->cMode==MODE_Box?BOX_13" ":"| "); + oputz(p->cMode==MODE_Box?BOX_13" ":"| "); } z = azData[i]; if( z==0 ) z = p->nullValue; w = p->actualWidth[j]; if( p->colWidth[j]<0 ) w = -w; - utf8_width_print(p->out, w, z); + utf8_width_print(w, z); if( j==nColumn-1 ){ - sputz(p->out, rowSep); + oputz(rowSep); if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1cMode==MODE_Table ){ print_row_separator(p, nColumn, "+"); }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); }else if( p->cMode==MODE_Column ){ - sputz(p->out, "\n"); + oputz("\n"); } } j = -1; if( seenInterrupt ) goto columnar_end; }else{ - sputz(p->out, colSep); + oputz(colSep); } } if( p->cMode==MODE_Table ){ @@ -3893,7 +3877,7 @@ static void exec_prepared_stmt_columnar( } columnar_end: if( seenInterrupt ){ - sputz(p->out, "Interrupt\n"); + oputz("Interrupt\n"); } nData = (nRow+1)*nColumn; for(i=0; iout; int bVerbose = pState->expert.bVerbose; rc = sqlite3_expert_analyze(p, pzErr); @@ -4042,8 +4025,8 @@ static int expertFinish( if( bVerbose ){ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); - sputz(out, "-- Candidates -----------------------------\n"); - sputf(out, "%s\n", zCand); + oputz("-- Candidates -----------------------------\n"); + oputf("%s\n", zCand); } for(i=0; ishellFlgs & SHFLG_DumpNoSys)!=0; if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) sputz(p->out, "DELETE FROM sqlite_sequence;\n"); + if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) sputz(p->out, "ANALYZE sqlite_schema;\n"); + if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n"); }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ @@ -4446,7 +4429,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ - sputz(p->out, "PRAGMA writable_schema=ON;\n"); + oputz("PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( @@ -4454,11 +4437,11 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); shell_check_oom(zIns); - sputf(p->out, "%s\n", zIns); + oputf("%s\n", zIns); sqlite3_free(zIns); return 0; }else{ - printSchemaLine(p->out, zSql, ";\n"); + printSchemaLine(zSql, ";\n"); } if( cli_strcmp(zType, "table")==0 ){ @@ -4516,7 +4499,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->mode = p->cMode = MODE_Insert; rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - sputz(p->out, "/****** CORRUPTION ERROR *******/\n"); + oputz("/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); @@ -4547,9 +4530,9 @@ static int run_schema_dump_query( if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - sputz(p->out, "/****** CORRUPTION ERROR *******/\n"); + oputz("/****** CORRUPTION ERROR *******/\n"); if( zErr ){ - sputf(p->out, "/****** %s ******/\n", zErr); + oputf("/****** %s ******/\n", zErr); sqlite3_free(zErr); zErr = 0; } @@ -4558,7 +4541,7 @@ static int run_schema_dump_query( sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ - sputf(p->out, "/****** ERROR: %s ******/\n", zErr); + oputf("/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } @@ -5154,7 +5137,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ shell_check_oom(a); memset(a, 0, n); if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ - sputz(stderr, "invalid pagesize\n"); + eputz("invalid pagesize\n"); goto readHexDb_error; } for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ @@ -6072,6 +6055,17 @@ static void tryToClone(ShellState *p, const char *zNewDb){ close_db(newDb); } +/* +** Change the output stream (file or pipe or console) to something else. +*/ +static void output_redir(ShellState *p, FILE *pfNew){ + if( p->out != stdout ) eputz("Output already redirected.\n"); + else{ + p->out = pfNew; + setOutputStream(pfNew); + } +} + /* ** Change the output file back to stdout. ** @@ -6114,6 +6108,7 @@ static void output_reset(ShellState *p){ } p->outfile[0] = 0; p->out = stdout; + setOutputStream(stdout); } /* @@ -6198,28 +6193,28 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ memcpy(aHdr, pb, 100); sqlite3_finalize(pStmt); }else{ - sputz(stderr, "unable to read database header\n"); + eputz("unable to read database header\n"); sqlite3_finalize(pStmt); return 1; } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; - sputf(p->out, "%-20s %d\n", "database page size:", i); - sputf(p->out, "%-20s %d\n", "write format:", aHdr[18]); - sputf(p->out, "%-20s %d\n", "read format:", aHdr[19]); - sputf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); + oputf("%-20s %d\n", "database page size:", i); + oputf("%-20s %d\n", "write format:", aHdr[18]); + oputf("%-20s %d\n", "read format:", aHdr[19]); + oputf("%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; iout, "%-20s %u", aField[i].zName, val); + oputf("%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) sputz(p->out, " (utf8)"); - if( val==2 ) sputz(p->out, " (utf16le)"); - if( val==3 ) sputz(p->out, " (utf16be)"); + if( val==1 ) oputz(" (utf8)"); + if( val==2 ) oputz(" (utf16le)"); + if( val==3 ) oputz(" (utf16be)"); } } - sputz(p->out, "\n"); + oputz("\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -6232,11 +6227,11 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p->db, zSql); sqlite3_free(zSql); - sputf(p->out, "%-20s %d\n", aQuery[i].zName, val); + oputf("%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); - sputf(p->out, "%-20s %u\n", "data version", iDataVersion); + oputf("%-20s %u\n", "data version", iDataVersion); return 0; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ @@ -6481,7 +6476,6 @@ static int lintFkeyIndexes( int nArg /* Number of entries in azArg[] */ ){ sqlite3 *db = pState->db; /* Database handle to query "main" db of */ - FILE *out = pState->out; /* Stream to write non-error output to */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ @@ -6614,15 +6608,15 @@ static int lintFkeyIndexes( && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ - sputf(out, "-- Parent table %s\n", zParent); + oputf("-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ - sputf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); + oputf("%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ - sputf(out, "%s/* no extra indexes required for %s -> %s */\n", + oputf("%s/* no extra indexes required for %s -> %s */\n", zIndent, zFrom, zTarget ); } @@ -7119,15 +7113,15 @@ static int arListCommand(ArCommand *pAr){ shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - sputf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + oputf("%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( pAr->bVerbose ){ - sputf(pAr->p->out, "%s % 10d %s %s\n", + oputf("%s % 10d %s %s\n", sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); }else{ - sputf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + oputf("%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -7154,7 +7148,7 @@ static int arRemoveCommand(ArCommand *pAr){ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - sputf(pAr->p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); @@ -7167,7 +7161,7 @@ static int arRemoveCommand(ArCommand *pAr){ } } if( zErr ){ - sputf(stdout, "ERROR: %s\n", zErr); + sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */ sqlite3_free(zErr); } } @@ -7231,11 +7225,11 @@ static int arExtractCommand(ArCommand *pAr){ j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); sqlite3_bind_int(pSql, j, i); if( pAr->bDryRun ){ - sputf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + oputf("%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( i==0 && pAr->bVerbose ){ - sputf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + oputf("%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -7255,7 +7249,7 @@ static int arExtractCommand(ArCommand *pAr){ static int arExecSql(ArCommand *pAr, const char *zSql){ int rc; if( pAr->bDryRun ){ - sputf(pAr->p->out, "%s\n", zSql); + oputf("%s\n", zSql); rc = SQLITE_OK; }else{ char *zErr = 0; @@ -7436,7 +7430,7 @@ static int arDotCommand( } cmd.db = 0; if( cmd.bDryRun ){ - sputf(pState->out, "-- open database '%s'%s\n", cmd.zFile, + oputf("-- open database '%s'%s\n", cmd.zFile, eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); } rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, @@ -8137,7 +8131,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int eTxn = sqlite3_txn_state(p->db, azName[i*2]); int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); const char *z = azName[i*2+1]; - sputf(p->out, "%s: %s %s%s\n", + oputf("%s: %s %s%s\n", azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", eTxn==SQLITE_TXN_NONE ? "" : eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); @@ -8179,7 +8173,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); } sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - sputf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ @@ -8267,8 +8261,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - sputz(p->out, "PRAGMA foreign_keys=OFF;\n"); - sputz(p->out, "BEGIN TRANSACTION;\n"); + oputz("PRAGMA foreign_keys=OFF;\n"); + oputz("BEGIN TRANSACTION;\n"); } p->writableSchema = 0; p->showHeader = 0; @@ -8299,13 +8293,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_free(zLike); if( p->writableSchema ){ - sputz(p->out, "PRAGMA writable_schema=OFF;\n"); + oputz("PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - sputz(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); } p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; @@ -8442,10 +8436,9 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all file-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - sputz(p->out, "Available file-controls:\n"); + oputz("Available file-controls:\n"); for(i=0; iout, " .filectrl %s %s\n", - aCtrl[i].zCtrlName, aCtrl[i].zUsage); + oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; goto meta_command_exit; @@ -8512,7 +8505,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg!=2 ) break; sqlite3_file_control(p->db, zSchema, filectrl, &z); if( z ){ - sputf(p->out, "%s\n", z); + oputf("%s\n", z); sqlite3_free(z); } isOk = 2; @@ -8526,19 +8519,19 @@ static int do_meta_command(char *zLine, ShellState *p){ } x = -1; sqlite3_file_control(p->db, zSchema, filectrl, &x); - sputf(p->out,"%d\n", x); + oputf("%d\n", x); isOk = 2; break; } } } if( isOk==0 && iCtrl>=0 ){ - sputf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ char zBuf[100]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - sputf(p->out, "%s\n", zBuf); + oputf("%s\n", zBuf); } }else @@ -8579,15 +8572,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( doStats==0 ){ - sputz(p->out, "/* No STAT tables available */\n"); + oputz("/* No STAT tables available */\n"); }else{ - sputz(p->out, "ANALYZE sqlite_schema;\n"); + oputz("ANALYZE sqlite_schema;\n"); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - sputz(p->out, "ANALYZE sqlite_schema;\n"); + oputz("ANALYZE sqlite_schema;\n"); } }else @@ -8605,7 +8598,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg>=2 ){ n = showHelp(p->out, azArg[1]); if( n==0 ){ - sputf(p->out, "Nothing matches '%s'\n", azArg[1]); + oputf("Nothing matches '%s'\n", azArg[1]); } }else{ showHelp(p->out, 0); @@ -8649,7 +8642,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zTable==0 ){ zTable = z; }else{ - sputf(p->out, "ERROR: extra argument: \"%s\". Usage:\n", z); + oputf("ERROR: extra argument: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } @@ -8670,13 +8663,13 @@ static int do_meta_command(char *zLine, ShellState *p){ xRead = csv_read_one_field; useOutputMode = 0; }else{ - sputf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); + oputf("ERROR: unknown option: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } } if( zTable==0 ){ - sputf(p->out, "ERROR: missing %s argument. Usage:\n", + oputf("ERROR: missing %s argument. Usage:\n", zFile==0 ? "FILE" : "TABLE"); showHelp(p->out, "import"); goto meta_command_exit; @@ -8742,12 +8735,12 @@ static int do_meta_command(char *zLine, ShellState *p){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; - sputz(p->out, "Column separator "); - output_c_string(p->out, zSep); - sputz(p->out, ", row separator "); + oputz("Column separator "); + output_c_string(zSep); + oputz(", row separator "); zSep[0] = sCtx.cRowSep; - output_c_string(p->out, zSep); - sputz(p->out, "\n"); + output_c_string(zSep); + oputz("\n"); } sCtx.z = sqlite3_malloc64(120); if( sCtx.z==0 ){ @@ -8800,7 +8793,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); if( eVerbose>=1 ){ - sputf(p->out, "%s\n", zCreate); + oputf("%s\n", zCreate); } rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); if( rc ){ @@ -8835,7 +8828,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zSql[j++] = ')'; zSql[j] = 0; if( eVerbose>=2 ){ - sputf(p->out, "Insert using: %s\n", zSql); + oputf("Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); if( rc ){ @@ -8904,8 +8897,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( eVerbose>0 ){ - sputf(p->out, - "Added %d rows with %d errors using %d lines of input\n", + oputf("Added %d rows with %d errors using %d lines of input\n", sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } }else @@ -9195,13 +9187,12 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - sputf(p->out, - "current output mode: %s --wrap %d --wordwrap %s --%squote\n", + oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n", modeDescr[p->mode], p->cmOpts.iWrap, p->cmOpts.bWordWrap ? "on" : "off", p->cmOpts.bQuote ? "" : "no"); }else{ - sputf(p->out, "current output mode: %s\n", modeDescr[p->mode]); + oputf("current output mode: %s\n", modeDescr[p->mode]); } zMode = modeDescr[p->mode]; } @@ -9420,7 +9411,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ eMode = 'e'; /* text editor */ }else{ - sputf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); + oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; @@ -9432,7 +9423,7 @@ static int do_meta_command(char *zLine, ShellState *p){ break; } }else{ - sputf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); + oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; sqlite3_free(zFile); @@ -9473,28 +9464,28 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifdef SQLITE_OMIT_POPEN eputz("Error: pipes are not supported in this OS\n"); rc = 1; - p->out = stdout; + output_redir(p, stdout); #else - p->out = popen(zFile + 1, "w"); - if( p->out==0 ){ + FILE *pfPipe = popen(zFile + 1, "w"); + if( pfPipe==0 ){ eputf("Error: cannot open pipe \"%s\"\n", zFile + 1); - p->out = stdout; rc = 1; }else{ - if( zBom ) sputz(p->out, zBom); + output_redir(p, pfPipe); + if( zBom ) oputz(zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif }else{ - p->out = output_file_open(zFile, bTxtMode); - if( p->out==0 ){ + FILE *pfFile = output_file_open(zFile, bTxtMode); + if( pfFile==0 ){ if( cli_strcmp(zFile,"off")!=0 ){ eputf("Error: cannot write to \"%s\"\n", zFile); } - p->out = stdout; rc = 1; } else { - if( zBom ) sputz(p->out, zBom); + output_redir(p, pfFile); + if( zBom ) oputz(zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } @@ -9535,7 +9526,7 @@ static int do_meta_command(char *zLine, ShellState *p){ "SELECT key, quote(value) " "FROM temp.sqlite_parameters;", -1, &pStmt, 0); while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - sputf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), + oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), sqlite3_column_text(pStmt,1)); } sqlite3_finalize(pStmt); @@ -9580,7 +9571,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rx!=SQLITE_OK ){ - sputf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); + oputf("Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); pStmt = 0; rc = 1; @@ -9609,10 +9600,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i1 ) sputz(p->out, " "); - sputz(p->out, azArg[i]); + if( i>1 ) oputz(" "); + oputz(azArg[i]); } - sputz(p->out, "\n"); + oputz("\n"); }else #ifndef SQLITE_OMIT_PROGRESS_CALLBACK @@ -9919,7 +9910,7 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&sSelect, "sql IS NOT NULL" " ORDER BY snum, rowid", 0); if( bDebug ){ - sputf(p->out, "SQL: %s;\n", sSelect.z); + oputf("SQL: %s;\n", sSelect.z); }else{ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); } @@ -10043,7 +10034,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_enable(pSession->p, ii); - sputf(p->out, "session %s enable flag = %d\n", pSession->zName, ii); + oputf("session %s enable flag = %d\n", pSession->zName, ii); } }else @@ -10078,7 +10069,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_indirect(pSession->p, ii); - sputf(p->out, "session %s indirect flag = %d\n", pSession->zName, ii); + oputf("session %s indirect flag = %d\n", pSession->zName, ii); } }else @@ -10090,7 +10081,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=1 ) goto session_syntax_error; if( pAuxDb->nSession ){ ii = sqlite3session_isempty(pSession->p); - sputf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii); + oputf("session %s isempty flag = %d\n", pSession->zName, ii); } }else @@ -10099,7 +10090,7 @@ static int do_meta_command(char *zLine, ShellState *p){ */ if( cli_strcmp(azCmd[0],"list")==0 ){ for(i=0; inSession; i++){ - sputf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); + oputf("%d %s\n", i, pAuxDb->aSession[i].zName); } }else @@ -10149,7 +10140,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, v; for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); + oputf("%s: %d 0x%x\n", azArg[i], v, v); } } if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ @@ -10158,7 +10149,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); - sputz(p->out, zBuf); + oputz(zBuf); } } }else @@ -10234,7 +10225,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sputf(stdout, "%d: %s %s\n", tno, zOp, zSql); } if( cli_strcmp(zOp,"memo")==0 ){ - sputf(p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else if( cli_strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; @@ -10243,18 +10234,18 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ - sputf(p->out, "Result: %s\n", str.z); + oputf("Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; - sputf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); + oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); sqlite3_free(zErrMsg); }else if( cli_strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; - sputf(p->out, "%d: Expected: [%s]\n", tno, zAns); - sputf(p->out, "%d: Got: [%s]\n", tno, str.z); + oputf("%d: Expected: [%s]\n", tno, zAns); + oputf("%d: Got: [%s]\n", tno, str.z); } } else{ @@ -10266,7 +10257,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); - sputf(p->out, "%d errors out of %d tests\n", nErr, nTest); + oputf("%d errors out of %d tests\n", nErr, nTest); }else if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ @@ -10392,7 +10383,7 @@ static int do_meta_command(char *zLine, ShellState *p){ freeText(&sQuery); freeText(&sSql); if( bDebug ){ - sputf(p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else{ shell_exec(p, zSql, 0); } @@ -10422,7 +10413,7 @@ static int do_meta_command(char *zLine, ShellState *p){ "' OR ') as query, tname from tabcols group by tname)" , zRevText); shell_check_oom(zRevText); - if( bDebug ) sputf(p->out, "%s\n", zRevText); + if( bDebug ) oputf("%s\n", zRevText); lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); if( lrc!=SQLITE_OK ){ /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the @@ -10435,7 +10426,7 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); sqlite3_stmt *pCheckStmt; lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); - if( bDebug ) sputf(p->out, "%s\n", zGenQuery); + if( bDebug ) oputf("%s\n", zGenQuery); if( lrc!=SQLITE_OK ){ rc = 1; }else{ @@ -10494,46 +10485,46 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; goto meta_command_exit; } - sputf(p->out, "%12.12s: %s\n","echo", + oputf("%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); - sputf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); - sputf(p->out, "%12.12s: %s\n","explain", + oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); + oputf("%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); - sputf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); + oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]); if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - sputf(p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", + oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", modeDescr[p->mode], p->cmOpts.iWrap, p->cmOpts.bWordWrap ? "on" : "off", p->cmOpts.bQuote ? "" : "no"); }else{ - sputf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); + oputf("%12.12s: %s\n","mode", modeDescr[p->mode]); } - sputf(p->out, "%12.12s: ", "nullvalue"); - output_c_string(p->out, p->nullValue); - sputz(p->out, "\n"); - sputf(p->out,"%12.12s: %s\n","output", + oputf("%12.12s: ", "nullvalue"); + output_c_string(p->nullValue); + oputz("\n"); + oputf("%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); - sputf(p->out,"%12.12s: ", "colseparator"); - output_c_string(p->out, p->colSeparator); - sputz(p->out, "\n"); - sputf(p->out,"%12.12s: ", "rowseparator"); - output_c_string(p->out, p->rowSeparator); - sputz(p->out, "\n"); + oputf("%12.12s: ", "colseparator"); + output_c_string(p->colSeparator); + oputz("\n"); + oputf("%12.12s: ", "rowseparator"); + output_c_string(p->rowSeparator); + oputz("\n"); switch( p->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; case 2: zOut = "stmt"; break; case 3: zOut = "vmstep"; break; } - sputf(p->out, "%12.12s: %s\n","stats", zOut); - sputf(p->out, "%12.12s: ", "width"); + oputf("%12.12s: %s\n","stats", zOut); + oputf("%12.12s: ", "width"); for (i=0;inWidth;i++) { - sputf(p->out, "%d ", p->colWidth[i]); + oputf("%d ", p->colWidth[i]); } - sputz(p->out, "\n"); - sputf(p->out, "%12.12s: %s\n", "filename", + oputz("\n"); + oputf("%12.12s: %s\n", "filename", p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); }else @@ -10651,9 +10642,9 @@ static int do_meta_command(char *zLine, ShellState *p){ for(i=0; iout, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); + oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - sputz(p->out, "\n"); + oputz("\n"); } } @@ -10728,10 +10719,10 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all test-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - sputz(p->out, "Available test-controls:\n"); + oputz("Available test-controls:\n"); for(i=0; iout, " .testctrl %s %s\n", + oputf(" .testctrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; @@ -10865,7 +10856,7 @@ static int do_meta_command(char *zLine, ShellState *p){ case SQLITE_TESTCTRL_SEEK_COUNT: { u64 x = 0; rc2 = sqlite3_test_control(testctrl, p->db, &x); - sputf(p->out, "%llu\n", x); + oputf("%llu\n", x); isOk = 3; break; } @@ -10896,11 +10887,11 @@ static int do_meta_command(char *zLine, ShellState *p){ int val = 0; rc2 = sqlite3_test_control(testctrl, -id, &val); if( rc2!=SQLITE_OK ) break; - if( id>1 ) sputz(p->out, " "); - sputf(p->out, "%d: %d", id, val); + if( id>1 ) oputz(" "); + oputf("%d: %d", id, val); id++; } - if( id>1 ) sputz(p->out, "\n"); + if( id>1 ) oputz("\n"); isOk = 3; } break; @@ -10916,12 +10907,12 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( isOk==0 && iCtrl>=0 ){ - sputf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ - sputf(p->out, "%d\n", rc2); + oputf("%d\n", rc2); }else if( isOk==2 ){ - sputf(p->out, "0x%08x\n", rc2); + oputf("0x%08x\n", rc2); } }else #endif /* !defined(SQLITE_UNTESTABLE) */ @@ -11084,21 +11075,21 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; - sputf(p->out, "SQLite %s %s\n" /*extra-version-info*/, + oputf("SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); #if SQLITE_HAVE_ZLIB - sputf(p->out, "zlib version %s\n", zlibVersion()); + oputf("zlib version %s\n", zlibVersion()); #endif #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) #if defined(__clang__) && defined(__clang_major__) - sputf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." + oputf("clang-" CTIMEOPT_VAL(__clang_major__) "." CTIMEOPT_VAL(__clang_minor__) "." CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); #elif defined(_MSC_VER) - sputf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); + oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); #elif defined(__GNUC__) && defined(__VERSION__) - sputf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); + oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz); #endif }else @@ -11108,10 +11099,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ - sputf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); - sputf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - sputf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - sputf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + oputf("vfs.zName = \"%s\"\n", pVfs->zName); + oputf("vfs.iVersion = %d\n", pVfs->iVersion); + oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); + oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); } } }else @@ -11123,13 +11114,13 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); } for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ - sputf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, + oputf("vfs.zName = \"%s\"%s\n", pVfs->zName, pVfs==pCurrent ? " <--- CURRENT" : ""); - sputf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - sputf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - sputf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + oputf("vfs.iVersion = %d\n", pVfs->iVersion); + oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); + oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - sputz(p->out, "-----------------------------------\n"); + oputz("-----------------------------------\n"); } } }else @@ -11140,7 +11131,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ - sputf(p->out, "%s\n", zVfsName); + oputf("%s\n", zVfsName); sqlite3_free(zVfsName); } } @@ -11364,13 +11355,13 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); - sputf(p->out, "%s\n", zLineBuf); + oputf("%s\n", zLineBuf); } return 0; } static void echo_group_input(ShellState *p, const char *zDo){ - if( ShellHasFlag(p, SHFLG_Echo) ) sputf(p->out, "%s\n", zDo); + if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); } #ifdef SQLITE_SHELL_FIDDLE @@ -12408,16 +12399,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #elif SHELL_CON_TRANSLATE==2 zCharset = " (MBCS console I/O)"; #endif - oputf( - "SQLite version %s %.19s%s\n" /*extra-version-info*/ - "Enter \".help\" for usage hints.\n", - sqlite3_libversion(), sqlite3_sourceid(), zCharset - ); + oputf("SQLite version %s %.19s%s\n" /*extra-version-info*/ + "Enter \".help\" for usage hints.\n", + sqlite3_libversion(), sqlite3_sourceid(), zCharset); if( warnInmemoryDb ){ oputz("Connected to a "); printBold("transient in-memory database"); - oputz(".\nUse \".open FILENAME\" to reopen on a " - "persistent database.\n"); + oputz(".\nUse \".open FILENAME\" to reopen on a" + " persistent database.\n"); } zHistory = getenv("SQLITE_HISTORY"); if( zHistory ){ From fdbd9119d4f013d8a80c3b3e3d7362e9aca51390 Mon Sep 17 00:00:00 2001 From: larrybr Date: Sun, 12 Nov 2023 19:57:23 +0000 Subject: [PATCH 177/347] Add sqlite3_x86.exe unversioned binary. FossilOrigin-Name: a731cdddbb99dbd3f9d1875cad5094239b78969c84fe4c56ecd63e33a5874e3f --- manifest | 11 ++++++----- manifest.uuid | 2 +- sqlite3_x86.exe | Bin 0 -> 2658816 bytes 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100755 sqlite3_x86.exe diff --git a/manifest b/manifest index f6dcc5d4ab..c8c28dfb67 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\ssetOutputStream()\sto\sdesignate\simplicit\soutput\sfor\soput{z,f}()\semit\sfunctions,\sand\suse\sthem\sextensively. -D 2023-11-12T03:58:15.704 +C Add\ssqlite3_x86.exe\sunversioned\sbinary. +D 2023-11-12T19:57:23.673 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -657,6 +657,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a +F sqlite3_x86.exe 871926af39a0a54f90df3ad8a4270a15cb01be59af42e81afd220525812be3d3 x F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2 F src/alter.c 30c2333b8bb3af71e4eb9adeadee8aa20edb15917ed44b8422e5cd15f3dfcddc F src/analyze.c d4cc28738c29e009640ec20ebb6936ba6fcefff0d11aa93398d9bb9a5ead6c1f @@ -2141,8 +2142,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 06ef96a8233c6b6ca2653a0b780b3a401cab2b77594125f4f1c54b269c22a9f9 -R 0d28bc41b168f5a19d7cddd544d794a5 +P 7850fb98a19d0ae3535367de3bca9e50408a2c21504c5772947fc39f287aa830 +R 1cd3ad3b17c1d449dd66c6c243573a41 U larrybr -Z 3d0184f630f762e14e234f872c761cb8 +Z 1619f1a730674def90d8e58700e55e2d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ee60b43346..771a927936 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7850fb98a19d0ae3535367de3bca9e50408a2c21504c5772947fc39f287aa830 \ No newline at end of file +a731cdddbb99dbd3f9d1875cad5094239b78969c84fe4c56ecd63e33a5874e3f \ No newline at end of file diff --git a/sqlite3_x86.exe b/sqlite3_x86.exe new file mode 100755 index 0000000000000000000000000000000000000000..6112fbbae012ba75bb016c5cd5ef8da75c57b4b8 GIT binary patch literal 2658816 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 (^PAyf81jF?Y?)r>gKc+={<>3 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 z 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 z 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 z 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%0Ase0)QiMpU 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

    ;N-w}=lKx+j7!3!*L&fnlMd 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

    W{q^&u`@O5?fo`B!kOx{Es* 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 z

    +6_kQD>i8o$6B)C5$yhz!6 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

    *s<%of~|B{j{cBYB_ALO1EzN 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

    )tWGp#J5>s`-C^ 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%0A6OOI-y@9|X2yL`zvZnH$DIkmjcwvp2Et|^y` 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 z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l 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%0DWyX=6|Lb!{WAhs?(J zSkc~}=p!{YdtWvp9te#_Sp^4KcbkhqR-7Rexp;z zhR7`7zUzh0qV_yHa86~H&*`|oC%WV{MB{63k*=Qz??b^)Pdkbmq11t6CX8!G z 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 Date: Mon, 13 Nov 2023 05:24:00 +0000 Subject: [PATCH 178/347] Enhance console_io to permit emits limited in various ways, such as valid UTF-8, upto control chars, or with counted limits, all getting away from 0-termination as the sole limit. In CLI, use this capability to avoid certain emit-chars-singly procedures that were breaking up UTF-8 characters. This fixes broken json mode output (on Windows) and (maybe) C-literal-like emits. FossilOrigin-Name: 906c5c4082e30b7a0d07df89a42566461e2113507c5a0b339827ca0822b8fe84 --- ext/consio/console_io.c | 141 +++++++++++++++++++++++++++++----------- ext/consio/console_io.h | 41 ++++++++---- manifest | 16 ++--- manifest.uuid | 2 +- src/shell.c.in | 44 +++++++++++-- 5 files changed, 180 insertions(+), 64 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index b882a66b36..c1eaaa506e 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -278,22 +278,20 @@ SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ #undef setModeFlushQ #if SHELL_CON_TRANSLATE -/* Write plain 0-terminated output to stream known as reaching console. */ -static int conioZstrOut(PerStreamTags *ppst, const char *z){ +/* Write buffer cBuf as output to stream known to reach console, +** limited to ncTake char's. Return ncTake on success, else 0. */ +static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ int rv = 0; - if( z!=NULL && *z!=0 ){ - int nc; - int nwc; - nc = (int)strlen(z); - nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, 0,0); + if( z!=NULL ){ + int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); if( nwc > 0 ){ WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); if( zw!=NULL ){ - nwc = MultiByteToWideChar(CP_UTF8,0, z,nc, zw,nwc); + nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); if( nwc > 0 ){ /* Translation from UTF-8 to UTF-16, then WCHARs out. */ if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ - rv = nc; + rv = ncTake; } } sqlite3_free(zw); @@ -306,9 +304,11 @@ static int conioZstrOut(PerStreamTags *ppst, const char *z){ /* For {f,o,e}PrintfUtf8() when stream is known to reach console. */ static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ char *z = sqlite3_vmprintf(zFormat, ap); - int rv = conioZstrOut(ppst, z); - sqlite3_free(z); - return rv; + if( z ){ + int rv = conZstrEmit(ppst, z, (int)strlen(z)); + sqlite3_free(z); + return rv; + }else return 0; } #endif /* SHELL_CON_TRANSLATE */ @@ -428,7 +428,7 @@ SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ if( pstReachesConsole(ppst) ){ int rv; maybeSetupAsConsole(ppst, 1); - rv = conioZstrOut(ppst, z); + rv = conZstrEmit(ppst, z, (int)strlen(z)); if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); return rv; }else { @@ -444,7 +444,7 @@ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ PerStreamTags pst; /* Needed only for heretofore unknown streams. */ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); #if SHELL_CON_TRANSLATE - if( pstReachesConsole(ppst) ) return conioZstrOut(ppst, z); + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { #endif return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); @@ -458,7 +458,7 @@ SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ PerStreamTags pst; /* Needed only for heretofore unknown streams. */ PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); #if SHELL_CON_TRANSLATE - if( pstReachesConsole(ppst) ) return conioZstrOut(ppst, z); + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { #endif return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); @@ -467,33 +467,96 @@ SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ #endif } -#if 0 -/* Next 3 functions could be optimized to avoid console mode futzing. */ -SQLITE_INTERNAL_LINKAGE int fPutcUtf8(int ch, FILE *pfO){ - if( (ch & ~0x7f) != 0 ) return 0; - else{ - char ac[2] = "?"; - ac[0] = (char)ch; - return (fPutsUtf8(ac, pfO) > 0); - } -} -SQLITE_INTERNAL_LINKAGE int oPutcUtf8(int ch){ - if( (ch & ~0x7f) != 0 ) return 0; - else{ - char ac[2] = "?"; - ac[0] = (char)ch; - return (oPutsUtf8(ac) > 0); - } -} -SQLITE_INTERNAL_LINKAGE int ePutcUtf8(int ch){ - if( (ch & ~0x7f) != 0 ) return 0; - else{ - char ac[2] = "?"; - ac[0] = (char)ch; - return (ePutsUtf8(ac) > 0); +/* Skip over as much z[] input char sequence as is valid UTF-8, +** limited per nAccept char's or whole characters and containing +** no char cn such that ((1<=0 => char count, nAccept<0 => character + */ +static const char* zSkipValidUtf8(const char *z, int nAccept, long ccm){ + int ng = (nAccept<0)? -nAccept : 0; + const char *pcLimit = (nAccept>=0)? z+nAccept : 0; + while( (pcLimit)? (z 0) ){ + char c = *z; + if( (c & 0x80) == 0 ){ + if( ccm != 0L && c < 0x20 && ((1L<= pcLimit ) return z; + else{ + char ct = *zt++; + if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ + /* Trailing bytes are too few, too many, or invalid. */ + return z; + } + } + } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ + z = zt; + } } + return z; } + +SQLITE_INTERNAL_LINKAGE int +fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept, long ctrlMask){ + const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); + int ncConsume = (int)(zPast - cBuf); + if( pfO == 0 ) return ncConsume; +#if SHELL_CON_TRANSLATE + PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); + if( pstReachesConsole(ppst) ){ + int rv; + maybeSetupAsConsole(ppst, 1); + rv = conZstrEmit(ppst, cBuf, ncConsume); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + return rv; + }else { #endif + return (int)fwrite(cBuf, 1, ncConsume, pfO); +#if SHELL_CON_TRANSLATE + } +#endif +} + +SQLITE_INTERNAL_LINKAGE int +oPutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ + FILE *pfOut; + const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); + int ncConsume = (int)(zPast - cBuf); + PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +#if SHELL_CON_TRANSLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, ncConsume); + }else { +#endif + return (int)fwrite(cBuf, 1, ncConsume, pfOut); +#if SHELL_CON_TRANSLATE + } +#endif +} + +SQLITE_INTERNAL_LINKAGE int +ePutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ + FILE *pfErr; + const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); + int ncConsume = (int)(zPast - cBuf); + PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); +#if SHELL_CON_TRANSLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, ncConsume); + }else { +#endif + return (int)fwrite(cBuf, 1, ncConsume, pfErr); +#if SHELL_CON_TRANSLATE + } +#endif +} SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; diff --git a/ext/consio/console_io.h b/ext/consio/console_io.h index 9a049ad6a6..52a8d63ebe 100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@ -136,20 +136,39 @@ SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z); /* Like fPutsUtf8 except stream is always the designated error. */ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); -#if 0 /* -** Emit output like fputc(), with appropriate translation(s). -** This is not strictly needed on fully UTF-8-aware platforms. -** It exists for sake of orthogonality and output designation. +** Emit output like fPutsUtf8(), except that the length of the +** accepted char or character sequence may be limited by nAccept. ** -** The routine returns an error for non-ASCII character input. +** The magnitude and sign of nAccept control what nAccept limits. +** If positive, nAccept limits the number of char's accepted. +** If negative, it limits the number of valid input characters. +** Obtain the behavior of {f,o,e}PutsUtf8 with nAccept==INT_MAX. +** +** Returns the number of accepted char values. +** +** When ctrlMask!=0, it specifies a set of control characters not +** accepted as input, so that cBuf[abs(N)] on return will be one +** of the non-accepted characters unless nAccept limited the scan. +** Each bit in ctrlMask, 1< z ) oPutbUtf8(z, (int)(pcEnd-z), 0); + if( (c = *pcEnd)==0 ) break; + ++pcEnd; switch( c ){ case '\\': case '"': cbsSay = (char)c; @@ -1848,6 +1869,7 @@ static void output_c_string(const char *z){ ace[1] = (char)c; oputz(ace+1); } + z = pcEnd; } oputz(zq); } @@ -1856,14 +1878,26 @@ static void output_c_string(const char *z){ ** Output the given string as a quoted according to JSON quoting rules. */ static void output_json_string(const char *z, i64 n){ - unsigned int c; + char c; static const char *zq = "\""; + static long ctrlMask = ~0L; + static const char *zDQBS = "\"\\"; + const char *pcLimit; char ace[3] = "\\?"; char cbsSay; + if( z==0 ) z = ""; - if( n<0 ) n = strlen(z); + pcLimit = z + ((n<0)? strlen(z) : (size_t)n); oputz(zq); - while( n-- ){ + while( z < pcLimit ){ + const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); + const char *pcPast = z + fPutbUtf8(0, z, (int)(pcLimit-z), ctrlMask); + const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; + if( pcEnd > z ){ + oPutbUtf8(z, (int)(pcEnd-z), 0); + z = pcEnd; + } + if( z >= pcLimit ) break; c = *(z++); switch( c ){ case '"': case '\\': From c270dad95d64bf0ec66ae421d977532806db2edf Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 13 Nov 2023 12:53:47 +0000 Subject: [PATCH 179/347] Slight cleanup of length-limited strpbrk() replacement, and eliminate strpbrk() call. FossilOrigin-Name: 97e2c9621e3e01048e33da63ca4146ededa33cf6adefa996fa49b8c6a8d555b5 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 26 +++++++++++++++----------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 1769d0a02b..e985a98d08 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sconsole_io\sto\spermit\semits\slimited\sin\svarious\sways,\ssuch\sas\svalid\sUTF-8,\supto\scontrol\schars,\sor\swith\scounted\slimits,\sall\sgetting\saway\sfrom\s0-termination\sas\sthe\ssole\slimit.\sIn\sCLI,\suse\sthis\scapability\sto\savoid\scertain\semit-chars-singly\sprocedures\sthat\swere\sbreaking\sup\sUTF-8\scharacters.\sThis\sfixes\sbroken\sjson\smode\soutput\s(on\sWindows)\sand\s(maybe)\sC-literal-like\semits. -D 2023-11-13T05:24:00.316 +C Slight\scleanup\sof\slength-limited\sstrpbrk()\sreplacement,\sand\seliminate\sstrpbrk()\scall. +D 2023-11-13T12:53:47.904 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -728,7 +728,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in adb9a11d6c7c0d2260ad381468d22f4d0d68240a061e5553fce73e7e1ad40410 +F src/shell.c.in 0bc051f506f812be03351673aa206cc87cfac47ccc04e60191c984bb79e38265 F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2142,8 +2142,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 a731cdddbb99dbd3f9d1875cad5094239b78969c84fe4c56ecd63e33a5874e3f -R 1643036426e25a342daef2af41a2624b +P 906c5c4082e30b7a0d07df89a42566461e2113507c5a0b339827ca0822b8fe84 +R ea455f9417313759f4a37b1578d0b70c U larrybr -Z 98b08e37ed82f681bcbba6df6ae3a4d3 +Z 3d1eb938bb91a620d97eb2c42b47a7fe # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ba0909751a..9359c7a103 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -906c5c4082e30b7a0d07df89a42566461e2113507c5a0b339827ca0822b8fe84 \ No newline at end of file +97e2c9621e3e01048e33da63ca4146ededa33cf6adefa996fa49b8c6a8d555b5 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 63ab46ad5a..bb542fd1c4 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1819,18 +1819,22 @@ static void output_quoted_escaped_string(const char *z){ setTextMode(pfO, 1); } -/* Like strpbrk, but with an optional limit on search length. */ -static const char *anyOfInStr(const char *s, const char *zAny, i64 n){ - if( n<0 ) return strpbrk(s, zAny); - else{ - void *pcFirst = (void*)(s+(size_t)n); - while(*zAny){ - void *pc = memchr(s, *zAny&0xff, n); - if( pc && pc < pcFirst ) pcFirst = pc; - ++zAny; +/* +** Find earliest of chars within s specified in zAny. +** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. +*/ +static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){ + if( ns == ~0 ) ns = strlen(s); + const char *pcFirst = 0; + while(*zAny){ + const char *pc = (const char*)memchr(s, *zAny&0xff, ns); + if( pc ){ + pcFirst = pc; + ns = pcFirst - s; } - return (const char*)(((const char*)pcFirst < s+(size_t)n)? pcFirst : 0); + ++zAny; } + return pcFirst; } /* ** Output the given string as a quoted according to C or TCL quoting rules. @@ -1844,7 +1848,7 @@ static void output_c_string(const char *z){ char cbsSay; oputz(zq); while( *z!=0 ){ - char *pcDQBSRO = strpbrk(z, zDQBSRO); + const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~0); const char *pcPast = z + fPutbUtf8(0, z, INT_MAX, ctrlMask); const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; if( pcEnd > z ) oPutbUtf8(z, (int)(pcEnd-z), 0); From 41d4b7bdeec2c982e75dbd83cd7c89148c7a6b64 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 13 Nov 2023 13:01:01 +0000 Subject: [PATCH 180/347] Fix var-intro after executable code departure from old C. FossilOrigin-Name: 08996f4fd52f5742e77225ce3da2ac43ea5cc44bb0d8d21126b6d624273842a5 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index e985a98d08..33cb884f0d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Slight\scleanup\sof\slength-limited\sstrpbrk()\sreplacement,\sand\seliminate\sstrpbrk()\scall. -D 2023-11-13T12:53:47.904 +C Fix\svar-intro\safter\sexecutable\scode\sdeparture\sfrom\sold\sC. +D 2023-11-13T13:01:01.956 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -728,7 +728,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in 0bc051f506f812be03351673aa206cc87cfac47ccc04e60191c984bb79e38265 +F src/shell.c.in 6317180fce95b4d994423047648676e9d8c4bd7b82ab8ad98644a9e9a81ff24c F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2142,8 +2142,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 906c5c4082e30b7a0d07df89a42566461e2113507c5a0b339827ca0822b8fe84 -R ea455f9417313759f4a37b1578d0b70c +P 97e2c9621e3e01048e33da63ca4146ededa33cf6adefa996fa49b8c6a8d555b5 +R 9e09f295654f16b3a3c0e5e6e0bcae0d U larrybr -Z 3d1eb938bb91a620d97eb2c42b47a7fe +Z 4863af021829bb44bd729f706b66926d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9359c7a103..e0b6ffab58 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -97e2c9621e3e01048e33da63ca4146ededa33cf6adefa996fa49b8c6a8d555b5 \ No newline at end of file +08996f4fd52f5742e77225ce3da2ac43ea5cc44bb0d8d21126b6d624273842a5 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index bb542fd1c4..03cadd639a 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1824,8 +1824,8 @@ static void output_quoted_escaped_string(const char *z){ ** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated. */ static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){ - if( ns == ~0 ) ns = strlen(s); const char *pcFirst = 0; + if( ns == ~0 ) ns = strlen(s); while(*zAny){ const char *pc = (const char*)memchr(s, *zAny&0xff, ns); if( pc ){ From e108029332f7d403961b3f5c9bbe1c8fe3a9e6d5 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 13 Nov 2023 14:29:12 +0000 Subject: [PATCH 181/347] Add new fts5 API xQueryToken(). FossilOrigin-Name: 828566392b3ea8db603cb1ae5eccbc8ac035efaa284bc7c15ba89874f634aec9 --- ext/fts5/fts5.h | 2 +- ext/fts5/fts5Int.h | 2 ++ ext/fts5/fts5_expr.c | 26 +++++++++++++++++++ ext/fts5/fts5_main.c | 16 +++++++++++- ext/fts5/fts5_tcl.c | 18 +++++++++++++ ext/fts5/test/fts5origintext.test | 43 +++++++++++++++++++++++++++++++ manifest | 22 ++++++++-------- manifest.uuid | 2 +- 8 files changed, 117 insertions(+), 14 deletions(-) diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 730a2fb5e3..5a2008882f 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -263,7 +263,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ void *(*xUserData)(Fts5Context*); diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 4aa578559b..df25fd9ffa 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -773,6 +773,8 @@ int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); +int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); + /******************************************* ** The fts5_expr.c API above this point is used by the other hand-written ** C code in this module. The interfaces below this point are called by diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index a2c6320719..80387a90fd 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3145,3 +3145,29 @@ int sqlite3Fts5ExprPhraseCollist( return rc; } + +/* +** Does the work of the fts5_api.xQueryToken() API method. +*/ +int sqlite3Fts5ExprQueryToken( + Fts5Expr *pExpr, + int iPhrase, + int iToken, + const char **ppOut, + int *pnOut +){ + Fts5ExprPhrase *pPhrase = 0; + + if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ + return SQLITE_RANGE; + } + pPhrase = pExpr->apExprPhrase[iPhrase]; + if( iToken<0 || iToken>=pPhrase->nTerm ){ + return SQLITE_RANGE; + } + + *ppOut = pPhrase->aTerm[iToken].pTerm; + *pnOut = pPhrase->aTerm[iToken].nFullTerm; + return SQLITE_OK; +} + diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 6e86ca5951..7ddc7b6fcf 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -2323,13 +2323,24 @@ static int fts5ApiPhraseFirstColumn( return rc; } +static int fts5ApiQueryToken( + Fts5Context* pCtx, + int iPhrase, + int iToken, + const char **ppOut, + int *pnOut +){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); +} + static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { - 2, /* iVersion */ + 3, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, @@ -2349,6 +2360,9 @@ static const Fts5ExtensionApi sFts5Api = { fts5ApiPhraseNext, fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, + fts5ApiQueryToken, + 0, + 0 }; /* diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c index fb4bea8e9e..a9390adc9e 100644 --- a/ext/fts5/fts5_tcl.c +++ b/ext/fts5/fts5_tcl.c @@ -244,6 +244,8 @@ static int SQLITE_TCLAPI xF5tApi( { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */ { "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */ { "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */ + + { "xQueryToken", 2, "IPHRASE ITERM" }, /* 19 */ { 0, 0, 0} }; @@ -500,6 +502,22 @@ static int SQLITE_TCLAPI xF5tApi( break; } + CASE(18, "xQueryToken") { + const char *pTerm = 0; + int nTerm = 0; + int iPhrase = 0; + int iTerm = 0; + + if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[3], &iTerm) ) return TCL_ERROR; + rc = p->pApi->xQueryToken(p->pFts, iPhrase, iTerm, &pTerm, &nTerm); + if( rc==SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(pTerm, nTerm)); + } + + break; + } + default: assert( 0 ); break; diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 3fd5f17e7b..2c9a4ba5a3 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -157,5 +157,48 @@ do_execsql_test 3.1.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3} do_execsql_test 3.1.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10} +#------------------------------------------------------------------------- +# +reset_db +sqlite3_fts5_register_origintext db +proc querytoken {cmd iPhrase iToken} { + set txt [$cmd xQueryToken $iPhrase $iToken] + string map [list "\0" "."] $txt +} +sqlite3_fts5_create_function db querytoken querytoken + +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize='origintext unicode61', tokendata=1 + ); + INSERT INTO ft VALUES('one two three four'); +} + +do_execsql_test 3.1 { + SELECT rowid, querytoken(ft, 0, 0) FROM ft('TwO') +} {1 two.TwO} +do_execsql_test 3.2 { + SELECT rowid, querytoken(ft, 0, 0) FROM ft('one TWO ThreE') +} {1 one} +do_execsql_test 3.3 { + SELECT rowid, querytoken(ft, 1, 0) FROM ft('one TWO ThreE') +} {1 two.TWO} +do_execsql_test 3.4 { + SELECT rowid, querytoken(ft, 0, 2) FROM ft('"one TWO ThreE"') +} {1 three.ThreE} + +do_catchsql_test 3.5 { + SELECT rowid, querytoken(ft, 0, 3) FROM ft('"one TWO ThreE"') +} {1 SQLITE_RANGE} +do_catchsql_test 3.6 { + SELECT rowid, querytoken(ft, 1, 0) FROM ft('"one TWO ThreE"') +} {1 SQLITE_RANGE} +do_catchsql_test 3.7 { + SELECT rowid, querytoken(ft, -1, 0) FROM ft('"one TWO ThreE"') +} {1 SQLITE_RANGE} + + + + finish_test diff --git a/manifest b/manifest index baff022d13..4a539764cf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sdeclarations\sfor\snew\sAPI\sfunctions. -D 2023-11-08T14:55:20.854 +C Add\snew\sfts5\sAPI\sxQueryToken(). +D 2023-11-13T14:29:12.382 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -87,17 +87,17 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7 F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h 68256dd94eaba3e874762a63578922fd62a5ca87c76a28f7636effee4d9bd781 -F ext/fts5/fts5Int.h a21eb1cf036ac9eb943e45ed307762901ea86f0159bf0848baa2079a112ddc2f +F ext/fts5/fts5.h e27cdb10e38d87cb041dcb56cef97addf7d902aeab07e84e7102f5fc65d3357c +F ext/fts5/fts5Int.h 19b198459a2791415919428d44ebf4c830b59b2da6f27f8faaffe39a876b7ecf F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c fd091d0558fda2517602ed5886ec615ce3e1bd76fb0bb0e5d1aa85ba8db287a8 +F ext/fts5/fts5_expr.c 69c81af515ce1cedccf093c7c76f8b3b4f24bafbfb1d03a431af9f5c69a81834 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 F ext/fts5/fts5_index.c 145723e22ffee28dbe2a24933e74ad998d32419223c0ddb8506a1f0c39b952c4 -F ext/fts5/fts5_main.c a07ed863b8bd9e6fefb62db2fd40a3518eb30a5f7dcfda5be915dd2db45efa2f +F ext/fts5/fts5_main.c ddac85dbd28167af81f64e568bfe9020bf9708d650de207d1465ed19938316d1 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d -F ext/fts5/fts5_tcl.c 0d2bb0ff7bf6ee136015be118167f0bd956ddd05a8f02c68bd34299b50648f9f +F ext/fts5/fts5_tcl.c 71641a0c5693c64acfad9d10e64475ec92d9f464d06ba7fd350552de373586d8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b F ext/fts5/fts5_tokenize.c 83cfcede3898001cab84432a36ce1503e3080cf9b1c682b022ec82e267ea4c13 @@ -188,7 +188,7 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 646df137f1aa5b3d7032374ebe82bfdbe88d9f825d73ce8d44bead480317a9c5 +F ext/fts5/test/fts5origintext.test 8296984d268d1d20f85c9de316f422ffb6ebc12020d3f8a0a18144d6ca7b347f F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2143,8 +2143,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 3a869cf1f84b0e9bdcc4de53685430ab41eafacbba1ca7b87e727aa98811c6c5 -R 9e06a9e0b44056d476c02db915884372 +P b8a48cc18c94d15017f898c820fdd784efbaac20d7a45c4d97269333e8f2ec60 +R 177f59b3c48b3865f7f595e0ec855966 U dan -Z 6c0dbde9f9a0f05c4a27f8bd01924beb +Z ac818dc8fd8dc9007ff77eae486bea2e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cf6088cc7c..f6858a05cd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b8a48cc18c94d15017f898c820fdd784efbaac20d7a45c4d97269333e8f2ec60 \ No newline at end of file +828566392b3ea8db603cb1ae5eccbc8ac035efaa284bc7c15ba89874f634aec9 \ No newline at end of file From 7f7d7bea01e5782b566854ec3fc03c1b65dc52a5 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 13 Nov 2023 14:58:37 +0000 Subject: [PATCH 182/347] JNI: add sqlite3_bind_nio_buffer() and initial tests for binding ByteBuffer objects as blobs on JVMs which have JNI support for nio buffers. FossilOrigin-Name: b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f --- ext/jni/src/c/sqlite3-jni.c | 79 ++++++++++++++++---- ext/jni/src/c/sqlite3-jni.h | 8 ++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 75 ++++++++++++++++++- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 58 ++++++++++++-- manifest | 18 ++--- manifest.uuid | 2 +- 6 files changed, 211 insertions(+), 29 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index fafb2ab5fd..e3814825b1 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -870,6 +870,31 @@ static jbyte * s3jni__jbyteArray_bytes2(JNIEnv * const env, jbyteArray jBA, jsiz #define s3jni_jbyteArray_commit(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_COMMIT) +/* +** If jbb is-a java.nio.Buffer object and the JNI environment +** supports it, *pBuf is set to the buffer's memory and *pN is set to +** its length. If jbb is NULL, not a Buffer, or the JNI environment +** does not support that operation, *pBuf is set to 0 and *pN is set +** to 0. +** +** Note that the length of the buffer can be larger than SQLITE_LIMIT +** but this function does not know what byte range of the buffer is +** required so cannot check for that violation. The caller is required +** to ensure that any to-be-bind()ed range fits within SQLITE_LIMIT. + */ +/*static*/ void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){ + *pBuf = 0; + *pN = 0; + if( jbb ){ + *pBuf = (*env)->GetDirectBufferAddress(env, jbb); + *pN = *pBuf ? (jint)(*env)->GetDirectBufferCapacity(env, jbb) : 0 + /* why the Java limits the buffer length to int but the JNI API + uses a jlong for the length is a mystery. */; + } +} +#define s3jni_get_nio_buffer(JOBJ,vpOut,jpOut) \ + s3jni__get_nio_buffer(env,(JOBJ),(vpOut),(jpOut)) + /* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. @@ -1479,13 +1504,13 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, ** argument is a Java sqlite3 object, as this operation only has void ** pointers to work with. */ -#define PtrGet_T(T,OBJ) (T*)NativePointerHolder_get(OBJ, S3JniNph(T)) -#define PtrGet_sqlite3(OBJ) PtrGet_T(sqlite3, OBJ) -#define PtrGet_sqlite3_backup(OBJ) PtrGet_T(sqlite3_backup, OBJ) -#define PtrGet_sqlite3_blob(OBJ) PtrGet_T(sqlite3_blob, OBJ) -#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) -#define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ) -#define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ) +#define PtrGet_T(T,JOBJ) (T*)NativePointerHolder_get((JOBJ), S3JniNph(T)) +#define PtrGet_sqlite3(JOBJ) PtrGet_T(sqlite3, (JOBJ)) +#define PtrGet_sqlite3_backup(JOBJ) PtrGet_T(sqlite3_backup, (JOBJ)) +#define PtrGet_sqlite3_blob(JOBJ) PtrGet_T(sqlite3_blob, (JOBJ)) +#define PtrGet_sqlite3_context(JOBJ) PtrGet_T(sqlite3_context, (JOBJ)) +#define PtrGet_sqlite3_stmt(JOBJ) PtrGet_T(sqlite3_stmt, (JOBJ)) +#define PtrGet_sqlite3_value(JOBJ) PtrGet_T(sqlite3_value, (JOBJ)) /* ** LongPtrGet_T(X,Y) expects X to be an unqualified sqlite3 struct ** type name and Y to be a native pointer to such an object in the @@ -1505,12 +1530,12 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, ** a difference of microseconds (i.e. below our testing measurement ** threshold) might add up. */ -#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) -#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,JLongAsPtr) -#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,JLongAsPtr) -#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,JLongAsPtr) -#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,JLongAsPtr) -#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,JLongAsPtr) +#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)((JLongAsPtr))) +#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,(JLongAsPtr)) +#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,(JLongAsPtr)) +#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,(JLongAsPtr)) +#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,(JLongAsPtr)) +#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,(JLongAsPtr)) /* ** Extracts the new S3JniDb instance from the free-list, or allocates ** one if needed, associates it with pDb, and returns. Returns NULL @@ -2408,6 +2433,34 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( return (jint)rc; } +S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer, + jint iBegin, jint iN +){ + sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt); + void * pBuf = 0; + jint nBuf = 0; + jlong iEnd = 0; + if( !SJG.g.cByteBuffer || !pStmt || iBegin<0 ){ + return (jint)SQLITE_MISUSE; + } + s3jni_get_nio_buffer(jBuffer, &pBuf, &nBuf); + if( !pBuf || iBegin>=nBuf ){ + return (jint)sqlite3_bind_null(pStmt, ndx); + } + assert( nBuf > 0 ); + assert( iBegin < nBuf ); + iEnd = iN<0 ? nBuf - iBegin : iBegin + iN; + if( iEnd>(jlong)nBuf ) iEnd = nBuf-iBegin; + if( iEnd-iBegin >(jlong)SQLITE_MAX_LENGTH ){ + return SQLITE_MISUSE; + } + assert( iBegin>=0 ); + assert( iEnd > iBegin ); + return (jint)sqlite3_bind_blob(pStmt, (int)ndx, pBuf + iBegin, + (int)(iEnd - iBegin), SQLITE_TRANSIENT); +} + S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val ){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 51d49bba3c..f306c53fcb 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -881,6 +881,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1int64 JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1java_1object (JNIEnv *, jclass, jlong, jint, jobject); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_bind_nio_buffer + * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;ILjava/nio/ByteBuffer;II)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1nio_1buffer + (JNIEnv *, jclass, jobject, jint, jobject, jint, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_bind_null diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 2dc238ce3e..569d8f4a97 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -215,7 +215,7 @@ public final class CApi { If n is negative, SQLITE_MISUSE is returned. If n>data.length then n is silently truncated to data.length. */ - static int sqlite3_bind_blob( + public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n ){ return sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, n); @@ -229,6 +229,28 @@ public final class CApi { : sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, data.length); } + /** + Convenience overload which is a simple proxy for + sqlite3_bind_nio_buffer(). + */ + public static int sqlite3_bind_blob( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data, + int begin, int n + ){ + return sqlite3_bind_nio_buffer(stmt, ndx, data, begin, n); + } + + /** + Convenience overload which is equivalant to passing its arguments + to sqlite3_bind_nio_buffer() with the values 0 and -1 for the + final two arguments. + */ + public static int sqlite3_bind_blob( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data + ){ + return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1); + } + private static native int sqlite3_bind_double( @NotNull long ptrToStmt, int ndx, double v ); @@ -261,6 +283,57 @@ public final class CApi { @NotNull long ptrToStmt, int ndx, @Nullable Object o ); + /** + Binds the contents of the given buffer object as a blob. + + The byte range of the buffer may be restricted by providing a + start index and a number of bytes. beginPos may not be negative + but a negative howMany is interpretated as the remainder of the + buffer past the given start position. + + If beginPos+howMany would extend past the end of the buffer, the + range is silently truncated to fit the buffer. + + If any of the following are true, this function behaves like + sqlite3_bind_null(): the buffer is null, beginPos is past the end + of the buffer, howMany is 0, or the calculated slice of the blob + has a length of 0. + + If ndx is out of range, it returns SQLITE_RANGE, as documented + for sqlite3_bind_blob(). If any other arguments are invalid or + if sqlite3_jni_supports_nio() is false then SQLITE_MISUSE is + returned. Note that this function is bound (as it were) by the + SQLITE_LIMIT_LENGTH constraint and SQLITE_MISUSE is returned if + that's violated. + + This function does not modify the buffer's streaming-related + cursors. + + If the buffer is modified in a separate thread while this + operation is running, results are undefined and will likely + result in corruption of the bound data or a segmentation fault. + + Design note: this function should arguably take a java.nio.Buffer + instead of ByteBuffer, but it can only operate on "direct" + buffers and the only such class offered by Java is (apparently) + ByteBuffer. + + @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html + */ + public static native int sqlite3_bind_nio_buffer( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data, + int beginPos, int howMany + ); + + /** + Convenience overload which binds the given buffer's entire contents. + */ + public static int sqlite3_bind_nio_buffer( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data + ){ + return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1); + } + /** Binds the given object at the given index. If o is null then this behaves like sqlite3_bind_null(). diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 3ac58c67d3..0587eb6318 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -38,6 +38,14 @@ import java.util.concurrent.Future; @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @interface SingleThreadOnly{} +/** + Annotation for Tester1 tests which must only be run if JNI-level support for + java.nio.Buffer is available. +*/ +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) +@interface RequiresNioBuffer{} + public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; @@ -557,6 +565,45 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } + @RequiresNioBuffer + private void testBindByteBuffer(){ + sqlite3 db = createNewDb(); + execSql(db, "CREATE TABLE t(a)"); + sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); + java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10); + buf.put((byte)0x31)/*note that we'll skip this one*/ + .put((byte)0x32) + .put((byte)0x33) + .put((byte)0x34); + int rc = sqlite3_bind_blob(stmt, 1, buf, -1, 0); + affirm( SQLITE_MISUSE==rc ); + rc = sqlite3_bind_blob(stmt, 1, buf, 1, 3); + affirm( 0==rc ); + rc = sqlite3_step(stmt); + affirm(SQLITE_DONE == rc); + sqlite3_finalize(stmt); + stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); + int n = 0; + int total = 0; + while( SQLITE_ROW == sqlite3_step(stmt) ){ + byte[] blob = sqlite3_column_blob(stmt, 0); + affirm(3 == blob.length); + int i = 0; + for(byte b : blob){ + affirm( i<=3 ); + affirm(b == buf.get(1 + i++)); + total += b; + } + ++n; + } + sqlite3_finalize(stmt); + affirm(1 == n); + affirm(total == 0x32 + 0x33 + 0x34); + /* TODO: these tests need to be much more extensive to check the + begin range handling. */ + sqlite3_close_v2(db); + } + private void testSql(){ sqlite3 db = createNewDb(); sqlite3_stmt stmt = prepare(db, "SELECT 1"); @@ -1877,18 +1924,19 @@ public class Tester1 implements Runnable { if( forceFail ){ testMethods.add(m); } + }else if( m.isAnnotationPresent( RequiresNioBuffer.class ) + && !sqlite3_jni_supports_nio() ){ + outln("Skipping test for lack JNI nio.Buffer support: ",name,"()\n"); + ++nSkipped; }else if( !m.isAnnotationPresent( ManualTest.class ) ){ if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ - if( 0==nSkipped++ ){ - out("Skipping tests in multi-thread mode:"); - } - out(" "+name+"()"); + out("Skipping test in multi-thread mode: ",name,"()\n"); + ++nSkipped; }else if( name.startsWith("test") ){ testMethods.add(m); } } } - if( nSkipped>0 ) out("\n"); } final long timeStart = System.currentTimeMillis(); diff --git a/manifest b/manifest index a6ae666a58..47b34117a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\swrapper1:\swhen\schecking\sfor\san\sout-of-bounds\sstatement\scolumn\sindex,\sperform\sthe\sis-statement-finalized\scheck\sbefore\sthe\srange\scheck\sso\sthat\sthe\sformer\sexception\strumps\sthe\slatter. -D 2023-11-11T14:50:01.933 +C JNI:\sadd\ssqlite3_bind_nio_buffer()\sand\sinitial\stests\sfor\sbinding\sByteBuffer\sobjects\sas\sblobs\son\sJVMs\swhich\shave\sJNI\ssupport\sfor\snio\sbuffers. +D 2023-11-13T14:58:37.421 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,8 +241,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9cd020d4 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26 -F ext/jni/src/c/sqlite3-jni.h 891444578550a7aa69fe5e0dedb3e6dedad752501ba99801f17797be51796934 +F ext/jni/src/c/sqlite3-jni.c a04d0d77e7391a69f7f8ca4b38e24de59b3a8f61610f2e91698c190c07283850 +F ext/jni/src/c/sqlite3-jni.h 2848299f845d36b4b6123d360e7a4eb960d040637a10158079af49f4ded16453 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java bd4a6490548f913bf9719443dee3d8a233f920ed1614b622738527d746e00f5d +F ext/jni/src/org/sqlite/jni/capi/CApi.java 5ef54290c17dca46d7f24001ac3b689559e1b37ee40d06b88fa5315d64863789 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java b1a0c015d92a8d0c07a8f6751e9b057557cec9d803e002d48ee5f3b9963abd55 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4ec21172917f641787767443f418854329bf9b9779807b644e000dac1ec77013 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -2139,8 +2139,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 a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd -R 78a63cfa87011ebb24c6cac260db14c9 +P 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d +R 7292f27854539ba720bc8a8fcd0c273f U stephan -Z ad92dad6ace48bd20f7cf67cbd6f4f40 +Z 1881d5bf6b59348a7807616e897efc0f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4865106033..693d450f8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d \ No newline at end of file +b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f \ No newline at end of file From 5ed4ba0f7d696561beee4387c94a5d0ca775207c Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 13 Nov 2023 15:59:27 +0000 Subject: [PATCH 183/347] Cure many warnings from gcc, clang and CL.exe. FossilOrigin-Name: 29ea2a3aadd34facc45a2e9f9a567bac16fca76beb019b879e2611433c85bbb3 --- ext/consio/console_io.c | 94 ++++++++++++++++++++++++++++------------- ext/consio/console_io.h | 6 ++- manifest | 16 +++---- manifest.uuid | 2 +- src/shell.c.in | 21 +++++---- 5 files changed, 88 insertions(+), 51 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index c1eaaa506e..379059b807 100755 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -77,20 +77,24 @@ typedef struct PerStreamTags { #endif #if SHELL_CON_TRANSLATE -# define CI_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ +# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ {0,0,0,0}, SHELL_INVALID_FILE_PTR } #else -# define CI_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } +# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } #endif /* Quickly say whether a known output is going to the console. */ -static short pstReachesConsole(PerStreamTags *ppst){ #if SHELL_CON_TRANSLATE +static short pstReachesConsole(PerStreamTags *ppst){ +# if SHELL_CON_TRANSLATE return (ppst->hx != INVALID_HANDLE_VALUE); -#else +# else return (ppst->reachesConsole != 0); -#endif +# endif } +#else +# define pstReachesConsole(ppst) 0 +#endif #if SHELL_CON_TRANSLATE static void restoreConsoleArb(PerStreamTags *ppst){ @@ -140,26 +144,23 @@ static short isValidStreamInfo(PerStreamTags *ppst){ } static ConsoleInfo consoleInfo = { - { /* pstSetup */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, - { /* pstDesignated[] */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER }, + { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, + { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, SAC_NoConsole /* sacSetup */ }; -#undef SHELL_INVALID_FILE_PTR -#undef CI_INITIALIZER SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; -static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ #if SHELL_CON_TRANSLATE +static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ if( pstReachesConsole(ppst) ){ DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; SetConsoleMode(ppst->hx, cm); } -#else - (void)ppst; - (void)odir; -#endif } +#else +# define maybeSetupAsConsole(ppst,odir) +#endif SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ #if SHELL_CON_TRANSLATE @@ -206,6 +207,7 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ #endif } +#ifdef CONSIO_INPUT_REDIR /* Say whether given FILE* is among those known, via either ** consoleClassifySetup() or set{Output,Error}Stream, as ** readable, and return an associated PerStreamTags pointer @@ -221,6 +223,7 @@ static PerStreamTags * isKnownReadable(FILE *pf){ } while( apst[++ix] != 0 ); return apst[ix]; } +#endif /* Say whether given FILE* is among those known, via either ** consoleClassifySetup() or set{Output,Error}Stream, as @@ -256,9 +259,11 @@ static FILE *designateEmitStream(FILE *pf, unsigned chix){ SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ return designateEmitStream(pf, 1); } +#ifdef CONSIO_SET_ERROR_STREAM SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ return designateEmitStream(pf, 2); } +#endif #if SHELL_CON_TRANSLATE static void setModeFlushQ(FILE *pf, short bFlush, int mode){ @@ -312,7 +317,7 @@ static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ } #endif /* SHELL_CON_TRANSLATE */ - +#ifdef CONSIO_GET_EMIT_STREAM static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, PerStreamTags *ppst){ PerStreamTags *rv = isKnownWritable(pf); @@ -321,6 +326,7 @@ static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, streamOfConsole(pf, ppst); return ppst; } +#endif /* Get stream info, either for designated output or error stream when ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. @@ -359,9 +365,12 @@ SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ va_list ap; int rv; FILE *pfOut; - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +#if SHELL_CON_TRANSLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); - +#else + getEmitStreamInfo(1, &pst, &pfOut); +#endif va_start(ap, zFormat); #if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ){ @@ -380,9 +389,12 @@ SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ va_list ap; int rv; FILE *pfErr; - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +#if SHELL_CON_TRANSLATE PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); - +#else + getEmitStreamInfo(2, &pst, &pfErr); +#endif va_start(ap, zFormat); #if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ){ @@ -400,9 +412,11 @@ SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ va_list ap; int rv; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ #if SHELL_CON_TRANSLATE - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); +#else + getEmitStreamInfo(0, &pst, &pfO); #endif va_start(ap, zFormat); @@ -422,9 +436,13 @@ SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ } SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ #if SHELL_CON_TRANSLATE - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); +#else + getEmitStreamInfo(0, &pst, &pfO); +#endif +#if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ){ int rv; maybeSetupAsConsole(ppst, 1); @@ -441,8 +459,12 @@ SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ FILE *pfErr; - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +#if SHELL_CON_TRANSLATE PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); +#else + getEmitStreamInfo(2, &pst, &pfErr); +#endif #if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { @@ -455,8 +477,12 @@ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ FILE *pfOut; - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +#if SHELL_CON_TRANSLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +#else + getEmitStreamInfo(1, &pst, &pfOut); +#endif #if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { @@ -506,7 +532,7 @@ fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept, long ctrlMask){ int ncConsume = (int)(zPast - cBuf); if( pfO == 0 ) return ncConsume; #if SHELL_CON_TRANSLATE - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); if( pstReachesConsole(ppst) ){ int rv; @@ -527,8 +553,12 @@ oPutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ FILE *pfOut; const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); int ncConsume = (int)(zPast - cBuf); - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +#if SHELL_CON_TRANSLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +#else + getEmitStreamInfo(1, &pst, &pfOut); +#endif #if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ){ return conZstrEmit(ppst, cBuf, ncConsume); @@ -540,23 +570,25 @@ oPutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ #endif } +#ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ FILE *pfErr; const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); int ncConsume = (int)(zPast - cBuf); - PerStreamTags pst; /* Needed only for heretofore unknown streams. */ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -#if SHELL_CON_TRANSLATE +# if SHELL_CON_TRANSLATE if( pstReachesConsole(ppst) ){ return conZstrEmit(ppst, cBuf, ncConsume); }else { -#endif +# endif return (int)fwrite(cBuf, 1, ncConsume, pfErr); -#if SHELL_CON_TRANSLATE +# if SHELL_CON_TRANSLATE } -#endif +# endif } +#endif /* defined(CONSIO_EPUTB) */ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; @@ -622,3 +654,5 @@ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ } #endif } + +#undef SHELL_INVALID_FILE_PTR diff --git a/ext/consio/console_io.h b/ext/consio/console_io.h index 52a8d63ebe..eabc6148cf 100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@ -108,7 +108,9 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); */ SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); +#ifdef CONSIO_SET_ERROR_STREAM SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); +#endif /* ** Emit output like fprintf(). If the output is going to the @@ -167,8 +169,10 @@ fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept, long ctrlMask); SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept, long ctrlMask); /* Like fPutbUtf8 except stream is always the designated error. */ +#ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept, long ctrlMask); +#endif /* ** Collect input like fgets(...) with special provisions for input @@ -179,7 +183,7 @@ ePutbUtf8(const char *cBuf, int nAccept, long ctrlMask); */ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); /* Like fGetsUtf8 except stream is always the designated input. */ -SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); +/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ /* ** Set given stream for binary mode, where newline translation is diff --git a/manifest b/manifest index 33cb884f0d..c035bcc964 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\svar-intro\safter\sexecutable\scode\sdeparture\sfrom\sold\sC. -D 2023-11-13T13:01:01.956 +C Cure\smany\swarnings\sfrom\sgcc,\sclang\sand\sCL.exe. +D 2023-11-13T15:59:27.593 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,8 +50,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 41fd57d99340dd51efa3a61af12eedbb90a62ff5db31c5f9d3e665134bc09353 x -F ext/consio/console_io.h 3a2ad14e8adcca431fe9bd12e8ebb5ecfe30002cfa190b2be1479048a441e6ec +F ext/consio/console_io.c b59dac1eb138339fcef6da8feae2f3a9a4397b7362ef52b4dca630ef5f848f23 x +F ext/consio/console_io.h 515d27ca9e5a5d36e12ff2b85977b5931fdeba0ce142f40178d94dc8638b4c81 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@ -728,7 +728,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in 6317180fce95b4d994423047648676e9d8c4bd7b82ab8ad98644a9e9a81ff24c +F src/shell.c.in 187da23da7601bfb225d0efadf77cbf766d9cee7574323a25a3d42dc29cd27fe F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2142,8 +2142,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 97e2c9621e3e01048e33da63ca4146ededa33cf6adefa996fa49b8c6a8d555b5 -R 9e09f295654f16b3a3c0e5e6e0bcae0d +P 08996f4fd52f5742e77225ce3da2ac43ea5cc44bb0d8d21126b6d624273842a5 +R ab417e4721f6826c6db3c6522006d6a5 U larrybr -Z 4863af021829bb44bd729f706b66926d +Z 401b90e437fbd22102bf23dbdae3a8b4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e0b6ffab58..45b8bd6907 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -08996f4fd52f5742e77225ce3da2ac43ea5cc44bb0d8d21126b6d624273842a5 \ No newline at end of file +29ea2a3aadd34facc45a2e9f9a567bac16fca76beb019b879e2611433c85bbb3 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 03cadd639a..0435710efa 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1483,7 +1483,7 @@ static void shellPutsFunc( int nVal, sqlite3_value **apVal ){ - ShellState *p = (ShellState*)sqlite3_user_data(pCtx); + /* Unused: (ShellState*)sqlite3_user_data(pCtx); */ (void)nVal; oputf("%s\n", sqlite3_value_text(apVal[0])); sqlite3_result_value(pCtx, apVal[0]); @@ -1825,7 +1825,7 @@ static void output_quoted_escaped_string(const char *z){ */ static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){ const char *pcFirst = 0; - if( ns == ~0 ) ns = strlen(s); + if( ns == ~(size_t)0 ) ns = strlen(s); while(*zAny){ const char *pc = (const char*)memchr(s, *zAny&0xff, ns); if( pc ){ @@ -1848,7 +1848,7 @@ static void output_c_string(const char *z){ char cbsSay; oputz(zq); while( *z!=0 ){ - const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~0); + const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); const char *pcPast = z + fPutbUtf8(0, z, INT_MAX, ctrlMask); const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; if( pcEnd > z ) oPutbUtf8(z, (int)(pcEnd-z), 0); @@ -3001,7 +3001,6 @@ static void displayLinuxIoStats(void){ ** Display a single line of status using 64-bit values. */ static void displayStatLine( - ShellState *p, /* The shell context */ char *zLabel, /* Label for this one line */ char *zFormat, /* Format for the result */ int iStatusCtrl, /* Which status to display */ @@ -3067,22 +3066,22 @@ static int display_stats( return 0; } - displayStatLine(pArg, "Memory Used:", + displayStatLine("Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine(pArg, "Number of Outstanding Allocations:", + displayStatLine("Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", + displayStatLine("Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", + displayStatLine("Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", + displayStatLine("Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", + displayStatLine("Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", + displayStatLine("Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif From 87c407da182f9f8aa69f446988ea05a967ce062a Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 13 Nov 2023 18:35:37 +0000 Subject: [PATCH 184/347] JNI: add sqlite3_result_nio_buffer() and tests. Discover that we cannot create sensible sqlite3_column_nio_buffer() or sqlite3_value_nio_buffer() counterparts because of ByteBuffer interface limitations. FossilOrigin-Name: 44b4df01ff86841fb85b6295cbada422c6ba8a32a420a2e840e2d607b6c90164 --- ext/jni/src/c/sqlite3-jni.c | 134 ++++++++++++++++--- ext/jni/src/c/sqlite3-jni.h | 8 ++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 72 +++++++++- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 86 +++++++----- manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 251 insertions(+), 69 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index e3814825b1..e04b3ab5f3 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -882,7 +882,7 @@ static jbyte * s3jni__jbyteArray_bytes2(JNIEnv * const env, jbyteArray jBA, jsiz ** required so cannot check for that violation. The caller is required ** to ensure that any to-be-bind()ed range fits within SQLITE_LIMIT. */ -/*static*/ void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){ +static void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){ *pBuf = 0; *pN = 0; if( jbb ){ @@ -2433,32 +2433,90 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( return (jint)rc; } +/** + Helper for use with s3jni_setup_nio_args(). +*/ +struct S3JniNioArgs { + jobject jBuf; /* input - ByteBuffer */ + jint iBegin; /* input - byte offset */ + jint iN; /* input - byte count to bind */ + jint nBuf; /* output - jBuf's buffer size */ + void * p; /* output - jBuf's buffer memory */ + const void * pStart; /* output - offset of p to bind */ + int iOutLen; /* output - number of bytes from pStart to bind */ +}; +typedef struct S3JniNioArgs S3JniNioArgs; +static const S3JniNioArgs S3JniNioArgs_empty = { + 0,0,0,0,0,0,0 +}; + +/** + Internal helper for sqlite3_bind_nio_buffer() and + sqlite3_result_nio_buffer(). Populates pArgs and returns 0 on + success, non-0 if the operation should fail. The caller is + required to check for SJG.g.cByteBuffer!=0 before calling + this and reporting it in a way appropriate for that routine. + This function may assert() that SJG.g.cByteBuffer is not 0. + + The (jBuffer, iBegin, iN) arguments are the (ByteBuffer, offset, + length) arguments to the bind/result method. +*/ +static int s3jni_setup_nio_args( + JNIEnv *env, S3JniNioArgs * pArgs, + jobject jBuffer, jint iBegin, jint iN +){ + jlong iEnd = 0; + *pArgs = S3JniNioArgs_empty; + pArgs->jBuf = jBuffer; + pArgs->iBegin = iBegin; + pArgs->iN = iN; + assert( SJG.g.cByteBuffer ); + if( pArgs->iBegin<0 ){ + return SQLITE_MISUSE; + } + s3jni_get_nio_buffer(pArgs->jBuf, &pArgs->p, &pArgs->nBuf); + if( !pArgs->p ){ + return SQLITE_MISUSE; + }else if( pArgs->iBegin>=pArgs->nBuf ){ + pArgs->pStart = 0; + pArgs->iOutLen = 0; + return 0; + } + assert( pArgs->nBuf > 0 ); + assert( pArgs->iBegin < pArgs->nBuf ); + iEnd = pArgs->iN<0 ? pArgs->nBuf - pArgs->iBegin : pArgs->iBegin + pArgs->iN; + if( iEnd>(jlong)pArgs->nBuf ) iEnd = pArgs->nBuf - pArgs->iBegin; + if( iEnd - pArgs->iBegin > (jlong)SQLITE_MAX_LENGTH ){ + return SQLITE_TOOBIG; + } + assert( pArgs->iBegin >= 0 ); + assert( iEnd > pArgs->iBegin ); + pArgs->pStart = pArgs->p + pArgs->iBegin; + pArgs->iOutLen = (int)(iEnd - pArgs->iBegin); + assert( pArgs->iOutLen > 0 ); + return 0; +} + S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)( JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer, jint iBegin, jint iN ){ sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt); - void * pBuf = 0; - jint nBuf = 0; - jlong iEnd = 0; - if( !SJG.g.cByteBuffer || !pStmt || iBegin<0 ){ - return (jint)SQLITE_MISUSE; + S3JniNioArgs args; + int rc; + if( !pStmt || !SJG.g.cByteBuffer ) return SQLITE_MISUSE; + rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN); + if(rc){ + return rc; + }else if( !args.pStart || !args.iOutLen ){ + return sqlite3_bind_null(pStmt, ndx); } - s3jni_get_nio_buffer(jBuffer, &pBuf, &nBuf); - if( !pBuf || iBegin>=nBuf ){ - return (jint)sqlite3_bind_null(pStmt, ndx); - } - assert( nBuf > 0 ); - assert( iBegin < nBuf ); - iEnd = iN<0 ? nBuf - iBegin : iBegin + iN; - if( iEnd>(jlong)nBuf ) iEnd = nBuf-iBegin; - if( iEnd-iBegin >(jlong)SQLITE_MAX_LENGTH ){ - return SQLITE_MISUSE; - } - assert( iBegin>=0 ); - assert( iEnd > iBegin ); - return (jint)sqlite3_bind_blob(pStmt, (int)ndx, pBuf + iBegin, - (int)(iEnd - iBegin), SQLITE_TRANSIENT); + assert( args.iOutLen>0 ); + assert( args.nBuf > 0 ); + assert( args.pStart != 0 ); + assert( (args.pStart + args.iOutLen) <= (args.p + args.nBuf) ); + return sqlite3_bind_blob( pStmt, (int)ndx, args.pStart, + args.iOutLen, SQLITE_TRANSIENT ); } S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( @@ -4470,6 +4528,40 @@ S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( } } +S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( + JniArgsEnvClass, jobject jpCtx, jobject jBuffer, + jint iBegin, jint iN +){ + sqlite3_context * pCx = PtrGet_sqlite3_context(jpCtx); + int rc; + S3JniNioArgs args; + if( !pCx ){ + return; + }else if( !SJG.g.cByteBuffer ){ + sqlite3_result_error( + pCx, "This JVM does not support JNI access to ByteBuffers.", -1 + ); + return; + } + rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN); + if(rc){ + if( iBegin<0 ){ + sqlite3_result_error(pCx, "Start index may not be negative.", -1); + }else if( SQLITE_TOOBIG==rc ){ + sqlite3_result_error_toobig(pCx); + }else{ + sqlite3_result_error( + pCx, "Invalid arguments to sqlite3_result_nio_buffer().", -1 + ); + } + }else if( !args.pStart || !args.iOutLen ){ + sqlite3_result_null(pCx); + }else{ + sqlite3_result_blob(pCx, args.pStart, args.iOutLen, SQLITE_TRANSIENT); + } +} + + S3JniApi(sqlite3_result_null(),void,1result_1null)( JniArgsEnvClass, jobject jpCx ){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index f306c53fcb..bf1cc56d1e 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1729,6 +1729,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1int64 JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object (JNIEnv *, jclass, jobject, jobject); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_result_nio_buffer + * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Ljava/nio/ByteBuffer;II)V + */ +JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1nio_1buffer + (JNIEnv *, jclass, jobject, jobject, jint, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_result_null diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index 569d8f4a97..d3d434f7d6 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -295,16 +295,16 @@ public final class CApi { range is silently truncated to fit the buffer. If any of the following are true, this function behaves like - sqlite3_bind_null(): the buffer is null, beginPos is past the end - of the buffer, howMany is 0, or the calculated slice of the blob - has a length of 0. + sqlite3_bind_null(): the buffer is null, beginPos is at or past + the end of the buffer, howMany is 0, or the calculated slice of + the blob has a length of 0. If ndx is out of range, it returns SQLITE_RANGE, as documented - for sqlite3_bind_blob(). If any other arguments are invalid or - if sqlite3_jni_supports_nio() is false then SQLITE_MISUSE is + for sqlite3_bind_blob(). If beginPos is negative or if + sqlite3_jni_supports_nio() returns false then SQLITE_MISUSE is returned. Note that this function is bound (as it were) by the - SQLITE_LIMIT_LENGTH constraint and SQLITE_MISUSE is returned if - that's violated. + SQLITE_LIMIT_LENGTH constraint and SQLITE_TOOBIG is returned if + the resulting slice of the buffer exceeds that limit. This function does not modify the buffer's streaming-related cursors. @@ -318,6 +318,10 @@ public final class CApi { buffers and the only such class offered by Java is (apparently) ByteBuffer. + Design note: there are no sqlite3_column_nio_buffer() and + sqlite3_value_nio_buffer() counterparts because the ByteBuffer + interface does not enable sensible implementations of those. + @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html */ public static native int sqlite3_bind_nio_buffer( @@ -1560,6 +1564,39 @@ public final class CApi { @NotNull sqlite3_context cx, @NotNull Object o ); + /** + Similar to sqlite3_bind_nio_buffer(), this works like + sqlite3_result_blob() but accepts a java.nio.ByteBuffer as its + input source. See sqlite3_bind_nio_buffer() for the semantics of + the second and subsequent arguments. + + If cx is null then this function will silently fail. If + sqlite3_jni_supports_nio() returns false or iBegin is negative, + an error result is set. If (begin+n) extends beyond the end of + the buffer, it is silently truncated to fit. + + If any of the following apply, this function behaves like + sqlite3_result_null(): the blob is null, the resulting slice of + the blob is empty. + + If the resulting slice of the buffer exceeds SQLITE_LIMIT_LENGTH + then this function behaves like sqlite3_result_error_toobig(). + */ + public static native void sqlite3_result_nio_buffer( + @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob, + int begin, int n + ); + + /** + Convenience overload which uses the whole input object + as the result blob content. + */ + public static void sqlite3_result_nio_buffer( + @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob + ){ + sqlite3_result_nio_buffer(cx, blob, 0, -1); + } + public static native void sqlite3_result_null( @NotNull sqlite3_context cx ); @@ -1654,6 +1691,27 @@ public final class CApi { sqlite3_result_blob(cx, blob, (int)(null==blob ? 0 : blob.length)); } + /** + Convenience overload which behaves like + sqlite3_result_nio_buffer(). + */ + public static void sqlite3_result_blob( + @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob, + int begin, int n + ){ + sqlite3_result_nio_buffer(cx, blob, begin, n); + } + + /** + Convenience overload which behaves like the two-argument overload of + sqlite3_result_nio_buffer(). + */ + public static void sqlite3_result_blob( + @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob + ){ + sqlite3_result_nio_buffer(cx, blob); + } + /** Binds the given text using C's sqlite3_result_blob64() unless: diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index 0587eb6318..b50e3eaac1 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -39,12 +39,12 @@ import java.util.concurrent.Future; @interface SingleThreadOnly{} /** - Annotation for Tester1 tests which must only be run if JNI-level support for - java.nio.Buffer is available. + Annotation for Tester1 tests which must only be run if + sqlite3_jni_supports_nio() is true. */ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) -@interface RequiresNioBuffer{} +@interface RequiresJniNio{} public class Tester1 implements Runnable { //! True when running in multi-threaded mode. @@ -565,42 +565,65 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - @RequiresNioBuffer + @RequiresJniNio private void testBindByteBuffer(){ + /* TODO: these tests need to be much more extensive to check the + begin/end range handling. */ + sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); - sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10); + + final java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10); buf.put((byte)0x31)/*note that we'll skip this one*/ .put((byte)0x32) .put((byte)0x33) - .put((byte)0x34); - int rc = sqlite3_bind_blob(stmt, 1, buf, -1, 0); - affirm( SQLITE_MISUSE==rc ); - rc = sqlite3_bind_blob(stmt, 1, buf, 1, 3); - affirm( 0==rc ); - rc = sqlite3_step(stmt); - affirm(SQLITE_DONE == rc); + .put((byte)0x34) + .put((byte)0x35)/*we'll skip this one too*/; + + final int expectTotal = buf.get(1) + buf.get(2) + buf.get(3); + sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); + affirm( SQLITE_MISUSE == sqlite3_bind_blob(stmt, 1, buf, -1, 0) ); + affirm( 0 == sqlite3_bind_blob(stmt, 1, buf, 1, 3) ); + affirm( SQLITE_DONE == sqlite3_step(stmt) ); sqlite3_finalize(stmt); - stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); - int n = 0; + stmt = prepare(db, "SELECT a FROM t;"); int total = 0; - while( SQLITE_ROW == sqlite3_step(stmt) ){ - byte[] blob = sqlite3_column_blob(stmt, 0); - affirm(3 == blob.length); - int i = 0; - for(byte b : blob){ - affirm( i<=3 ); - affirm(b == buf.get(1 + i++)); - total += b; - } - ++n; + affirm( SQLITE_ROW == sqlite3_step(stmt) ); + byte blob[] = sqlite3_column_blob(stmt, 0); + affirm(3 == blob.length); + int i = 0; + for(byte b : blob){ + affirm( i<=3 ); + affirm(b == buf.get(1 + i++)); + total += b; } + affirm( SQLITE_DONE == sqlite3_step(stmt) ); sqlite3_finalize(stmt); - affirm(1 == n); - affirm(total == 0x32 + 0x33 + 0x34); - /* TODO: these tests need to be much more extensive to check the - begin range handling. */ + affirm(total == expectTotal); + + SQLFunction func = + new ScalarFunction(){ + public void xFunc(sqlite3_context cx, sqlite3_value[] args){ + sqlite3_result_blob(cx, buf, 1, 3); + } + }; + + affirm( 0 == sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func) ); + stmt = prepare(db, "SELECT myfunc()"); + affirm( SQLITE_ROW == sqlite3_step(stmt) ); + blob = sqlite3_column_blob(stmt, 0); + affirm(3 == blob.length); + i = 0; + total = 0; + for(byte b : blob){ + affirm( i<=3 ); + affirm(b == buf.get(1 + i++)); + total += b; + } + affirm( SQLITE_DONE == sqlite3_step(stmt) ); + sqlite3_finalize(stmt); + affirm(total == expectTotal); + sqlite3_close_v2(db); } @@ -1924,9 +1947,10 @@ public class Tester1 implements Runnable { if( forceFail ){ testMethods.add(m); } - }else if( m.isAnnotationPresent( RequiresNioBuffer.class ) + }else if( m.isAnnotationPresent( RequiresJniNio.class ) && !sqlite3_jni_supports_nio() ){ - outln("Skipping test for lack JNI nio.Buffer support: ",name,"()\n"); + outln("Skipping test for lack of JNI java.nio.ByteBuffer support: ", + name,"()\n"); ++nSkipped; }else if( !m.isAnnotationPresent( ManualTest.class ) ){ if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ diff --git a/manifest b/manifest index 47b34117a7..c74882a340 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\sadd\ssqlite3_bind_nio_buffer()\sand\sinitial\stests\sfor\sbinding\sByteBuffer\sobjects\sas\sblobs\son\sJVMs\swhich\shave\sJNI\ssupport\sfor\snio\sbuffers. -D 2023-11-13T14:58:37.421 +C JNI:\sadd\ssqlite3_result_nio_buffer()\sand\stests.\sDiscover\sthat\swe\scannot\screate\ssensible\ssqlite3_column_nio_buffer()\sor\ssqlite3_value_nio_buffer()\scounterparts\sbecause\sof\sByteBuffer\sinterface\slimitations. +D 2023-11-13T18:35:37.387 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,8 +241,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9cd020d4 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c a04d0d77e7391a69f7f8ca4b38e24de59b3a8f61610f2e91698c190c07283850 -F ext/jni/src/c/sqlite3-jni.h 2848299f845d36b4b6123d360e7a4eb960d040637a10158079af49f4ded16453 +F ext/jni/src/c/sqlite3-jni.c 56ddb35bbae71123bedd003ae2bf42adae7d683717bb15ac3ed51d16e2b6c3ff +F ext/jni/src/c/sqlite3-jni.h 1f28a6a2beec13a49efdb42804926d2139f6a786558ea52c121ddf0bf39af59a F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 5ef54290c17dca46d7f24001ac3b689559e1b37ee40d06b88fa5315d64863789 +F ext/jni/src/org/sqlite/jni/capi/CApi.java 1517e1f5fc53001b2cb7229a427d33747163f8f37348368fe7c3e14edbb9c512 F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4ec21172917f641787767443f418854329bf9b9779807b644e000dac1ec77013 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4d460ccce50e7ef4ce8e1ab5d46ceca70834a798d8a2233c93f4bede9deb6683 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -2139,8 +2139,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 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d -R 7292f27854539ba720bc8a8fcd0c273f +P b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f +R 2f05bfa531dff8515ddabab7cc0cd5f1 U stephan -Z 1881d5bf6b59348a7807616e897efc0f +Z acc87a8d47162ab6fa5eb71b4d2c3712 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 693d450f8a..b2e5d6bd1c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f \ No newline at end of file +44b4df01ff86841fb85b6295cbada422c6ba8a32a420a2e840e2d607b6c90164 \ No newline at end of file From ce2edab088b68c928f388d83493ac9c7b812224c Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 13 Nov 2023 23:11:10 +0000 Subject: [PATCH 185/347] JNI: add sqlite3_column_nio_buffer() and sqlite3_value_nio_buffer() using an only-slightly roundabout approach to creating properly-sized ByteBuffer objects. FossilOrigin-Name: efbc82b218d26b7ca9b881da69d5fd14d22b5211fbd85a835da50e5bfde3d160 --- ext/jni/src/c/sqlite3-jni.c | 90 ++++++++++++++++++-- ext/jni/src/c/sqlite3-jni.h | 24 +++++- ext/jni/src/org/sqlite/jni/capi/CApi.java | 48 ++++++++--- ext/jni/src/org/sqlite/jni/capi/Tester1.java | 22 ++++- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 173 insertions(+), 31 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index e04b3ab5f3..4028505a87 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -15,13 +15,14 @@ /* ** If you found this comment by searching the code for -** CallStaticObjectMethod then you're the victim of an OpenJDK bug: +** CallStaticObjectMethod because it appears in console output then +** you're probably the victim of an OpenJDK bug: ** ** https://bugs.openjdk.org/browse/JDK-8130659 ** -** It's known to happen with OpenJDK v8 but not with v19. -** -** This code does not use JNI's CallStaticObjectMethod(). +** It's known to happen with OpenJDK v8 but not with v19. It was +** triggered by this code long before it made any use of +** CallStaticObjectMethod(). */ /* @@ -664,6 +665,7 @@ struct S3JniGlobalType { ByteBuffer is available (which we determine during static init). */ jclass cByteBuffer /* global ref to java.nio.ByteBuffer */; + jmethodID byteBufferAlloc/* ByteBuffer.allocateDirect() */; } g; /* ** The list of Java-side auto-extensions @@ -1093,6 +1095,47 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, return rv; } +/* +** Creates a new ByteBuffer instance with a capacity of n. assert()s +** that SJG.g.cByteBuffer is not 0 and n>0. +*/ +static jobject s3jni__new_ByteBuffer(JNIEnv * const env, int n){ + jobject rv = 0; + assert( SJG.g.cByteBuffer ); + assert( SJG.g.byteBufferAlloc ); + assert( n > 0 ); + rv = (*env)->CallStaticObjectMethod(env, SJG.g.cByteBuffer, + SJG.g.byteBufferAlloc, (jint)n); + S3JniIfThrew { + S3JniExceptionReport; + S3JniExceptionClear; + } + s3jni_oom_check( rv ); + return rv; +} + +/* +** If n>0 and sqlite3_jni_supports_nio() is true then this creates a +** new ByteBuffer object and copies n bytes from p to it. Returns NULL +** if n is 0, sqlite3_jni_supports_nio() is false, or on allocation +** error (unless fatal alloc failures are enabled). +*/ +static jobject s3jni__blob_to_ByteBuffer(JNIEnv * const env, + const void * p, int n){ + jobject rv = NULL; + assert( n >= 0 ); + if( 0==n || !SJG.g.cByteBuffer ){ + return NULL; + } + rv = s3jni__new_ByteBuffer(env, n); + if( rv ){ + void * tgt = (*env)->GetDirectBufferAddress(env, rv); + memcpy(tgt, p, (size_t)n); + } + return rv; +} + + /* ** Requires jx to be a Throwable. Calls its toString() method and ** returns its value converted to a UTF-8 string. The caller owns the @@ -1936,7 +1979,7 @@ static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){ NativePointerHolder_set(S3JniNph(sqlite3_context), jCx, 0); for( ; i < argc; ++i ){ jobject jsv = (*env)->GetObjectArrayElement(env, jArgv, i); - assert(jsv); + assert(jsv && "Someone illegally modified a UDF argument array."); NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0); } } @@ -3022,6 +3065,21 @@ S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( return rv; } +S3JniApi(sqlite3_value_nio_buffer(),jobject,1column_1nio_1buffer)( + JniArgsEnvClass, jobject jStmt, jint ndx +){ + sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jStmt); + jobject rv = 0; + if( stmt ){ + const void * const p = sqlite3_column_blob(stmt, (int)ndx); + if( p ){ + const int n = sqlite3_column_bytes(stmt, (int)ndx); + rv = s3jni__blob_to_ByteBuffer(env, p, n); + } + } + return rv; +} + S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ @@ -5090,6 +5148,21 @@ S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( : 0; } +S3JniApi(sqlite3_value_nio_buffer(),jobject,1value_1nio_1buffer)( + JniArgsEnvClass, jobject jVal +){ + sqlite3_value * const sv = PtrGet_sqlite3_value(jVal); + jobject rv = 0; + if( sv ){ + const void * const p = sqlite3_value_blob(sv); + if( p ){ + const int n = sqlite3_value_bytes(sv); + rv = s3jni__blob_to_ByteBuffer(env, p, n); + } + } + return rv; +} + S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ @@ -6096,10 +6169,15 @@ Java_org_sqlite_jni_capi_CApi_init(JniArgsEnvClass){ unsigned char buf[16] = {0}; jobject bb = (*env)->NewDirectByteBuffer(env, buf, 16); if( bb ){ - SJG.g.cByteBuffer = (*env)->GetObjectClass(env, bb); + SJG.g.cByteBuffer = S3JniRefGlobal((*env)->GetObjectClass(env, bb)); + SJG.g.byteBufferAlloc = (*env)->GetStaticMethodID( + env, SJG.g.cByteBuffer, "allocateDirect", "(I)Ljava/nio/ByteBuffer;" + ); + S3JniExceptionIsFatal("Error getting ByteBuffer.allocateDirect() method."); S3JniUnrefLocal(bb); }else{ SJG.g.cByteBuffer = 0; + SJG.g.byteBufferAlloc = 0; } } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index bf1cc56d1e..c9034dbeed 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1105,6 +1105,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1bytes16 JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1count (JNIEnv *, jclass, jlong); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_column_database_name + * Signature: (JI)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1database_1name + (JNIEnv *, jclass, jlong, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_column_decltype @@ -1155,11 +1163,11 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1name /* * Class: org_sqlite_jni_capi_CApi - * Method: sqlite3_column_database_name - * Signature: (JI)Ljava/lang/String; + * Method: sqlite3_column_nio_buffer + * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;I)Ljava/nio/ByteBuffer; */ -JNIEXPORT jstring JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1database_1name - (JNIEnv *, jclass, jlong, jint); +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1nio_1buffer + (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_capi_CApi @@ -2105,6 +2113,14 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1int64 JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1java_1object (JNIEnv *, jclass, jlong); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_value_nio_buffer + * Signature: (Lorg/sqlite/jni/capi/sqlite3_value;)Ljava/nio/ByteBuffer; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1value_1nio_1buffer + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_value_nochange diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index d3d434f7d6..cd2a09e8ce 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -318,10 +318,6 @@ public final class CApi { buffers and the only such class offered by Java is (apparently) ByteBuffer. - Design note: there are no sqlite3_column_nio_buffer() and - sqlite3_value_nio_buffer() counterparts because the ByteBuffer - interface does not enable sensible implementations of those. - @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html */ public static native int sqlite3_bind_nio_buffer( @@ -646,6 +642,15 @@ public final class CApi { return sqlite3_column_count(stmt.getNativePointer()); } + private static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx); + + /** + Only available if built with SQLITE_ENABLE_COLUMN_METADATA. + */ + public static String sqlite3_column_database_name(@NotNull sqlite3_stmt stmt, int ndx){ + return sqlite3_column_database_name(stmt.getNativePointer(), ndx); + } + private static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx); public static String sqlite3_column_decltype(@NotNull sqlite3_stmt stmt, int ndx){ @@ -700,14 +705,15 @@ public final class CApi { return sqlite3_column_name(stmt.getNativePointer(), ndx); } - private static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx); - /** - Only available if built with SQLITE_ENABLE_COLUMN_METADATA. + A variant of sqlite3_column_blob() which returns the blob as a + ByteBuffer object. Returns null if its argument is null, if + sqlite3_jni_supports_nio() is false, or if sqlite3_column_blob() + would return null for the same inputs. */ - public static String sqlite3_column_database_name(@NotNull sqlite3_stmt stmt, int ndx){ - return sqlite3_column_database_name(stmt.getNativePointer(), ndx); - } + public static native java.nio.ByteBuffer sqlite3_column_nio_buffer( + @NotNull sqlite3_stmt stmt, int ndx + ); private static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx); @@ -1418,6 +1424,15 @@ public final class CApi { If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_new(), else it returns SQLITE_MISUSE with no side effects. + + WARNING: client code _must not_ hold a reference to the returned + sqlite3_value object beyond the scope of the preupdate hook in + which this function is called. Doing so will leave the client + holding a stale pointer, the address of which could point to + anything at all after the pre-update hook is complete. This API + has no way to record such objects and clear/invalidate them at + the end of a pre-update hook. We "could" add infrastructure to do + so, but would require significant levels of bookkeeping. */ public static int sqlite3_preupdate_new(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out){ @@ -1441,6 +1456,9 @@ public final class CApi { If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this acts as a proxy for C's sqlite3_preupdate_old(), else it returns SQLITE_MISUSE with no side effects. + + WARNING: see warning in sqlite3_preupdate_new() regarding the + potential for stale sqlite3_value handles. */ public static int sqlite3_preupdate_old(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out){ @@ -2110,6 +2128,16 @@ public final class CApi { return type.isInstance(o) ? (T)o : null; } + /** + A variant of sqlite3_column_blob() which returns the blob as a + ByteBuffer object. Returns null if its argument is null, if + sqlite3_jni_supports_nio() is false, or if sqlite3_value_blob() + would return null for the same input. + */ + public static native java.nio.ByteBuffer sqlite3_value_nio_buffer( + @NotNull sqlite3_value v + ); + private static native int sqlite3_value_nochange(@NotNull long ptrToValue); public static int sqlite3_value_nochange(@NotNull sqlite3_value v){ diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index b50e3eaac1..9e9f9a8843 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -494,6 +494,7 @@ public class Tester1 implements Runnable { stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); StringBuilder sbuf = new StringBuilder(); n = 0; + final boolean tryNio = sqlite3_jni_supports_nio(); while( SQLITE_ROW == sqlite3_step(stmt) ){ final sqlite3_value sv = sqlite3_value_dup(sqlite3_column_value(stmt,0)); final String txt = sqlite3_column_text16(stmt, 0); @@ -508,6 +509,15 @@ public class Tester1 implements Runnable { StandardCharsets.UTF_8)) ); affirm( txt.length() == sqlite3_value_bytes16(sv)/2 ); affirm( txt.equals(sqlite3_value_text16(sv)) ); + if( tryNio ){ + java.nio.ByteBuffer bu = sqlite3_value_nio_buffer(sv); + byte ba[] = sqlite3_value_blob(sv); + affirm( ba.length == bu.capacity() ); + int i = 0; + for( byte b : ba ){ + affirm( b == bu.get(i++) ); + } + } sqlite3_value_free(sv); ++n; } @@ -570,6 +580,10 @@ public class Tester1 implements Runnable { /* TODO: these tests need to be much more extensive to check the begin/end range handling. */ + java.nio.ByteBuffer zeroCheck = + java.nio.ByteBuffer.allocateDirect(0); + affirm( null != zeroCheck ); + zeroCheck = null; sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); @@ -590,11 +604,17 @@ public class Tester1 implements Runnable { int total = 0; affirm( SQLITE_ROW == sqlite3_step(stmt) ); byte blob[] = sqlite3_column_blob(stmt, 0); + java.nio.ByteBuffer nioBlob = + sqlite3_column_nio_buffer(stmt, 0); affirm(3 == blob.length); + affirm(blob.length == nioBlob.capacity()); + affirm(blob.length == nioBlob.limit()); int i = 0; for(byte b : blob){ affirm( i<=3 ); - affirm(b == buf.get(1 + i++)); + affirm(b == buf.get(1 + i)); + affirm(b == nioBlob.get(i)); + ++i; total += b; } affirm( SQLITE_DONE == sqlite3_step(stmt) ); diff --git a/manifest b/manifest index c74882a340..af3d7483a2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI:\sadd\ssqlite3_result_nio_buffer()\sand\stests.\sDiscover\sthat\swe\scannot\screate\ssensible\ssqlite3_column_nio_buffer()\sor\ssqlite3_value_nio_buffer()\scounterparts\sbecause\sof\sByteBuffer\sinterface\slimitations. -D 2023-11-13T18:35:37.387 +C JNI:\sadd\ssqlite3_column_nio_buffer()\sand\ssqlite3_value_nio_buffer()\susing\san\sonly-slightly\sroundabout\sapproach\sto\screating\sproperly-sized\sByteBuffer\sobjects. +D 2023-11-13T23:11:10.170 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -241,8 +241,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9cd020d4 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 56ddb35bbae71123bedd003ae2bf42adae7d683717bb15ac3ed51d16e2b6c3ff -F ext/jni/src/c/sqlite3-jni.h 1f28a6a2beec13a49efdb42804926d2139f6a786558ea52c121ddf0bf39af59a +F ext/jni/src/c/sqlite3-jni.c c63250298bf0bdc335704d877f559dcdefaa041ba0556022b13599d43badb822 +F ext/jni/src/c/sqlite3-jni.h bd95cd17bfe2c7bd5e865a785bc5ed99591f1a1581c9b1a1b9ba7d65fb41d4cc F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca @@ -251,7 +251,7 @@ F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63 F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759 F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca -F ext/jni/src/org/sqlite/jni/capi/CApi.java 1517e1f5fc53001b2cb7229a427d33747163f8f37348368fe7c3e14edbb9c512 +F ext/jni/src/org/sqlite/jni/capi/CApi.java bf81e122cff8a6d88b5185e57e38f5e2c3387a4e84ee6732aff234da6966d21f F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f -F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4d460ccce50e7ef4ce8e1ab5d46ceca70834a798d8a2233c93f4bede9deb6683 +F ext/jni/src/org/sqlite/jni/capi/Tester1.java 646b75c3ec454a2daa20fef1c15ed76f24882f8e529ff3749779bc2ece3c2820 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950 @@ -2139,8 +2139,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 b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f -R 2f05bfa531dff8515ddabab7cc0cd5f1 +P 44b4df01ff86841fb85b6295cbada422c6ba8a32a420a2e840e2d607b6c90164 +R 723ebc4fdaf6a9b23550997df97352fc U stephan -Z acc87a8d47162ab6fa5eb71b4d2c3712 +Z bec7a2465a92db0fb274e1f6df96d01f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b2e5d6bd1c..bcd471c105 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -44b4df01ff86841fb85b6295cbada422c6ba8a32a420a2e840e2d607b6c90164 \ No newline at end of file +efbc82b218d26b7ca9b881da69d5fd14d22b5211fbd85a835da50e5bfde3d160 \ No newline at end of file From b481413d951526b5ec02bbec090db3c3dbcd76c0 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 14 Nov 2023 01:33:15 +0000 Subject: [PATCH 186/347] JNI: add sqlite3_blob_write() overload which accepts a java.nio.ByteBuffer. Cleanups in adjacent code. FossilOrigin-Name: ca32af8542aa2725cc87f54541b19897556f610e4674edf9f22a84e3d4097a82 --- ext/jni/src/c/sqlite3-jni.c | 119 +++++++++++------- ext/jni/src/c/sqlite3-jni.h | 8 ++ ext/jni/src/org/sqlite/jni/capi/CApi.java | 54 +++++++- .../org/sqlite/jni/capi/OutputPointer.java | 22 ++++ ext/jni/src/org/sqlite/jni/capi/Tester1.java | 38 +++++- manifest | 20 +-- manifest.uuid | 2 +- 7 files changed, 205 insertions(+), 58 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 4028505a87..bd4c665694 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2481,85 +2481,93 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( */ struct S3JniNioArgs { jobject jBuf; /* input - ByteBuffer */ - jint iBegin; /* input - byte offset */ - jint iN; /* input - byte count to bind */ + jint iOffset; /* input - byte offset */ + jint iHowMany; /* input - byte count to bind/read/write */ jint nBuf; /* output - jBuf's buffer size */ void * p; /* output - jBuf's buffer memory */ - const void * pStart; /* output - offset of p to bind */ - int iOutLen; /* output - number of bytes from pStart to bind */ + const void * pStart; /* output - offset of p to bind/read/write */ + int nOut; /* output - number of bytes from pStart to bind/read/write */ }; typedef struct S3JniNioArgs S3JniNioArgs; static const S3JniNioArgs S3JniNioArgs_empty = { 0,0,0,0,0,0,0 }; -/** - Internal helper for sqlite3_bind_nio_buffer() and - sqlite3_result_nio_buffer(). Populates pArgs and returns 0 on - success, non-0 if the operation should fail. The caller is - required to check for SJG.g.cByteBuffer!=0 before calling - this and reporting it in a way appropriate for that routine. - This function may assert() that SJG.g.cByteBuffer is not 0. - - The (jBuffer, iBegin, iN) arguments are the (ByteBuffer, offset, - length) arguments to the bind/result method. +/* +** Internal helper for sqlite3_bind_nio_buffer() and +** sqlite3_result_nio_buffer(). Populates pArgs and returns 0 on +** success, non-0 if the operation should fail. The caller is required +** to check for SJG.g.cByteBuffer!=0 before calling this and reporting +** it in a way appropriate for that routine. This function may +** assert() that SJG.g.cByteBuffer is not 0. +** +** The (jBuffer, iOffset, iN) arguments are the (ByteBuffer, offset, +** length) arguments to the bind/result method. +** +** Returns 0 if everything looks to be in order, else some SQLITE_... +** result code */ static int s3jni_setup_nio_args( JNIEnv *env, S3JniNioArgs * pArgs, - jobject jBuffer, jint iBegin, jint iN + jobject jBuffer, jint iOffset, jint iN ){ jlong iEnd = 0; *pArgs = S3JniNioArgs_empty; pArgs->jBuf = jBuffer; - pArgs->iBegin = iBegin; - pArgs->iN = iN; + pArgs->iOffset = iOffset; + pArgs->iHowMany = iN; assert( SJG.g.cByteBuffer ); - if( pArgs->iBegin<0 ){ - return SQLITE_MISUSE; + if( pArgs->iOffset<0 ){ + return SQLITE_ERROR + /* SQLITE_MISUSE would arguably fit better but we use + SQLITE_ERROR for consistency with the code documented for a + negative target blob offset in sqlite3_blob_read/write(). */; } s3jni_get_nio_buffer(pArgs->jBuf, &pArgs->p, &pArgs->nBuf); if( !pArgs->p ){ return SQLITE_MISUSE; - }else if( pArgs->iBegin>=pArgs->nBuf ){ + }else if( pArgs->iOffset>=pArgs->nBuf ){ pArgs->pStart = 0; - pArgs->iOutLen = 0; + pArgs->nOut = 0; return 0; } assert( pArgs->nBuf > 0 ); - assert( pArgs->iBegin < pArgs->nBuf ); - iEnd = pArgs->iN<0 ? pArgs->nBuf - pArgs->iBegin : pArgs->iBegin + pArgs->iN; - if( iEnd>(jlong)pArgs->nBuf ) iEnd = pArgs->nBuf - pArgs->iBegin; - if( iEnd - pArgs->iBegin > (jlong)SQLITE_MAX_LENGTH ){ + assert( pArgs->iOffset < pArgs->nBuf ); + iEnd = pArgs->iHowMany<0 + ? pArgs->nBuf - pArgs->iOffset + : pArgs->iOffset + pArgs->iHowMany; + if( iEnd>(jlong)pArgs->nBuf ) iEnd = pArgs->nBuf - pArgs->iOffset; + if( iEnd - pArgs->iOffset > (jlong)SQLITE_MAX_LENGTH ){ return SQLITE_TOOBIG; } - assert( pArgs->iBegin >= 0 ); - assert( iEnd > pArgs->iBegin ); - pArgs->pStart = pArgs->p + pArgs->iBegin; - pArgs->iOutLen = (int)(iEnd - pArgs->iBegin); - assert( pArgs->iOutLen > 0 ); + assert( pArgs->iOffset >= 0 ); + assert( iEnd > pArgs->iOffset ); + pArgs->pStart = pArgs->p + pArgs->iOffset; + pArgs->nOut = (int)(iEnd - pArgs->iOffset); + assert( pArgs->nOut > 0 ); return 0; } S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)( JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer, - jint iBegin, jint iN + jint iOffset, jint iN ){ sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt); S3JniNioArgs args; int rc; if( !pStmt || !SJG.g.cByteBuffer ) return SQLITE_MISUSE; - rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN); + rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); if(rc){ return rc; - }else if( !args.pStart || !args.iOutLen ){ + }else if( !args.pStart || !args.nOut ){ return sqlite3_bind_null(pStmt, ndx); } - assert( args.iOutLen>0 ); + assert( args.nOut>0 ); assert( args.nBuf > 0 ); assert( args.pStart != 0 ); - assert( (args.pStart + args.iOutLen) <= (args.p + args.nBuf) ); + assert( (args.pStart + args.nOut) <= (args.p + args.nBuf) ); return sqlite3_bind_blob( pStmt, (int)ndx, args.pStart, - args.iOutLen, SQLITE_TRANSIENT ); + args.nOut, SQLITE_TRANSIENT ); } S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( @@ -2797,6 +2805,33 @@ S3JniApi(sqlite3_blob_write(),jint,1blob_1write)( return (jint)rc; } +S3JniApi(sqlite3_blob_write_nio_buffer(),jint,1blob_1write_1nio_1buffer)( + JniArgsEnvClass, jlong jpBlob, jint iSrcOff, jobject jBB, jint iTgtOff, jint iHowMany +){ + sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); + S3JniNioArgs args; + int rc; + if( !b || !SJG.g.cByteBuffer ){ + return SQLITE_MISUSE; + }else if( iTgtOff<0 || iSrcOff<0 ){ + return SQLITE_ERROR + /* for consistency with underlying sqlite3_blob_write() */; + }else if( 0==iHowMany ){ + return 0; + } + rc = s3jni_setup_nio_args(env, &args, jBB, iTgtOff, iHowMany); + if(rc){ + return rc; + }else if( !args.pStart || !args.nOut ){ + return 0; + } + assert( args.nOut>0 ); + assert( args.nBuf > 0 ); + assert( args.pStart != 0 ); + assert( (args.pStart + args.nOut) <= (args.p + args.nBuf) ); + return sqlite3_blob_write( b, args.pStart, (int)args.nOut, iSrcOff ); +} + /* Central C-to-Java busy handler proxy. */ static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; @@ -3065,7 +3100,7 @@ S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( return rv; } -S3JniApi(sqlite3_value_nio_buffer(),jobject,1column_1nio_1buffer)( +S3JniApi(sqlite3_column_nio_buffer(),jobject,1column_1nio_1buffer)( JniArgsEnvClass, jobject jStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jStmt); @@ -4588,7 +4623,7 @@ S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( JniArgsEnvClass, jobject jpCtx, jobject jBuffer, - jint iBegin, jint iN + jint iOffset, jint iN ){ sqlite3_context * pCx = PtrGet_sqlite3_context(jpCtx); int rc; @@ -4601,9 +4636,9 @@ S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( ); return; } - rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN); + rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); if(rc){ - if( iBegin<0 ){ + if( iOffset<0 ){ sqlite3_result_error(pCx, "Start index may not be negative.", -1); }else if( SQLITE_TOOBIG==rc ){ sqlite3_result_error_toobig(pCx); @@ -4612,10 +4647,10 @@ S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( pCx, "Invalid arguments to sqlite3_result_nio_buffer().", -1 ); } - }else if( !args.pStart || !args.iOutLen ){ + }else if( !args.pStart || !args.nOut ){ sqlite3_result_null(pCx); }else{ - sqlite3_result_blob(pCx, args.pStart, args.iOutLen, SQLITE_TRANSIENT); + sqlite3_result_blob(pCx, args.pStart, args.nOut, SQLITE_TRANSIENT); } } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index c9034dbeed..71565468d8 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1009,6 +1009,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1reopen JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1write (JNIEnv *, jclass, jlong, jbyteArray, jint); +/* + * Class: org_sqlite_jni_capi_CApi + * Method: sqlite3_blob_write_nio_buffer + * Signature: (JILjava/nio/ByteBuffer;II)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1blob_1write_1nio_1buffer + (JNIEnv *, jclass, jlong, jint, jobject, jint, jint); + /* * Class: org_sqlite_jni_capi_CApi * Method: sqlite3_busy_handler diff --git a/ext/jni/src/org/sqlite/jni/capi/CApi.java b/ext/jni/src/org/sqlite/jni/capi/CApi.java index cd2a09e8ce..513c504052 100644 --- a/ext/jni/src/org/sqlite/jni/capi/CApi.java +++ b/ext/jni/src/org/sqlite/jni/capi/CApi.java @@ -119,13 +119,15 @@ public final class CApi {

    This routine returns false without side effects if the current JNIEnv is not cached, else returns true, but this information is primarily for testing of the JNI bindings and is not information - which client-level code can use to make any informed decisions. + which client-level code can use to make any informed + decisions. Its return type and semantics are not considered + stable and may change at any time. */ public static native boolean sqlite3_java_uncache_thread(); /** - Returns true if this JVM has JNI-level support for direct memory - access using java.nio.ByteBuffer, else returns false. + Returns true if this JVM has JNI-level support for C-level direct + memory access using java.nio.ByteBuffer, else returns false. */ public static native boolean sqlite3_jni_supports_nio(); @@ -565,6 +567,49 @@ public final class CApi { return sqlite3_blob_write(b.getNativePointer(), bytes, iOffset); } + + /** + An internal level of indirection in order to avoid having + overloaded names of sqlite3_blob_write() in the C API, as the + resulting mangled names are unwieldy. The public face of this + method is the sqlite3_blob_write() overload which takes a + java.nio.ByteBuffer. + */ + private static native int sqlite3_blob_write_nio_buffer( + @NotNull long ptrToBlob, int tgtOffset, + @NotNull java.nio.ByteBuffer src, + int srcOffset, int howMany + ); + + /** + Writes howMany bytes of memory from offset srcOffset of the src + buffer at position tgtOffset of b. + + If howMany is negative then it's equivalent to the number of + bytes remaining starting at srcOffset. If the computed input + slice exceeds src's bounds, the slice is silently truncated. + */ + public static int sqlite3_blob_write( + @NotNull sqlite3_blob b, int tgtOffset, + @NotNull java.nio.ByteBuffer src, + int srcOffset, int howMany + ){ + return sqlite3_blob_write_nio_buffer(b.getNativePointer(), tgtOffset, + src, srcOffset, howMany); + } + + /** + Convenience overload which writes all of src to the given offset + of b. + */ + public static int sqlite3_blob_write( + @NotNull sqlite3_blob b, int tgtOffset, + @NotNull java.nio.ByteBuffer src + ){ + return sqlite3_blob_write_nio_buffer(b.getNativePointer(), tgtOffset, + src, 0, -1); + } + private static native int sqlite3_busy_handler( @NotNull long ptrToDb, @Nullable BusyHandlerCallback handler ); @@ -1787,7 +1832,8 @@ public final class CApi {

    ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language -** text that describes the error, as either UTF-8 or UTF-16 respectively. +** text that describes the error, as either UTF-8 or UTF-16 respectively, +** or NULL if no error message is available. ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** -** ^The sqlite3_errstr() interface returns the English-language text -** that describes the [result code], as UTF-8. +** ^The sqlite3_errstr(E) interface returns the English-language text +** that describes the [result code] E, as UTF-8, or NULL if E is not an +** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** From 0399994759cef5531a528275039d20a35e13e2f7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 15 Nov 2023 16:10:31 +0000 Subject: [PATCH 206/347] Work toward getting jsonb_remove() to work directly on JSONB blobs. FossilOrigin-Name: a79ff8e58fcaf718a6fb78e145117f1d6d40d133f31e9752bb9c6d484850a27b --- manifest | 17 ++++++----- manifest.uuid | 2 +- src/json.c | 81 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index ea4fb34420..f9398864df 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\senhancements\sand\sfixes\sfrom\strunk\sinto\sthe\sjsonb\sbranch. -D 2023-11-15T13:23:40.944 +C Work\stoward\sgetting\sjsonb_remove()\sto\swork\sdirectly\son\sJSONB\sblobs. +D 2023-11-15T16:10:31.627 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -258,7 +258,7 @@ F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 482f53dfec9e3ac2a9070d3fceebd56250932aaaf7c4f5bc8de29fc011416e0c F ext/jni/src/org/sqlite/jni/capi/ConfigLogCallback.java b995ca412f59b631803b93aa5b3684fce62e335d1e123207084c054abfd488d4 -F ext/jni/src/org/sqlite/jni/capi/ConfigSqlLogCallback.java e5723900b6458bc6288f52187090a78ebe0a20f403ac7c887ec9061dfe51aba7 w ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java +F ext/jni/src/org/sqlite/jni/capi/ConfigSqlLogCallback.java e5723900b6458bc6288f52187090a78ebe0a20f403ac7c887ec9061dfe51aba7 F ext/jni/src/org/sqlite/jni/capi/NativePointerHolder.java b7036dcb1ef1b39f1f36ac605dde0ff1a24a9a01ade6aa1a605039443e089a61 F ext/jni/src/org/sqlite/jni/capi/OutputPointer.java 246b0e66c4603f41c567105a21189d138aaf8c58203ecd4928802333da553e7c F ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java 97352091abd7556167f4799076396279a51749fdae2b72a6ba61cd39b3df0359 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 01644dc0c33b331fd5f294eddb1e4e02a4057535193f5f38161ee75047dd7177 +F src/json.c 3ff1ea643cab62e3ddb8be3a64bff2dd94af37ea461ea343054796e7f11638f8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2141,8 +2141,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 091a5f058dfe2115fb9213655b34f00bcec80aebb299b571975cfe4ecd5ec206 9264955e6e47aa8fc3a6f8bed192a6c12f43de49f7fba2e0cc080e47abedde14 -R cb95d20763613c633369d4c9e1ac8345 +P ba91408f4c044feda003ef93784ccefb619f99ab64379ced481ee8e9e890fd41 +R 58cf82355c33a53c2234fea2c219b8e4 +T *branch * jsonb-remove +T *sym-jsonb-remove * +T -sym-jsonb * U drh -Z c8298398287bfd474381f2a9e0abdbd8 +Z 367d226fdddcafa052b6fb6b4d87ed0a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5cff63237a..7cf8c33cb1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ba91408f4c044feda003ef93784ccefb619f99ab64379ced481ee8e9e890fd41 \ No newline at end of file +a79ff8e58fcaf718a6fb78e145117f1d6d40d133f31e9752bb9c6d484850a27b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3787167e90..e66c4b1326 100644 --- a/src/json.c +++ b/src/json.c @@ -3813,11 +3813,12 @@ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ ** Edit the size of the element at iRoot by the amount in pParse->delta. */ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ - u32 sz; - assert( pParse->delta==0 ); + u32 sz = 0; + assert( pParse->delta!=0 ); (void)jsonbPayloadSize(pParse, iRoot, &sz); sz += pParse->delta; jsonBlobChangePayloadSize(pParse, iRoot, sz); + pParse->nBlob += pParse->delta; } /* @@ -3837,7 +3838,8 @@ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ static u32 jsonLookupBlobStep( JsonParse *pParse, /* The JSON to search */ u32 iRoot, /* Begin the search at this element of aBlob[] */ - const char *zPath /* The path to search */ + const char *zPath, /* The path to search */ + u32 iLabel /* Label if iRoot is a value of in an object */ ){ u32 i, j, k, nKey, sz, n, iEnd, rc; const char *zKey; @@ -3848,10 +3850,13 @@ static u32 jsonLookupBlobStep( n = jsonbPayloadSize(pParse, iRoot, &sz); sz += n; if( pParse->eEdit==JEDIT_DEL ){ + if( iLabel>0 ){ + sz += iRoot - iLabel; + iRoot = iLabel; + } memmove(&pParse->aBlob[iRoot], &pParse->aBlob[iRoot+sz], - pParse->nBlob - iRoot); - pParse->nBlob -= n; - pParse->delta = -(int)n; + pParse->nBlob - (iRoot+sz)); + pParse->delta = -(int)sz; }else if( pParse->eEdit==JEDIT_INS ){ /* Already exists, so json_insert() is a no-op */ }else{ @@ -3895,21 +3900,22 @@ static u32 jsonLookupBlobStep( } if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); - j = iRoot + n; + j = iRoot + n; /* j is the index of a label */ iEnd = j+sz; while( jaBlob[j] & 0x0f; if( xJSONB_TEXTRAW ) return JSON_BLOB_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_BLOB_ERROR; - k = j+n; + k = j+n; /* k is the index of the label text */ if( k+sz>=iEnd ) return JSON_BLOB_ERROR; if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ - j = k+sz; - if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; - n = jsonbPayloadSize(pParse, j, &sz); - if( n==0 || j+n+sz>iEnd ) return JSON_BLOB_ERROR; - rc = jsonLookupBlobStep(pParse, j, &zPath[i]); + u32 v = k+sz; /* v is the index of the value */ + if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; + n = jsonbPayloadSize(pParse, v, &sz); + if( n==0 || v+n+sz>iEnd ) return JSON_BLOB_ERROR; + assert( j>0 ); + rc = jsonLookupBlobStep(pParse, v, &zPath[i], j); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } @@ -3955,7 +3961,7 @@ static u32 jsonLookupBlobStep( iEnd = j+sz; while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } @@ -3975,7 +3981,6 @@ static u32 jsonLookupBlobStep( pParse->nBlob - j); memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); pParse->delta = pParse->nIns; - pParse->nBlob += pParse->nIns; jsonAfterEditSizeAdjust(pParse, iRoot); return j; } @@ -4195,7 +4200,7 @@ static void jsonExtractFromBlob( if( px.aBlob==0 ) return; if( zPath[0]=='$' ){ zPath++; - i = jsonLookupBlobStep(&px, 0, zPath); + i = jsonLookupBlobStep(&px, 0, zPath, 0); }else if( (flags & JSON_ABPATH) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for @@ -4218,7 +4223,7 @@ static void jsonExtractFromBlob( jsonAppendChar(&jx, 0); zPath = jx.zBuf; } - i = jsonLookupBlobStep(&px, 0, zPath); + i = jsonLookupBlobStep(&px, 0, zPath, 0); jsonStringReset(&jx); }else{ sqlite3_result_error(ctx, "bad path", -1); @@ -4237,7 +4242,41 @@ static void jsonExtractFromBlob( sqlite3_free(zMsg); } } - + +/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent +** arguments are JSON paths of elements to be removed. Do that removal +** and return the result. +*/ +static void jsonRemoveFromBlob( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + int i; + u32 rc; + JsonParse px; + memset(&px, 0, sizeof(px)); + px.nBlob = sqlite3_value_bytes(argv[0]); + px.aBlob = (u8*)sqlite3_value_blob(argv[0]); + if( px.aBlob==0 ) return; + for(i=1; i0 ? sqlite3_free : SQLITE_DYNAMIC); + return; + +jsonRemoveFromBlob_error: + if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + return; + +} /**************************************************************************** ** SQL functions used for testing and debugging @@ -4780,6 +4819,12 @@ static void jsonRemoveFunc( u32 i; if( argc<1 ) return; + if( jsonFuncArgMightBeBinary(argv[0]) + && (SQLITE_PTR_TO_INT(sqlite3_user_data(ctx))&JSON_BLOB)!=0 + ){ + jsonRemoveFromBlob(ctx, argc, argv); + return; + } pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); if( pParse==0 ) return; for(i=1; i<(u32)argc; i++){ From af0c9ffb4ab9fc894398f8fae0cce8eb9fb7086f Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 15 Nov 2023 18:47:31 +0000 Subject: [PATCH 207/347] The jsonb_remove() routine now appears to be working. FossilOrigin-Name: e76d48137ea823b7810dc8c3b70eb21adabdd6cfbac36050c85d1375e94be1de --- manifest | 15 ++++++-------- manifest.uuid | 2 +- src/json.c | 55 ++++++++++++++++++++++++++++++++------------------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/manifest b/manifest index f9398864df..911d7e5d49 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\stoward\sgetting\sjsonb_remove()\sto\swork\sdirectly\son\sJSONB\sblobs. -D 2023-11-15T16:10:31.627 +C The\sjsonb_remove()\sroutine\snow\sappears\sto\sbe\sworking. +D 2023-11-15T18:47:31.537 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 3ff1ea643cab62e3ddb8be3a64bff2dd94af37ea461ea343054796e7f11638f8 +F src/json.c a647820e1e82fd516df80328ef3372209006bf99b7008d156411c8cd0d4d9fc4 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2141,11 +2141,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 ba91408f4c044feda003ef93784ccefb619f99ab64379ced481ee8e9e890fd41 -R 58cf82355c33a53c2234fea2c219b8e4 -T *branch * jsonb-remove -T *sym-jsonb-remove * -T -sym-jsonb * +P a79ff8e58fcaf718a6fb78e145117f1d6d40d133f31e9752bb9c6d484850a27b +R 6ab8908817380ee469413c76bca0c832 U drh -Z 367d226fdddcafa052b6fb6b4d87ed0a +Z a103fcc805c4c7ff278e886cba60f9d7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7cf8c33cb1..ebac1147c0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a79ff8e58fcaf718a6fb78e145117f1d6d40d133f31e9752bb9c6d484850a27b \ No newline at end of file +e76d48137ea823b7810dc8c3b70eb21adabdd6cfbac36050c85d1375e94be1de \ No newline at end of file diff --git a/src/json.c b/src/json.c index e66c4b1326..fa7913d944 100644 --- a/src/json.c +++ b/src/json.c @@ -2465,11 +2465,21 @@ static JsonNode *jsonLookupAppend( } /* -** Return the text of a syntax error message on a JSON path. Space is -** obtained from sqlite3_malloc(). +** Compute the text of an error in JSON path syntax. +** +** If ctx is not NULL then push the error message into ctx and return NULL. +* If ctx is NULL, then return the text of the error message. */ -static char *jsonPathSyntaxError(const char *zErr){ - return sqlite3_mprintf("JSON path error near '%q'", zErr); +static char *jsonPathSyntaxError(const char *zErr, sqlite3_context *ctx){ + char *zMsg = sqlite3_mprintf("JSON path error near '%q'", zErr); + if( ctx==0 ) return zMsg; + if( zMsg==0 ){ + sqlite3_result_error_nomem(ctx); + }else{ + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + } + return 0; } /* @@ -2490,7 +2500,6 @@ static JsonNode *jsonLookup( ){ const char *zErr = 0; JsonNode *pNode = 0; - char *zMsg; if( zPath==0 ) return 0; if( zPath[0]!='$' ){ @@ -2504,13 +2513,7 @@ static JsonNode *jsonLookup( lookup_err: pParse->nErr++; assert( zErr!=0 && pCtx!=0 ); - zMsg = jsonPathSyntaxError(zErr); - if( zMsg ){ - sqlite3_result_error(pCtx, zMsg, -1); - sqlite3_free(zMsg); - }else{ - sqlite3_result_error_nomem(pCtx); - } + jsonPathSyntaxError(zErr, pCtx); return 0; } @@ -3814,11 +3817,15 @@ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ */ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ u32 sz = 0; + u32 nBlob; assert( pParse->delta!=0 ); + assert( pParse->nBlobAlloc >= pParse->nBlob ); + nBlob = pParse->nBlob; + pParse->nBlob = pParse->nBlobAlloc; (void)jsonbPayloadSize(pParse, iRoot, &sz); + pParse->nBlob = nBlob; sz += pParse->delta; jsonBlobChangePayloadSize(pParse, iRoot, sz); - pParse->nBlob += pParse->delta; } /* @@ -3857,6 +3864,7 @@ static u32 jsonLookupBlobStep( memmove(&pParse->aBlob[iRoot], &pParse->aBlob[iRoot+sz], pParse->nBlob - (iRoot+sz)); pParse->delta = -(int)sz; + pParse->nBlob -= sz; }else if( pParse->eEdit==JEDIT_INS ){ /* Already exists, so json_insert() is a no-op */ }else{ @@ -3871,6 +3879,7 @@ static u32 jsonLookupBlobStep( memmove(&pParse->aBlob[iRoot+pParse->nIns], &pParse->aBlob[iRoot+sz], pParse->nBlob - iRoot - sz); + pParse->nBlob += d; } memcpy(&pParse->aBlob[iRoot], pParse->aIns, pParse->nIns); } @@ -3981,6 +3990,7 @@ static u32 jsonLookupBlobStep( pParse->nBlob - j); memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); pParse->delta = pParse->nIns; + pParse->nBlob += pParse->nIns; jsonAfterEditSizeAdjust(pParse, iRoot); return j; } @@ -4254,6 +4264,7 @@ static void jsonRemoveFromBlob( ){ int i; u32 rc; + const char *zPath = 0; JsonParse px; memset(&px, 0, sizeof(px)); px.nBlob = sqlite3_value_bytes(argv[0]); @@ -4261,21 +4272,25 @@ static void jsonRemoveFromBlob( if( px.aBlob==0 ) return; for(i=1; i0 ? sqlite3_free : SQLITE_DYNAMIC); + px.nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); return; -jsonRemoveFromBlob_error: +jsonRemoveFromBlob_patherror: if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonPathSyntaxError(zPath, ctx); return; - } /**************************************************************************** @@ -5814,7 +5829,7 @@ static int jsonEachFilter( } if( zErr ){ sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr, 0); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; }else if( pNode==0 ){ From 5e3ae1ec765fb7b5eb00854e64fbc84090636a9b Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 15 Nov 2023 19:21:09 +0000 Subject: [PATCH 208/347] Some simple test cases for JSONB direct remove. FossilOrigin-Name: 8cb4d2cbfc1b5f3c612d85138e66da8735d6589c7538fdf494a761c9b8104f18 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/jsonb01.test | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 test/jsonb01.test diff --git a/manifest b/manifest index 874a64e0cc..a211f0b659 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C jsonb_remove()\snow\sworks\swithout\shaving\sto\suse\sa\sJsonNode\sparse,\sassuming\nthat\sthe\sinput\sis\sJSONB. -D 2023-11-15T18:55:03.625 +C Some\ssimple\stest\scases\sfor\sJSONB\sdirect\sremove. +D 2023-11-15T19:21:09.380 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1329,6 +1329,7 @@ F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba01143 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d F test/json501.test c419deb835b70c1a2c8532936927bcc1146730328edd2052276715bfd209724d F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b +F test/jsonb01.test 830e87e012518aa0bf4358ed794fe40cbf0743247d5ade273223064547cb1ceb F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c 6e0228409ea7ca0497dad503fbd109badb5e59545d131014b6aaac68b56f484a F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 @@ -2141,9 +2142,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 ba91408f4c044feda003ef93784ccefb619f99ab64379ced481ee8e9e890fd41 e76d48137ea823b7810dc8c3b70eb21adabdd6cfbac36050c85d1375e94be1de -R 6ab8908817380ee469413c76bca0c832 -T +closed e76d48137ea823b7810dc8c3b70eb21adabdd6cfbac36050c85d1375e94be1de +P 5207679e929786e577a0553d0d84dda5125456dcde80c0f3156f14f4d8c804cb +R 76eca9722da06337c910d0b8ea9ca3cc U drh -Z a599420b20eb0d182795d486baa84f09 +Z 9b402027f08ff9f924d703e81b5baa92 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a1f41dd79e..1e29b18d5e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5207679e929786e577a0553d0d84dda5125456dcde80c0f3156f14f4d8c804cb \ No newline at end of file +8cb4d2cbfc1b5f3c612d85138e66da8735d6589c7538fdf494a761c9b8104f18 \ No newline at end of file diff --git a/test/jsonb01.test b/test/jsonb01.test new file mode 100644 index 0000000000..a01a96f558 --- /dev/null +++ b/test/jsonb01.test @@ -0,0 +1,46 @@ +# 2023-11-15 +# +# 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. +# +#*********************************************************************** +# Test cases for JSONB +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test jsonb01-1.1 { + CREATE TABLE t1(x JSON BLOB); + INSERT INTO t1 VALUES(jsonb('{a:5,b:{x:10,y:11},c:[1,2,3,4]}')); +} +foreach {id path res} { + 1 {$.a} {{{"b":{"x":10,"y":11},"c":[1,2,3,4]}}} + 2 {$.b} {{{"a":5,"c":[1,2,3,4]}}} + 3 {$.c} {{{"a":5,"b":{"x":10,"y":11}}}} + 4 {$.d} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} + 5 {$.b.x} {{{"a":5,"b":{"y":11},"c":[1,2,3,4]}}} + 6 {$.b.y} {{{"a":5,"b":{"x":10},"c":[1,2,3,4]}}} + 7 {$.c[0]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}} + 8 {$.c[1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}} + 9 {$.c[2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}} + 10 {$.c[3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}} + 11 {$.c[4]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} + 12 {$.c[#]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} + 13 {$.c[#-1]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3]}}} + 14 {$.c[#-2]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,4]}}} + 15 {$.c[#-3]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,3,4]}}} + 16 {$.c[#-4]} {{{"a":5,"b":{"x":10,"y":11},"c":[2,3,4]}}} + 17 {$.c[#-5]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} + 18 {$.c[#-6]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} +} { + do_execsql_test jsonb01-1.2.$id { + SELECT json(jsonb_remove(x,$path)) FROM t1; + } $res +} + +finish_test From 11e8242e2eec9172d2c47157900638f568813736 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 15 Nov 2023 20:32:06 +0000 Subject: [PATCH 209/347] Both json_remove() jsonb_remove() work on pure JSONB as long as the input is JSONB. FossilOrigin-Name: 68d551730be0a3ea9579646ed4836c73554c83ca7f2303b69a18843f1750f1a7 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 19 ++++++++++++++----- test/jsonb01.test | 5 ++++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index a211f0b659..9b3331efef 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Some\ssimple\stest\scases\sfor\sJSONB\sdirect\sremove. -D 2023-11-15T19:21:09.380 +C Both\sjson_remove()\sjsonb_remove()\swork\son\spure\sJSONB\sas\slong\sas\sthe\sinput\nis\sJSONB. +D 2023-11-15T20:32:06.379 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a647820e1e82fd516df80328ef3372209006bf99b7008d156411c8cd0d4d9fc4 +F src/json.c 949c4a36244d402d1e5f9c1815d9ce2cf8e94cf27ce7419e2d1b9dd5a97a2baa F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1329,7 +1329,7 @@ F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba01143 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d F test/json501.test c419deb835b70c1a2c8532936927bcc1146730328edd2052276715bfd209724d F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b -F test/jsonb01.test 830e87e012518aa0bf4358ed794fe40cbf0743247d5ade273223064547cb1ceb +F test/jsonb01.test cace70765b36a36aec9a85a41ea65667d3bbf647d4400ddc3ac76f8fe7d94f90 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c 6e0228409ea7ca0497dad503fbd109badb5e59545d131014b6aaac68b56f484a F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 @@ -2142,8 +2142,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 5207679e929786e577a0553d0d84dda5125456dcde80c0f3156f14f4d8c804cb -R 76eca9722da06337c910d0b8ea9ca3cc +P 8cb4d2cbfc1b5f3c612d85138e66da8735d6589c7538fdf494a761c9b8104f18 +R 17bf060181dd906a53600b2c931378c9 U drh -Z 9b402027f08ff9f924d703e81b5baa92 +Z a169b712ea2dc449ca00887f2092a50b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1e29b18d5e..94af86d981 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8cb4d2cbfc1b5f3c612d85138e66da8735d6589c7538fdf494a761c9b8104f18 \ No newline at end of file +68d551730be0a3ea9579646ed4836c73554c83ca7f2303b69a18843f1750f1a7 \ No newline at end of file diff --git a/src/json.c b/src/json.c index fa7913d944..89109e0039 100644 --- a/src/json.c +++ b/src/json.c @@ -4265,7 +4265,9 @@ static void jsonRemoveFromBlob( int i; u32 rc; const char *zPath = 0; + int flgs; JsonParse px; + memset(&px, 0, sizeof(px)); px.nBlob = sqlite3_value_bytes(argv[0]); px.aBlob = (u8*)sqlite3_value_blob(argv[0]); @@ -4283,8 +4285,17 @@ static void jsonRemoveFromBlob( if( rc==JSON_BLOB_NOTFOUND ) continue; if( JSON_BLOB_ISERROR(rc) ) goto jsonRemoveFromBlob_patherror; } - sqlite3_result_blob(ctx, px.aBlob, px.nBlob, - px.nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); + flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( flgs & JSON_BLOB ){ + sqlite3_result_blob(ctx, px.aBlob, px.nBlob, + px.nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); + }else{ + JsonString s; + jsonStringInit(&s, ctx); + jsonXlateBlobToText(&px, 0, &s); + jsonReturnString(&s); + if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + } return; jsonRemoveFromBlob_patherror: @@ -4834,9 +4845,7 @@ static void jsonRemoveFunc( u32 i; if( argc<1 ) return; - if( jsonFuncArgMightBeBinary(argv[0]) - && (SQLITE_PTR_TO_INT(sqlite3_user_data(ctx))&JSON_BLOB)!=0 - ){ + if( jsonFuncArgMightBeBinary(argv[0]) ){ jsonRemoveFromBlob(ctx, argc, argv); return; } diff --git a/test/jsonb01.test b/test/jsonb01.test index a01a96f558..d1b53ae6cc 100644 --- a/test/jsonb01.test +++ b/test/jsonb01.test @@ -38,9 +38,12 @@ foreach {id path res} { 17 {$.c[#-5]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} 18 {$.c[#-6]} {{{"a":5,"b":{"x":10,"y":11},"c":[1,2,3,4]}}} } { - do_execsql_test jsonb01-1.2.$id { + do_execsql_test jsonb01-1.2.$id.1 { SELECT json(jsonb_remove(x,$path)) FROM t1; } $res + do_execsql_test jsonb01-1.2.$id.2 { + SELECT json_remove(x,$path) FROM t1; + } $res } finish_test From e1edecf5d3c3a552af15e515102b98502fc82cc0 Mon Sep 17 00:00:00 2001 From: larrybr Date: Thu, 16 Nov 2023 18:31:05 +0000 Subject: [PATCH 210/347] Simplify and make more rational how console I/O package features are selected. (Motivated by Fiddle and other command-line utilities to soon use the package.) No substantive code-execution changes. FossilOrigin-Name: 1cde05877b7e47cb2ab9de26edd5245eb5ff1d91e69b94f3b24944fd4f6ccd3e --- Makefile.msc | 2 + ext/consio/console_io.c | 313 +++++++++++++++++++++------------------- ext/consio/console_io.h | 67 +++++---- manifest | 20 +-- manifest.uuid | 2 +- src/shell.c.in | 72 ++++++--- 6 files changed, 268 insertions(+), 208 deletions(-) mode change 100755 => 100644 ext/consio/console_io.c diff --git a/Makefile.msc b/Makefile.msc index 3a4d46b01f..33a9746dd4 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -2265,6 +2265,8 @@ SHELL_SRC = \ $(TOP)\ext\consio\console_io.h \ $(TOP)\ext\misc\appendvfs.c \ $(TOP)\ext\misc\completion.c \ + $(TOP)\ext\consio\console_io.c \ + $(TOP)\ext\consio\console_io.h \ $(TOP)\ext\misc\base64.c \ $(TOP)\ext\misc\base85.c \ $(TOP)\ext\misc\decimal.c \ diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c old mode 100755 new mode 100644 index 27727f2a8f..7ed06ccd31 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -28,28 +28,32 @@ # include "sqlite3.h" #endif -#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT -# ifndef SHELL_NO_SYSINC -# include -# include -# undef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# include +#ifndef SQLITE_CIO_NO_TRANSLATE +# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT +# ifndef SHELL_NO_SYSINC +# include +# include +# undef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include +# endif +# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ +# else +# ifndef SHELL_NO_SYSINC +# include +# endif +# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ # endif -# define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */ #else -# ifndef SHELL_NO_SYSINC -# include -# endif -# define SHELL_CON_TRANSLATE 0 /* Use plain C library stream I/O at console */ +# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ #endif -#if SHELL_CON_TRANSLATE +#if CIO_WIN_WC_XLATE /* Character used to represent a known-incomplete UTF-8 char group (�) */ static WCHAR cBadGroup = 0xfffd; #endif -#if SHELL_CON_TRANSLATE +#if CIO_WIN_WC_XLATE static HANDLE handleOfFile(FILE *pf){ int fileDesc = _fileno(pf); union { intptr_t osfh; HANDLE fh; } fid = { @@ -59,54 +63,51 @@ static HANDLE handleOfFile(FILE *pf){ } #endif +#ifndef SQLITE_CIO_NO_TRANSLATE typedef struct PerStreamTags { -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE HANDLE hx; DWORD consMode; char acIncomplete[4]; -#else +# else short reachesConsole; -#endif +# endif FILE *pf; } PerStreamTags; /* Define NULL-like value for things which can validly be 0. */ -#define SHELL_INVALID_FILE_PTR ((FILE *)~0) -#if SHELL_CON_TRANSLATE -# define SHELL_INVALID_CONS_MODE 0xFFFF0000 -#endif +# define SHELL_INVALID_FILE_PTR ((FILE *)~0) +# if CIO_WIN_WC_XLATE +# define SHELL_INVALID_CONS_MODE 0xFFFF0000 +# endif -#if SHELL_CON_TRANSLATE -# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ +# if CIO_WIN_WC_XLATE +# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ {0,0,0,0}, SHELL_INVALID_FILE_PTR } -#else -# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } -#endif +# else +# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } +# endif /* Quickly say whether a known output is going to the console. */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE static short pstReachesConsole(PerStreamTags *ppst){ -# if SHELL_CON_TRANSLATE return (ppst->hx != INVALID_HANDLE_VALUE); -# else - return (ppst->reachesConsole != 0); -# endif } -#else -# define pstReachesConsole(ppst) 0 -#endif +# else +# define pstReachesConsole(ppst) 0 +# endif -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE static void restoreConsoleArb(PerStreamTags *ppst){ if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode); } -#else -# define restoreConsoleArb(ppst) -#endif +# else +# define restoreConsoleArb(ppst) +# endif /* Say whether FILE* appears to be a console, collect associated info. */ static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE short rv = 0; DWORD dwCM = SHELL_INVALID_CONS_MODE; HANDLE fh = handleOfFile(pf); @@ -117,21 +118,21 @@ static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE; ppst->consMode = dwCM; return rv; -#else +# else ppst->pf = pf; ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; -#endif +# endif } -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE /* Define console modes for use with the Windows Console API. */ -# define SHELL_CONI_MODE \ +# define SHELL_CONI_MODE \ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) -# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ +# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ | ENABLE_VIRTUAL_TERMINAL_PROCESSING) -#endif +# endif typedef struct ConsoleInfo { PerStreamTags pstSetup[3]; @@ -151,19 +152,19 @@ static ConsoleInfo consoleInfo = { SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ if( pstReachesConsole(ppst) ){ DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; SetConsoleMode(ppst->hx, cm); } } -#else -# define maybeSetupAsConsole(ppst,odir) -#endif +# else +# define maybeSetupAsConsole(ppst,odir) +# endif SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE int ix = 0; while( ix < 6 ){ PerStreamTags *ppst = (ix<3)? @@ -171,7 +172,7 @@ SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ maybeSetupAsConsole(ppst, (ix % 3)>0); ++ix; } -#endif +# endif } SQLITE_INTERNAL_LINKAGE StreamsAreConsole @@ -193,7 +194,7 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ } SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE static ConsoleInfo *pci = &consoleInfo; if( pci->sacSetup ){ int ix; @@ -204,10 +205,11 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ } } } -#endif +# endif } +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ -#ifdef CONSIO_INPUT_REDIR +#ifdef SQLITE_CIO_INPUT_REDIR /* Say whether given FILE* is among those known, via either ** consoleClassifySetup() or set{Output,Error}Stream, as ** readable, and return an associated PerStreamTags pointer @@ -225,6 +227,7 @@ static PerStreamTags * isKnownReadable(FILE *pf){ } #endif +#ifndef SQLITE_CIO_NO_TRANSLATE /* Say whether given FILE* is among those known, via either ** consoleClassifySetup() or set{Output,Error}Stream, as ** writable, and return an associated PerStreamTags pointer @@ -259,20 +262,22 @@ static FILE *designateEmitStream(FILE *pf, unsigned chix){ SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ return designateEmitStream(pf, 1); } -#ifdef CONSIO_SET_ERROR_STREAM +# ifdef CONSIO_SET_ERROR_STREAM SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ return designateEmitStream(pf, 2); } -#endif +# endif +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ -#if SHELL_CON_TRANSLATE +#ifndef SQLITE_CIO_NO_SETMODE +# if CIO_WIN_WC_XLATE static void setModeFlushQ(FILE *pf, short bFlush, int mode){ if( bFlush ) fflush(pf); _setmode(_fileno(pf), mode); } -#else -# define setModeFlushQ(f, b, m) if(b) fflush(f) -#endif +# else +# define setModeFlushQ(f, b, m) if(b) fflush(f) +# endif SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ setModeFlushQ(pf, bFlush, _O_BINARY); @@ -280,9 +285,15 @@ SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ setModeFlushQ(pf, bFlush, _O_TEXT); } -#undef setModeFlushQ +# undef setModeFlushQ -#if SHELL_CON_TRANSLATE +#else /* defined(SQLITE_CIO_NO_SETMODE) */ +# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) +# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) +#endif /* defined(SQLITE_CIO_NO_SETMODE) */ + +#ifndef SQLITE_CIO_NO_TRANSLATE +# if CIO_WIN_WC_XLATE /* Write buffer cBuf as output to stream known to reach console, ** limited to ncTake char's. Return ncTake on success, else 0. */ static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ @@ -315,9 +326,9 @@ static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ return rv; }else return 0; } -#endif /* SHELL_CON_TRANSLATE */ +# endif /* CIO_WIN_WC_XLATE */ -#ifdef CONSIO_GET_EMIT_STREAM +# ifdef CONSIO_GET_EMIT_STREAM static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, PerStreamTags *ppst){ PerStreamTags *rv = isKnownWritable(pf); @@ -326,7 +337,7 @@ static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, streamOfConsole(pf, ppst); return ppst; } -#endif +# endif /* Get stream info, either for designated output or error stream when ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. @@ -366,22 +377,22 @@ SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ int rv; FILE *pfOut; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -#else +# else getEmitStreamInfo(1, &pst, &pfOut); -#endif +# endif assert(zFormat!=0); va_start(ap, zFormat); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ rv = conioVmPrintf(ppst, zFormat, ap); }else{ -#endif +# endif rv = vfprintf(pfOut, zFormat, ap); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif va_end(ap); return rv; } @@ -391,22 +402,22 @@ SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ int rv; FILE *pfErr; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -#else +# else getEmitStreamInfo(2, &pst, &pfErr); -#endif +# endif assert(zFormat!=0); va_start(ap, zFormat); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ rv = conioVmPrintf(ppst, zFormat, ap); }else{ -#endif +# endif rv = vfprintf(pfErr, zFormat, ap); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif va_end(ap); return rv; } @@ -415,37 +426,37 @@ SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ va_list ap; int rv; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); -#else +# else getEmitStreamInfo(0, &pst, &pfO); -#endif +# endif assert(zFormat!=0); va_start(ap, zFormat); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ maybeSetupAsConsole(ppst, 1); rv = conioVmPrintf(ppst, zFormat, ap); if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); }else{ -#endif +# endif rv = vfprintf(pfO, zFormat, ap); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif va_end(ap); return rv; } SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); -#else +# else getEmitStreamInfo(0, &pst, &pfO); -#endif +# endif assert(z!=0); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ int rv; maybeSetupAsConsole(ppst, 1); @@ -453,62 +464,66 @@ SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); return rv; }else { -#endif +# endif return (fputs(z, pfO)<0)? 0 : (int)strlen(z); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif } SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ FILE *pfErr; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -#else +# else getEmitStreamInfo(2, &pst, &pfErr); -#endif +# endif assert(z!=0); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { -#endif +# endif return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif } SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ FILE *pfOut; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -#else +# else getEmitStreamInfo(1, &pst, &pfOut); -#endif +# endif assert(z!=0); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); else { -#endif +# endif return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif } +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + +#if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE)) /* Skip over as much z[] input char sequence as is valid UTF-8, ** limited per nAccept char's or whole characters and containing ** no char cn such that ((1<=0 => char count, nAccept<0 => character */ -static const char* zSkipValidUtf8(const char *z, int nAccept, long ccm){ +SQLITE_INTERNAL_LINKAGE const char* +zSkipValidUtf8(const char *z, int nAccept, long ccm){ int ng = (nAccept<0)? -nAccept : 0; const char *pcLimit = (nAccept>=0)? z+nAccept : 0; assert(z!=0); - while( (pcLimit)? (z 0) ){ + while( (pcLimit)? (zpf) ) restoreConsoleArb(ppst); return rv; }else { -#endif - return (int)fwrite(cBuf, 1, ncConsume, pfO); -#if SHELL_CON_TRANSLATE +# endif + return (int)fwrite(cBuf, 1, nAccept, pfO); +# if CIO_WIN_WC_XLATE } -#endif +# endif } SQLITE_INTERNAL_LINKAGE int -oPutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ +oPutbUtf8(const char *cBuf, int nAccept){ FILE *pfOut; - const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); - int ncConsume = (int)(zPast - cBuf); PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -#else +# else getEmitStreamInfo(1, &pst, &pfOut); -#endif -#if SHELL_CON_TRANSLATE +# endif +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ - return conZstrEmit(ppst, cBuf, ncConsume); + return conZstrEmit(ppst, cBuf, nAccept); }else { -#endif - return (int)fwrite(cBuf, 1, ncConsume, pfOut); -#if SHELL_CON_TRANSLATE +# endif + return (int)fwrite(cBuf, 1, nAccept, pfOut); +# if CIO_WIN_WC_XLATE } -#endif +# endif } -#ifdef CONSIO_EPUTB +# ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int -ePutbUtf8(const char *cBuf, int nAccept, long ctrlMask){ +ePutbUtf8(const char *cBuf, int nAccept){ FILE *pfErr; - const char *zPast = zSkipValidUtf8(cBuf, nAccept, ctrlMask); - int ncConsume = (int)(zPast - cBuf); PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -# if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ - return conZstrEmit(ppst, cBuf, ncConsume); + return conZstrEmit(ppst, cBuf, nAccept); }else { -# endif - return (int)fwrite(cBuf, 1, ncConsume, pfErr); -# if SHELL_CON_TRANSLATE +# endif + return (int)fwrite(cBuf, 1, nAccept, pfErr); +# if CIO_WIN_WC_XLATE } -# endif +# endif } -#endif /* defined(CONSIO_EPUTB) */ +# endif /* defined(CONSIO_EPUTB) */ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ if( pfIn==0 ) pfIn = stdin; -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE if( pfIn == consoleInfo.pstSetup[0].pf && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ -# if SHELL_CON_TRANSLATE==1 -# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ +# if CIO_WIN_WC_XLATE==1 +# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ WCHAR wcBuf[SHELL_GULP+1]; int lend = 0, noc = 0; if( ncMax > 0 ) cBuf[0] = 0; while( noc < ncMax-8-1 && !lend ){ /* There is room for at least 2 more characters and a 0-terminator. */ int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; -# undef SHELL_GULP +# undef SHELL_GULP DWORD nbr = 0; BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0); if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ @@ -652,13 +663,15 @@ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ cBuf[noc] = 0; return cBuf; }else return 0; -# endif +# endif }else{ -#endif +# endif return fgets(cBuf, ncMax, pfIn); -#if SHELL_CON_TRANSLATE +# if CIO_WIN_WC_XLATE } -#endif +# endif } +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ +#undef CIO_WIN_WC_XLATE #undef SHELL_INVALID_FILE_PTR diff --git a/ext/consio/console_io.h b/ext/consio/console_io.h index eabc6148cf..4fb51d24d8 100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@ -35,6 +35,8 @@ # include "sqlite3.h" #endif +#ifndef SQLITE_CIO_NO_CLASSIFY + /* Define enum for use with following function. */ typedef enum StreamsAreConsole { SAC_NoConsole = 0, @@ -91,6 +93,13 @@ SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void); */ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); +#else /* defined(SQLITE_CIO_NO_CLASSIFY) */ +# define consoleClassifySetup(i,o,e) +# define consoleRenewSetup() +# define consoleRestore() +#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ + +#ifndef SQLITE_CIO_NO_REDIRECT /* ** Set stream to be used for the functions below which write ** to "the designated X stream", where X is Output or Error. @@ -108,10 +117,15 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); */ SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); -#ifdef CONSIO_SET_ERROR_STREAM +# ifdef CONSIO_SET_ERROR_STREAM SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); -#endif +# endif +#else +# define setOutputStream(pf) +# define setErrorStream(pf) +#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ +#ifndef SQLITE_CIO_NO_TRANSLATE /* ** Emit output like fprintf(). If the output is going to the ** console and translation from UTF-8 is necessary, perform @@ -140,38 +154,19 @@ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); /* ** Emit output like fPutsUtf8(), except that the length of the -** accepted char or character sequence may be limited by nAccept. -** -** The magnitude and sign of nAccept control what nAccept limits. -** If positive, nAccept limits the number of char's accepted. -** If negative, it limits the number of valid input characters. -** Obtain the behavior of {f,o,e}PutsUtf8 with nAccept==INT_MAX. +** accepted char or character sequence is limited by nAccept. ** ** Returns the number of accepted char values. -** -** When ctrlMask!=0, it specifies a set of control characters not -** accepted as input, so that cBuf[abs(N)] on return will be one -** of the non-accepted characters unless nAccept limited the scan. -** Each bit in ctrlMask, 1<=0 => char count, nAccept<0 => character + */ +SQLITE_INTERNAL_LINKAGE const char* +zSkipValidUtf8(const char *z, int nAccept, long ccm); + +#endif diff --git a/manifest b/manifest index 187de57e5a..4d3bb6ee32 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Add\sa\sfew\sasserts\sinto\s*Put*()\sfunctions\sto\scatch\scalls\sthat\smight\scause\san\saddress\sfault. -D 2023-11-14T03:24:43.961 +C Simplify\sand\smake\smore\srational\show\sconsole\sI/O\spackage\sfeatures\sare\sselected.\s(Motivated\sby\sFiddle\sand\sother\scommand-line\sutilities\sto\ssoon\suse\sthe\spackage.)\sNo\ssubstantive\scode-execution\schanges. +D 2023-11-16T18:31:05.379 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F Makefile.in f1ec22cfe53927eb9411ed82e452a0984df0ef88acb7a45024d98c5b6909da12 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 -F Makefile.msc 59bb36dba001f0b38212be0794fb838f25371008b180929bcf08aa799694c168 +F Makefile.msc f90f75b3b62809e1d13e5362b84a5cce90bc9d6772e4092be52c2b9a6638a7ed F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -50,8 +50,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c baffbeb5e73dcd3191c677bbcd5266c35891c17f87930cd89cf2463b7ec2da09 x -F ext/consio/console_io.h 515d27ca9e5a5d36e12ff2b85977b5931fdeba0ce142f40178d94dc8638b4c81 +F ext/consio/console_io.c 48536ed2f2add0abd8be80f72cae46e19738fa671b69daa88197df53a55da7c4 +F ext/consio/console_io.h b8dce7fa926b51f19b5d1c516219ac91b5c1ccddd467b2510e2557c900830f7d F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@ -728,7 +728,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 -F src/shell.c.in 187da23da7601bfb225d0efadf77cbf766d9cee7574323a25a3d42dc29cd27fe +F src/shell.c.in 9bfdb6faaea31e2ff0f940f1234e39f505f263b068ba6736838c7b6ad9d7d8ad F src/sqlite.h.in 4f841d3d117b830ee5ee45e8d89ceff1195f3ebb72d041ace8d116ba4c103b35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2142,8 +2142,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 29ea2a3aadd34facc45a2e9f9a567bac16fca76beb019b879e2611433c85bbb3 -R 8876fea59629c4e62a560a6743807241 +P 2341f9b6a48634a94e90e849f579afd07d398c3e0304efccf07d18b41a0e40aa +Q +45b8061e7568ccca164fe000f1f0a0d984b1c28fad530bc9fdea35793a0f40bc +Q +957ebaa2be3056371d679f7a00aeba242db34218fb839ffd1d1197cad0c4a510 +R dab16a36061bd95f615790a1ad678bef U larrybr -Z 00b6c0e6cfeef68150271d56dd5abcd0 +Z f345bc7daef2ffd72adafc6ced1c0785 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ca4099ba5e..47eb0416b6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2341f9b6a48634a94e90e849f579afd07d398c3e0304efccf07d18b41a0e40aa \ No newline at end of file +1cde05877b7e47cb2ab9de26edd5245eb5ff1d91e69b94f3b24944fd4f6ccd3e \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 0435710efa..91d99d336c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -237,13 +237,23 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif +/* Use console I/O package as a direct INCLUDE. */ #define SQLITE_INTERNAL_LINKAGE static +#ifdef SQLITE_SHELL_FIDDLE +/* Deselect most features from the console I/O package for Fiddle. */ +# define SQLITE_CIO_NO_REDIRECT +# define SQLITE_CIO_NO_CLASSIFY +# define SQLITE_CIO_NO_TRANSLATE +# define SQLITE_CIO_NO_SETMODE +#endif INCLUDE ../ext/consio/console_io.h INCLUDE ../ext/consio/console_io.c +#ifndef SQLITE_SHELL_FIDDLE + /* From here onward, fgets() is redirected to the console_io library. */ -#define fgets(b,n,f) fGetsUtf8(b,n,f) +# define fgets(b,n,f) fGetsUtf8(b,n,f) /* * Define macros for emitting output text in various ways: * sputz(s, z) => emit 0-terminated string z to given stream s @@ -252,24 +262,31 @@ INCLUDE ../ext/consio/console_io.c * oputf(f, ...) => emit varargs per format f to default stream * eputz(z) => emit 0-terminated string z to error stream * eputf(f, ...) => emit varargs per format f to error stream - * sputc(s, c) => emit single ASCII character to given stream s - * oputc(c) => emit single ASCII character to default stream - * eputc(c) => emit single ASCII character to error stream + * oputb(b, n) => emit char buffer b[0..n-1] to default stream * * Note that the default stream is whatever has been last set via: * setOutputStream(FILE *pf) * This is normally the stream that CLI normal output goes to. * For the stand-alone CLI, it is stdout with no .output redirect. */ -#define sputz(s,z) fPutsUtf8(z,s) -#define sputf fPrintfUtf8 -#define oputz(z) oPutsUtf8(z) -#define oputf oPrintfUtf8 -#define eputz(z) ePutsUtf8(z) -#define eputf ePrintfUtf8 -#define sputc(f,c) fPutcUtf8(c,f) -#define oputc(c) oPutcUtf8(c) -#define eputc(c) ePutcUtf8(c) +# define sputz(s,z) fPutsUtf8(z,s) +# define sputf fPrintfUtf8 +# define oputz(z) oPutsUtf8(z) +# define oputf oPrintfUtf8 +# define eputz(z) ePutsUtf8(z) +# define eputf ePrintfUtf8 +# define oputb(buf,na) oPutbUtf8(buf,na) + +#else +/* For Fiddle, all console handling and emit redirection is omitted. */ +# define sputz(fp,z) fputs(z,fp) +# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) +# define oputz(z) fputs(z,stdout) +# define oputf(fmt, ...) printf(fmt,__VA_ARGS__) +# define eputz(z) fputs(z,stderr) +# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) +# define oputb(buf,na) fwrite(buf,1,na,stdout) +#endif /* True if the timer is enabled */ static int enableTimer = 0; @@ -1722,8 +1739,10 @@ static const char *unused_string( static void output_quoted_string(const char *z){ int i; char c; +#ifndef SQLITE_SHELL_FIDDLE FILE *pfO = setOutputStream(invalidFileStream); setBinaryMode(pfO, 1); +#endif if( z==0 ) return; for(i=0; (c = z[i])!=0 && c!='\''; i++){} if( c==0 ){ @@ -1748,7 +1767,11 @@ static void output_quoted_string(const char *z){ } oputz("'"); } +#ifndef SQLITE_SHELL_FIDDLE setTextMode(pfO, 1); +#else + setTextMode(stdout, 1); +#endif } /* @@ -1763,8 +1786,10 @@ static void output_quoted_string(const char *z){ static void output_quoted_escaped_string(const char *z){ int i; char c; +#ifndef SQLITE_SHELL_FIDDLE FILE *pfO = setOutputStream(invalidFileStream); setBinaryMode(pfO, 1); +#endif for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} if( c==0 ){ oputf("'%s'",z); @@ -1816,7 +1841,11 @@ static void output_quoted_escaped_string(const char *z){ oputf(",'%s',char(10))", zNL); } } +#ifndef SQLITE_SHELL_FIDDLE setTextMode(pfO, 1); +#else + setTextMode(stdout, 1); +#endif } /* @@ -1849,9 +1878,9 @@ static void output_c_string(const char *z){ oputz(zq); while( *z!=0 ){ const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); - const char *pcPast = z + fPutbUtf8(0, z, INT_MAX, ctrlMask); + const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; - if( pcEnd > z ) oPutbUtf8(z, (int)(pcEnd-z), 0); + if( pcEnd > z ) oputb(z, (int)(pcEnd-z)); if( (c = *pcEnd)==0 ) break; ++pcEnd; switch( c ){ @@ -1895,10 +1924,10 @@ static void output_json_string(const char *z, i64 n){ oputz(zq); while( z < pcLimit ){ const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); - const char *pcPast = z + fPutbUtf8(0, z, (int)(pcLimit-z), ctrlMask); + const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; if( pcEnd > z ){ - oPutbUtf8(z, (int)(pcEnd-z), 0); + oputb(z, (int)(pcEnd-z)); z = pcEnd; } if( z >= pcLimit ) break; @@ -6092,6 +6121,7 @@ static void tryToClone(ShellState *p, const char *zNewDb){ close_db(newDb); } +#ifndef SQLITE_SHELL_FIDDLE /* ** Change the output stream (file or pipe or console) to something else. */ @@ -6147,6 +6177,10 @@ static void output_reset(ShellState *p){ p->out = stdout; setOutputStream(stdout); } +#else +# define output_redir(SS,pfO) +# define output_reset(SS) +#endif /* ** Run an SQL command and return the single integer result. @@ -9165,7 +9199,7 @@ static int do_meta_command(char *zLine, ShellState *p){ && cli_strcmp(zFile,"on")!=0 && cli_strcmp(zFile,"off")!=0 ){ - sputf(stdout, "cannot set .log to anything other" + sputz(stdout, "cannot set .log to anything other" " than \"on\" or \"off\"\n"); zFile = "off"; } @@ -11852,7 +11886,7 @@ static char *cmdline_option_value(int argc, char **argv, int i){ } static void sayAbnormalExit(void){ - if( seenInterrupt ) eputf("Program interrupted.\n"); + if( seenInterrupt ) eputz("Program interrupted.\n"); } #ifndef SQLITE_SHELL_IS_UTF8 From a0764f63a8fa984cc1c90b377916f8dbe11207ec Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 16 Nov 2023 21:11:56 +0000 Subject: [PATCH 211/347] When querying a tokendata=1 fts5 table, do not use a prefix cursor for the case where the term has only one variant. FossilOrigin-Name: d711c96ba855686d6881a50498418de3492144f005684b5ae55bca24413dce47 --- ext/fts5/fts5_index.c | 232 +++++++++++++++++++++-------- ext/fts5/test/fts5origintext2.test | 107 +++++++++++++ manifest | 13 +- manifest.uuid | 2 +- 4 files changed, 282 insertions(+), 72 deletions(-) create mode 100644 ext/fts5/test/fts5origintext2.test diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 2d6d561b7b..887bb75dac 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -368,6 +368,7 @@ struct Fts5Index { sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; + sqlite3_stmt *pIdxProbe; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; @@ -2629,6 +2630,18 @@ static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){ return p->pIdxSelect; } +static sqlite3_stmt *fts5IdxProbeStmt(Fts5Index *p){ + if( p->pIdxProbe==0 ){ + Fts5Config *pConfig = p->pConfig; + fts5IndexPrepareStmt(p, &p->pIdxProbe, sqlite3_mprintf( + "SELECT 1 FROM '%q'.'%q_idx' WHERE " + "segid=? AND term>? AND termzDb, pConfig->zName + )); + } + return p->pIdxProbe; +} + /* ** Initialize the object pIter to point to term pTerm/nTerm within segment ** pSeg. If there is no such term in the index, the iterator is set to EOF. @@ -3846,7 +3859,7 @@ static void fts5MultiIterNew( assert( iIter==nSeg ); } - /* If the above was successful, each component iterators now points + /* If the above was successful, each component iterator now points ** to the first entry in its segment. In this case initialize the ** aFirst[] array. Or, if an error has occurred, free the iterator ** object and set the output variable to NULL. */ @@ -6179,7 +6192,7 @@ static void fts5TokenMapPoslist( } static int fts5TokenMapHash(i64 iRowid, int iCol, int iOff){ - return iRowid + (iRowid << 3) + (iCol << 6) + (iOff << 9); + return (iRowid + (iRowid << 3) + (iCol << 6) + (iOff << 9)) & 0x7FFFFFFF; } static void fts5TokenMapHashify(Fts5Index *p, Fts5TokenMap *pMap){ @@ -6228,6 +6241,84 @@ static const u8 *fts5TokenMapLookup( return 0; } +/* +** The iterator passed as the second argument has been opened to scan and +** merge doclists for a series of tokens in tokendata=1 mode. This function +** tests whether or not, instead of using the cursor to read doclists to +** merge, it can be used directly by the upper layer. This is the case +** if the cursor currently points to the only token that corresponds to +** the queried term. i.e. if the next token that will be visited by the +** iterator does not match the query. +*/ +int fts5TokendataIterIsOk( + Fts5Index *p, + Fts5Iter *pIter, + const u8 *pToken, + int nToken +){ + int ii; + Fts5Buffer buf = {0, 0, 0}; + int bRet = 1; + Fts5Buffer *pTerm = 0; + + /* Iterator is not usable if it uses the hash table */ + if( pIter->aSeg[0].pSeg==0 ) return 0; + + for(ii=0; bRet && iinSeg; ii++){ + Fts5SegIter *pSeg = &pIter->aSeg[ii]; + Fts5Data *pLeaf = pSeg->pLeaf; + if( pLeaf ){ + + if( pTerm==0 ){ + pTerm = &pSeg->term; + }else{ + if( pSeg->term.n!=pTerm->n + || memcmp(pSeg->term.p, pTerm->p, pTerm->n) + ){ + bRet = 0; + break; + } + } + + if( pSeg->iEndofDoclistszLeaf ){ + /* Next term is on this node. Check it directly. */ + int nPrefix = 0; + fts5GetVarint32(&pLeaf->p[pSeg->iEndofDoclist], nPrefix); + if( nPrefix>=nToken ) bRet = 0; + }else{ + /* Next term is on a subsequent page. In this case query the %_idx + ** table to discover exactly what that next term is. */ + sqlite3_stmt *pProbe = fts5IdxProbeStmt(p); + if( pProbe ){ + int rc = SQLITE_OK; + if( buf.n==0 ){ + sqlite3Fts5BufferAppendBlob(&p->rc, &buf, nToken, pToken); + sqlite3Fts5BufferAppendBlob(&p->rc, &buf, 1, (const u8*)"\1"); + } + sqlite3_bind_int(pProbe, 1, pSeg->pSeg->iSegid); + sqlite3_bind_blob(pProbe,2, pSeg->term.p,pSeg->term.n, SQLITE_STATIC); + sqlite3_bind_blob(pProbe,3, buf.p, buf.n, SQLITE_STATIC); + + if( sqlite3_step(pProbe)==SQLITE_ROW ){ + bRet = 0; + } + rc = sqlite3_reset(pProbe); + if( p->rc==SQLITE_OK ) p->rc = rc; + } + } + } + } + + if( bRet ){ + for(ii=0; iinSeg; ii++){ + Fts5SegIter *pSeg = &pIter->aSeg[ii]; + pSeg->flags |= FTS5_SEGITER_ONETERM; + } + } + + fts5BufferFree(&buf); + return bRet; +} static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ @@ -6261,9 +6352,6 @@ static void fts5SetupPrefixIter( aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p); - if( iIdx==0 ){ - pMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); - } assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); if( p->rc==SQLITE_OK ){ @@ -6308,79 +6396,92 @@ static void fts5SetupPrefixIter( pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); - for( /* no-op */ ; - fts5MultiIterEof(p, p1)==0; - fts5MultiIterNext2(p, p1, &bNewTerm) - ){ - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; - int nTerm = pSeg->term.n; - const u8 *pTerm = pSeg->term.p; - p1->xSetOutputs(p1, pSeg); - if( pMap ){ + if( bDesc==0 && bTokenscan && fts5TokendataIterIsOk(p, p1, pToken,nToken) ){ + /* In this case iterator p1 may be used as is. */ + *ppIter = p1; + }else{ + + if( iIdx==0 && p->pConfig->eDetail==FTS5_DETAIL_FULL ){ + pMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); + } + assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); + + for( /* no-op */ ; + fts5MultiIterEof(p, p1)==0; + fts5MultiIterNext2(p, p1, &bNewTerm) + ){ + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; + int nTerm = pSeg->term.n; + const u8 *pTerm = pSeg->term.p; + p1->xSetOutputs(p1, pSeg); + + assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( bNewTerm ){ - fts5TokenMapTerm(p, pMap, &pTerm[1], nTerm-1); + if( nTermnToken && pTerm[nToken]!=0x00 ) break; } - fts5TokenMapPoslist(p, pMap, p1); - } - - assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); - if( bNewTerm ){ - if( nTermnToken && pTerm[nToken]!=0x00 ) break; - } - - if( p1->base.nData==0 ) continue; - - if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ - for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ - int i1 = i*nMerge; - int iStore; - assert( i1+nMerge<=nBuf ); - for(iStore=i1; iStorebase.nData==0 ) continue; + if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ + for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ + int i1 = i*nMerge; + int iStore; + assert( i1+nMerge<=nBuf ); for(iStore=i1; iStorebase.iRowid-(u64)iLastRowid, p1, &doclist); + iLastRowid = p1->base.iRowid; } - - xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); - iLastRowid = p1->base.iRowid; - } - - assert( (nBuf%nMerge)==0 ); - for(i=0; irc==SQLITE_OK ){ - xMerge(p, &doclist, nMerge, &aBuf[i]); + + assert( (nBuf%nMerge)==0 ); + for(i=0; irc==SQLITE_OK ){ + xMerge(p, &doclist, nMerge, &aBuf[i]); + } + for(iFree=i; iFreep = (u8*)&pData[1]; + pData->nn = pData->szLeaf = doclist.n; + if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); + if( pMap ) fts5TokenMapHashify(p, pMap); + fts5MultiIterNew2(p, pData, pMap, bDesc, ppIter); + pMap = 0; } + fts5BufferFree(&doclist); } - fts5MultiIterFree(p1); - - pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING); - if( pData ){ - pData->p = (u8*)&pData[1]; - pData->nn = pData->szLeaf = doclist.n; - if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); - if( pMap ) fts5TokenMapHashify(p, pMap); - fts5MultiIterNew2(p, pData, pMap, bDesc, ppIter); - } - fts5BufferFree(&doclist); } + fts5TokenMapFree(pMap); fts5StructureRelease(pStruct); sqlite3_free(aBuf); } @@ -6514,6 +6615,7 @@ int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); + sqlite3_finalize(p->pIdxProbe); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); @@ -6766,7 +6868,7 @@ int sqlite3Fts5IterToken( ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; if( pIter->pTokenMap ){ - *ppOut = fts5TokenMapLookup( + *ppOut = (const char*)fts5TokenMapLookup( pIter->pTokenMap, pIndexIter->iRowid, iCol, iOff, pnOut ); }else{ diff --git a/ext/fts5/test/fts5origintext2.test b/ext/fts5/test/fts5origintext2.test new file mode 100644 index 0000000000..7cf8d80071 --- /dev/null +++ b/ext/fts5/test/fts5origintext2.test @@ -0,0 +1,107 @@ +# 2014 Jan 08 +# +# 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. +# +#*********************************************************************** +# +# Tests focused on phrase queries. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5origintext + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +sqlite3_fts5_register_origintext db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", tokendata=1 + ); +} + +do_execsql_test 1.1 { + BEGIN; + INSERT INTO ft VALUES('Hello'); + INSERT INTO ft VALUES('hello'); + INSERT INTO ft VALUES('HELLO'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('World'); + INSERT INTO ft VALUES('world'); + INSERT INTO ft VALUES('WORLD'); + COMMIT; +} + +do_execsql_test 1.2 { SELECT rowid FROM ft('hello'); } {1 2 3} +do_execsql_test 1.3 { SELECT rowid FROM ft('today'); } {4 5 6} +do_execsql_test 1.4 { SELECT rowid FROM ft('world'); } {7 8 9} + +do_execsql_test 1.5 { + SELECT count(*) FROM ft_data +} 3 + +do_execsql_test 1.6 { + DELETE FROM ft; + INSERT INTO ft(ft, rank) VALUES('pgsz', 64); + BEGIN; + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 + ) + INSERT INTO ft SELECT 'Hello Hello Hello Hello Hello Hello Hello' FROM s; + INSERT INTO ft VALUES ('hELLO hELLO hELLO'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('today today today today today today today'); + INSERT INTO ft VALUES('World World World World World World World'); + INSERT INTO ft VALUES('world world world world world world world'); + INSERT INTO ft VALUES('WORLD WORLD WORLD WORLD WORLD WORLD WORLD'); + INSERT INTO ft VALUES('World World World World World World World'); + INSERT INTO ft VALUES('world world world world world world world'); + INSERT INTO ft VALUES('WORLD WORLD WORLD WORLD WORLD WORLD WORLD'); + COMMIT; +} + +do_execsql_test 1.7 { + SELECT count(*) FROM ft_data; +} 23 + +do_execsql_test 1.8 { SELECT rowid FROM ft('hello') WHERE rowid>100; } {101} + +do_execsql_test 1.9 { + DELETE FROM ft; + INSERT INTO ft(ft) VALUES('optimize'); + SELECT count(*) FROM ft_data; +} {2} +do_execsql_test 1.10 { + BEGIN; + INSERT INTO ft VALUES('Hello'); + INSERT INTO ft VALUES('hello'); + INSERT INTO ft VALUES('HELLO'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('today'); + INSERT INTO ft VALUES('World'); + INSERT INTO ft VALUES('world'); + INSERT INTO ft VALUES('WORLD'); +} + +breakpoint +do_execsql_test 1.11 { SELECT rowid FROM ft('hello'); } {1 2 3} +do_execsql_test 1.12 { SELECT rowid FROM ft('today'); } {4 5 6} +do_execsql_test 1.13 { SELECT rowid FROM ft('world'); } {7 8 9} + +finish_test + diff --git a/manifest b/manifest index 3477a07b6e..7763b46700 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\simplementation\sof\sxInstToken()\sAPI. -D 2023-11-15T11:45:19.681 +C When\squerying\sa\stokendata=1\sfts5\stable,\sdo\snot\suse\sa\sprefix\scursor\sfor\sthe\scase\swhere\sthe\sterm\shas\sonly\sone\svariant. +D 2023-11-16T21:11:56.608 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,7 +94,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c 4b50ed0c724cb160f086e20e964ed2d57b99d0d3c1cb1b029901c0300b11bd9f F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 3b51c2f0554a665694e777c8f2765cb5b1283d4bc960dde350a604af3e5e5d98 +F ext/fts5/fts5_index.c 70fa4a6d8a062ca4b63a62d0721d72ce2f6336413c6e8b0703881c708797d24d F ext/fts5/fts5_main.c f151eb2c6d27418d907c88cd623ad4508bdcf518a79d504e850270754c228b74 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -189,6 +189,7 @@ F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca F ext/fts5/test/fts5origintext.test 908a1fb6b1106e4b6ed0f9cf683c2ad7f986cce1aea1e0a13b3309c6f568932b +F ext/fts5/test/fts5origintext2.test a654c77f1548ccd8eab7f6d07230655c0070cdf32dcd4740ccdf496f77d5282c F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2143,8 +2144,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 828566392b3ea8db603cb1ae5eccbc8ac035efaa284bc7c15ba89874f634aec9 -R 7870d9470a55737470bd92d95fe480a9 +P a34b26fe7f60b74e7ae5cf64900920a3d352a20da2496401bcbc27041689cd07 +R d7c277a055a404d272fdcb5090bf371a U dan -Z d10d6cf5b22c051f4553454e4a3996a4 +Z 0e1bf556ad9eba9db356685a09c7ab31 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 96d818fc70..6373f95ef5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a34b26fe7f60b74e7ae5cf64900920a3d352a20da2496401bcbc27041689cd07 \ No newline at end of file +d711c96ba855686d6881a50498418de3492144f005684b5ae55bca24413dce47 \ No newline at end of file From c15133e82ac229e9954a9ad45b625ea3ea854eec Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 17 Nov 2023 11:58:26 +0000 Subject: [PATCH 212/347] Fix an incorrect assert() associated with ALTER TABLE where an unknown aggregate function contains an ORDER BY clause. dbsqlfuzz e0900262dadd5c78c2226ad6a435c7f0255be2cd. FossilOrigin-Name: a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb --- manifest | 17 ++++++++--------- manifest.uuid | 2 +- src/select.c | 3 ++- test/alter.test | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 63250e464d..f257f8a1aa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Changes\sso\sthat\sif\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined\sas\s2\sinstead\sof\s1,\sall\sblocking\slocks\sare\staken\sfor\sa\ssingle\smillisecond\sand\sthe\sdefault\sbusy-handler\sinvoked\sas\snormal. -D 2023-11-15T19:19:04.747 +C Fix\san\sincorrect\sassert()\sassociated\swith\sALTER\sTABLE\swhere\san\sunknown\naggregate\sfunction\scontains\san\sORDER\sBY\sclause.\ndbsqlfuzz\se0900262dadd5c78c2226ad6a435c7f0255be2cd. +D 2023-11-17T11:58:26.075 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -725,7 +725,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 503331aca8785254a7bf3d74ab338a99118fa297e1184a4dde33b3cdf7a9d341 +F src/select.c b3e8bb88f7a9572200fdb89cb1ef31605f45b7b2eb876d12d5102c068de905f0 F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -824,7 +824,7 @@ F test/aggnested.test ce85a6af7d59c3109e35c5f03b2cd11da1a9b1417371e2f942102d0f0d F test/aggorderby.test e6b98dbbf3ababa96892435d387de2dcf602ef02c2b848d2d817473066f154ba F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13 -F test/alter.test 403a7f8842457044a994d0ffb42963d6e84fcfbf5e8f54556063b25d966cd454 +F test/alter.test 5d3c2a662c54362193b08961f6a65178c434905ec0fda8e0a86ebd99ea04d3cc F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807687 F test/alter3.test ffc4ab29ce78a3517a66afd69b2730667e3471622509c283b2bd4c46f680fba3 F test/alter4.test 716caa071dd8a3c6d57225778d15d3c3cbf5e34b2e84ae44199aeb2bbf50a707 @@ -2140,9 +2140,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 fe5cc4169c5348324e863d3e339de94ed01749a3b9c86aa9adc5e3244bd9d033 9c2b4bdd03716bf492ba85198717f3084ebf187bdb068893bd1ff8662362df89 -R e462fc60136fbed1193c075a92c8ac8f -T +closed 9c2b4bdd03716bf492ba85198717f3084ebf187bdb068893bd1ff8662362df89 -U dan -Z 15065b86474a02479c391229a41be9ef +P 79e24ec3dd40373bbb93792829b18d9ef40daf19d4606174e36c8e19e61a7529 +R b29e959af1ddf305b69d7ebe92a6cddd +U drh +Z 00bba4de3942374c4ed33ae6871ad316 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7812733744..b54c03c181 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -79e24ec3dd40373bbb93792829b18d9ef40daf19d4606174e36c8e19e61a7529 \ No newline at end of file +a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb \ No newline at end of file diff --git a/src/select.c b/src/select.c index 2b28d9ca5e..f6a9526776 100644 --- a/src/select.c +++ b/src/select.c @@ -6372,10 +6372,11 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ SrcList *pTabList; SrcItem *pFrom; - assert( p->selFlags & SF_Resolved ); if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; + testcase( (p->selFlags & SF_Resolved)==0 ); + assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; diff --git a/test/alter.test b/test/alter.test index ee8e6c0b90..da562ce316 100644 --- a/test/alter.test +++ b/test/alter.test @@ -954,4 +954,21 @@ do_catchsql_test alter-20.3 { INSERT INTO t1(a) VALUES(45); } {1 {cannot store BLOB value in TEXT column t1.b}} +# 2023-11-17 dbsqlfuzz e0900262dadd5c78c2226ad6a435c7f0255be2cd +# Assertion fault associated with ALTER TABLE and an +# aggregate ORDER BY within an unknown aggregate function. +# +reset_db +do_execsql_test alter-21.1 { + CREATE TABLE t1(a,b,c,d); + CREATE TABLE t2(a,b,c,d,x); + CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN + SELECT unknown_function(a ORDER BY (SELECT group_concat(DISTINCT a ORDER BY a) FROM t1)) FROM t1; + END; + ALTER TABLE t2 RENAME TO e; +} {} +do_execsql_test alter-21.2 { + SELECT name, type FROM sqlite_schema ORDER BY name; +} {e table r1 trigger t1 table} + finish_test From f2bb6ab3fb5a67f2408e1bce102cd75ec51a44cd Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 17 Nov 2023 12:22:42 +0000 Subject: [PATCH 213/347] Fix harmless compiler warnings in debugging code. FossilOrigin-Name: ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbe.c | 3 ++- src/vdbeaux.c | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index f257f8a1aa..2c1dd24e5f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sincorrect\sassert()\sassociated\swith\sALTER\sTABLE\swhere\san\sunknown\naggregate\sfunction\scontains\san\sORDER\sBY\sclause.\ndbsqlfuzz\se0900262dadd5c78c2226ad6a435c7f0255be2cd. -D 2023-11-17T11:58:26.075 +C Fix\sharmless\scompiler\swarnings\sin\sdebugging\scode. +D 2023-11-17T12:22:42.597 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -795,11 +795,11 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c b22cc9f203a8c0b9ee5338a67f8860347d14845864c10248bebe84518a781677 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 -F src/vdbe.c 7034cf3eec0c905df753368efbcdd96377fca0245584e66766ec47a29fe468c8 +F src/vdbe.c 04b827a4ef155cb529bb36f828d46407eeb39cbfa4ce6a5e6eb862040d247e59 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c b07df805110dc6e81f2a3f9cd4e83f56ea523277a59bcec489a12b740c1079e7 -F src/vdbeaux.c f3997b5956c8d97bd2fc3392db42caecddfa6549e9df82e0a7e5804653ca475a +F src/vdbeaux.c b34dfbc09403ccb676608da16ff0780d23d466470563d24fdf6350b8d2271d5e F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 F src/vdbesort.c 237840ca1947511fa59bd4e18b9eeae93f2af2468c34d2427b059f896230a547 @@ -2140,8 +2140,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 79e24ec3dd40373bbb93792829b18d9ef40daf19d4606174e36c8e19e61a7529 -R b29e959af1ddf305b69d7ebe92a6cddd +P a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb +R dc0c90fa6e9a7208b4b533ec9a5980a8 U drh -Z 00bba4de3942374c4ed33ae6871ad316 +Z 5cf7a41ba5c0eb3c7c4392bc0e2744d2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b54c03c181..98bef54da7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb \ No newline at end of file +ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 544c8d8457..4ea5080323 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -132,11 +132,12 @@ int sqlite3_found_count = 0; ** sqlite3CantopenError(lineno) */ static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ - static int n = 0; + static u64 n = 0; (void)pc; (void)pOp; (void)v; n++; + if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */ } #endif diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 1d9921b193..114a759c9a 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -211,10 +211,11 @@ static int growOpArray(Vdbe *v, int nOp){ ** sqlite3CantopenError(lineno) */ static void test_addop_breakpoint(int pc, Op *pOp){ - static int n = 0; + static u64 n = 0; (void)pc; (void)pOp; n++; + if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */ } #endif From 735e7ee62ab6a9780054a5f3328471fc23f5b0be Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 17 Nov 2023 17:10:37 +0000 Subject: [PATCH 214/347] When SQLITE_ENABLE_SETLK_TIMEOUT is defined, use a separate mutex in os_unix.c for each shm locking slot. FossilOrigin-Name: 4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36 --- manifest | 17 ++-- manifest.uuid | 2 +- src/os_unix.c | 223 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 163 insertions(+), 79 deletions(-) diff --git a/manifest b/manifest index 2c1dd24e5f..21392b6b04 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings\sin\sdebugging\scode. -D 2023-11-17T12:22:42.597 +C When\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined,\suse\sa\sseparate\smutex\sin\sos_unix.c\sfor\seach\sshm\slocking\sslot. +D 2023-11-17T17:10:37.075 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -709,7 +709,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c f8a557ff5b387ec599e8b84b7341e5a45ebdd579da03788364a34b9a9567faeb +F src/os_unix.c 1f6a930e8469b3709728690d089b55deb2fe605ba860941ddc21a2345e51bce4 F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334 @@ -2140,8 +2140,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb -R dc0c90fa6e9a7208b4b533ec9a5980a8 -U drh -Z 5cf7a41ba5c0eb3c7c4392bc0e2744d2 +P ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 +R 277be03c0e13cb54ac8c8a3d23aba815 +T *branch * unix-setlk-timeout-mutexes +T *sym-unix-setlk-timeout-mutexes * +T -sym-trunk * +U dan +Z 8cc8841ac15d12b07622be01120eb3c1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 98bef54da7..5124df900b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 \ No newline at end of file +4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index 3171b41fd6..82c81a4b12 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4313,6 +4313,25 @@ static int unixGetpagesize(void){ ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. +** +** aLock[SQLITE_SHM_NLOCK]: +** This array records the various locks held by clients on each of the +** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no +** locks are held by the process on this slot. If it is set to -1, then +** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[] +** value is set to a positive value, then it is the number of shared +** locks currently held on the slot. +** +** aMutex[SQLITE_SHM_NLOCK]: +** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex +** pShmMutex is used to protect the aLock[] array and the right to +** call fcntl() on unixShmNode.hShm to obtain or release locks. +** +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array +** of mutexes - one for each locking slot. To read or write locking +** slot aLock[iSlot], the caller must hold the corresponding mutex +** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a +** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ @@ -4326,10 +4345,11 @@ struct unixShmNode { char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK]; +#endif int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ #ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ u8 nextShmId; /* Next available unixShm.id value */ #endif }; @@ -4412,16 +4432,29 @@ static int unixShmSystemLock( struct flock f; /* The posix advisory locking structure */ int rc = SQLITE_OK; /* Result code form fcntl() */ - /* Access to the unixShmNode object is serialized by the caller */ pShmNode = pFile->pInode->pShmNode; - assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); - assert( pShmNode->nRef>0 || unixMutexHeld() ); + + /* Assert that the correct mutex or mutexes are held. */ + if( pShmNode->nRef==0 ){ + assert( ofst==UNIX_SHM_DMS && n==1 && unixMutexHeld() ); + }else{ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + int ii; + for(ii=ofst-UNIX_SHM_BASE; iiaMutex[ii]) ); + } +#else + assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); + assert( pShmNode->nRef>0 ); +#endif + } /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<=SQLITE_SHM_NLOCK ); + assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) ); if( pShmNode->hShm>=0 ){ int res; @@ -4440,39 +4473,28 @@ static int unixShmSystemLock( } } - /* Update the global lock state and do debug tracing */ + /* Do debug tracing */ #ifdef SQLITE_DEBUG - { u16 mask; OSTRACE(("SHM-LOCK ")); - mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; - pShmNode->sharedMask &= ~mask; + OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock %d ok", ofst)); - pShmNode->exclMask &= ~mask; - pShmNode->sharedMask |= mask; + OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d ok", ofst)); - pShmNode->exclMask |= mask; - pShmNode->sharedMask &= ~mask; + OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1)); } }else{ if( lockType==F_UNLCK ){ - OSTRACE(("unlock %d failed", ofst)); + OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); + OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d failed", ofst)); + OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1)); } } - OSTRACE((" - afterwards %03x,%03x\n", - pShmNode->sharedMask, pShmNode->exclMask)); - } #endif return rc; @@ -4509,6 +4531,11 @@ static void unixShmPurge(unixFile *pFd){ int i; assert( p->pInode==pFd->pInode ); sqlite3_mutex_free(p->pShmMutex); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + for(i=0; iaMutex[i]); + } +#endif for(i=0; inRegion; i+=nShmPerMap){ if( p->hShm>=0 ){ osMunmap(p->apRegion[i], p->szRegion); @@ -4689,6 +4716,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + { + int ii; + for(ii=0; iiaMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pShmNode->aMutex[ii]==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto shm_open_err; + } + } + } +#endif } if( pInode->bProcessLock==0 ){ @@ -4910,9 +4949,11 @@ shmpage_out: */ #ifdef SQLITE_DEBUG static int assertLockingArrayOk(unixShmNode *pShmNode){ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + return 1; +#else unixShm *pX; int aLock[SQLITE_SHM_NLOCK]; - assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); memset(aLock, 0, sizeof(aLock)); for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ @@ -4930,13 +4971,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){ assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); +#endif } #endif /* ** Change the lock state for a shared-memory segment. ** -** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** Note that the relationship between SHARED and EXCLUSIVE locks is a little ** different here than in posix. In xShmLock(), one can go from unlocked ** to shared and back or from unlocked to exclusive and back. But one may ** not go from shared to exclusive or from exclusive to shared. @@ -4951,7 +4993,7 @@ static int unixShmLock( unixShm *p; /* The shared memory being locked */ unixShmNode *pShmNode; /* The underlying file iNode */ int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ + u16 mask = (1<<(ofst+n)) - (1<pShm; @@ -4997,18 +5039,51 @@ static int unixShmLock( } #endif - mask = (1<<(ofst+n)) - (1<1 || mask==(1<pShmMutex); - assert( assertLockingArrayOk(pShmNode) ); - if( flags & SQLITE_SHM_UNLOCK ){ - if( (p->exclMask|p->sharedMask) & mask ){ - int ii; - int bUnlock = 1; + /* Check if there is any work to do. There are three cases: + ** + ** a) An unlock operation where there are locks to unlock, + ** b) An shared lock where the requested lock is not already held + ** c) An exclusive lock where the requested lock is not already held + ** + ** The SQLite core never requests an exclusive lock that it already holds. + ** This is assert()ed below. + */ + assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) + || 0==(p->exclMask & mask) + ); + if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) + || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) + || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) + ){ - for(ii=ofst; ii((p->sharedMask & (1<aMutex[iMutex]); + } +#else + sqlite3_mutex_enter(pShmNode->pShmMutex); +#endif + + if( flags & SQLITE_SHM_UNLOCK ){ + /* Case (a) - unlock. */ + int bUnlock = 1; + assert( (p->exclMask & p->sharedMask)==0 ); + assert( (flags & SQLITE_SHM_EXCLUSIVE)==0 || (p->exclMask & mask)==mask ); + assert( (flags & SQLITE_SHM_SHARED)==0 || (p->sharedMask & mask)==mask ); + + /* If this is a SHARED lock being unlocked, it is possible that other + ** clients within this process are holding the same SHARED lock. In + ** this case, set bUnlock to 0 so that the posix lock is not removed + ** from the file-descriptor below. */ + if( flags & SQLITE_SHM_SHARED ){ + assert( n==1 ); + assert( aLock[ofst]>=1 ); + if( aLock[ofst]>1 ){ bUnlock = 0; + aLock[ofst]--; + p->sharedMask &= ~mask; } } @@ -5016,23 +5091,15 @@ static int unixShmLock( rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ memset(&aLock[ofst], 0, sizeof(int)*n); + p->sharedMask &= ~mask; + p->exclMask &= ~mask; } - }else if( ALWAYS(p->sharedMask & (1<1 ); - aLock[ofst]--; } + }else if( flags & SQLITE_SHM_SHARED ){ + /* Case (b) - a shared lock. */ - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - } - }else if( flags & SQLITE_SHM_SHARED ){ - assert( n==1 ); - assert( (p->exclMask & (1<sharedMask & mask)==0 ){ if( aLock[ofst]<0 ){ + /* An exclusive lock is held by some other connection. BUSY. */ rc = SQLITE_BUSY; }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); @@ -5043,34 +5110,48 @@ static int unixShmLock( p->sharedMask |= mask; aLock[ofst]++; } - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. */ - int ii; - for(ii=ofst; iisharedMask & mask)==0 ); - if( ALWAYS((p->exclMask & (1<sharedMask & mask)==0 ); + assert( (p->exclMask & mask)==0 ); + + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. */ + for(ii=ofst; iisharedMask & mask)==0 ); - p->exclMask |= mask; - for(ii=ofst; iiexclMask |= mask; + for(ii=ofst; iiaMutex[iMutex]); + } +#else + sqlite3_mutex_leave(pShmNode->pShmMutex); +#endif } - assert( assertLockingArrayOk(pShmNode) ); - sqlite3_mutex_leave(pShmNode->pShmMutex); + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; From 003d419b33c6ee608901a9e2d04c2e402e7cec28 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 17 Nov 2023 17:25:30 +0000 Subject: [PATCH 215/347] Add an assert() to prove the sqlite3_prepare() always either returns SQLITE_OK or else leaves *ppStmt set to NULL. See [forum:/forumpost/70bb8576c6c084c2|forum post 70bb8576c6c084c2]. FossilOrigin-Name: feadd4024228e578811447c4b2d2b60780ff3d3681f80ca903585aa7b289d758 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/prepare.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 2c1dd24e5f..a1d7b763f5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings\sin\sdebugging\scode. -D 2023-11-17T12:22:42.597 +C Add\san\sassert()\sto\sprove\sthe\ssqlite3_prepare()\salways\seither\sreturns\nSQLITE_OK\sor\selse\sleaves\s*ppStmt\sset\sto\sNULL.\s\sSee\n[forum:/forumpost/70bb8576c6c084c2|forum\spost\s70bb8576c6c084c2]. +D 2023-11-17T17:25:30.458 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -720,7 +720,7 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c b3b4ad9c0298d63098a067acca613c21a5f56b4d176d5842922bcd0b07b7164e F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 -F src/prepare.c bde74add20fc0e8ce0c4e937a1f70a36d17413afe4f71d3e103f5cb74b17c8d9 +F src/prepare.c 371f6115cb69286ebc12c6f2d7511279c2e47d9f54f475d46a554d687a3b312c F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 @@ -2140,8 +2140,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 a9443dbfbe25e588b4adddde664ddf482f19f71c704fbf356d49cf3a6135e7fb -R dc0c90fa6e9a7208b4b533ec9a5980a8 +P ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 +R 0df7c74767a0014b650e41ddac5ac20d U drh -Z 5cf7a41ba5c0eb3c7c4392bc0e2744d2 +Z 3d905d6e432172a6f350f89a5634e3bc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 98bef54da7..75e0636b11 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 \ No newline at end of file +feadd4024228e578811447c4b2d2b60780ff3d3681f80ca903585aa7b289d758 \ No newline at end of file diff --git a/src/prepare.c b/src/prepare.c index d3e134e764..87569ee91d 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -868,6 +868,7 @@ static int sqlite3LockAndPrepare( assert( (rc&db->errMask)==rc ); db->busyHandler.nBusy = 0; sqlite3_mutex_leave(db->mutex); + assert( rc==SQLITE_OK || (*ppStmt)==0 ); return rc; } From f9ae48b9ede6fedb3cac4027f0a1e3840c763955 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 17 Nov 2023 17:55:03 +0000 Subject: [PATCH 216/347] Add the new "run-fuzzcheck" target on the autoconf unix makefile. Requires that FUZZDB be set to the name of the fuzz-data database file. FossilOrigin-Name: 8fa3915dbfdd9a5b0fcf4d695590b3de7e7d19d2917924186ca889cd60bf0eb9 --- Makefile.in | 15 +++++++++++++++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Makefile.in b/Makefile.in index c521586a14..4a78e3cf38 100644 --- a/Makefile.in +++ b/Makefile.in @@ -710,6 +710,21 @@ fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP) $(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS) +# Usage: FUZZDB=filename make run-fuzzcheck +# +# Where filename is a fuzzcheck database, this target builds and runs +# fuzzcheck, fuzzcheck-asan, and fuzzcheck-ubsan on that database. +# +# FUZZDB can be a glob pattern of two or more databases. Example: +# +# FUZZDB=test/fuzzdata*.db make run-fuzzcheck +# +run-fuzzcheck: fuzzcheck$(TEXE) fuzzcheck-asan$(TEXE) fuzzcheck-ubsan$(TEXE) + @if test "$(FUZZDB)" = ""; then echo 'ERROR: No FUZZDB specified. Rerun with FUZZDB=filename'; exit 1; fi + ./fuzzcheck$(TEXE) --spinner $(FUZZDB) + ./fuzzcheck-asan$(TEXE) --spinner $(FUZZDB) + ./fuzzcheck-ubsan$(TEXE) --spinner $(FUZZDB) + ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \ $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) diff --git a/manifest b/manifest index a1d7b763f5..8a6ff75125 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Add\san\sassert()\sto\sprove\sthe\ssqlite3_prepare()\salways\seither\sreturns\nSQLITE_OK\sor\selse\sleaves\s*ppStmt\sset\sto\sNULL.\s\sSee\n[forum:/forumpost/70bb8576c6c084c2|forum\spost\s70bb8576c6c084c2]. -D 2023-11-17T17:25:30.458 +C Add\sthe\snew\s"run-fuzzcheck"\starget\son\sthe\sautoconf\sunix\smakefile.\s\sRequires\nthat\sFUZZDB\sbe\sset\sto\sthe\sname\sof\sthe\sfuzz-data\sdatabase\sfile. +D 2023-11-17T17:55:03.500 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 8b59912fc1538f96a08555605c5886cdcc733696ae7f22e374b2a4752196ca20 +F Makefile.in a0cf17b2a456ae24959030b979bc6f1ec5efc74a240fb834c12e63dd1127cd72 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc f0cf219350d9af4fba411b4f6306dce2adc897484e8f446de1fb4f40de674d00 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 @@ -2140,8 +2140,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 ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 -R 0df7c74767a0014b650e41ddac5ac20d +P feadd4024228e578811447c4b2d2b60780ff3d3681f80ca903585aa7b289d758 +R b258b9f86fa511b15c0d194695659eb8 U drh -Z 3d905d6e432172a6f350f89a5634e3bc +Z 85994133c2347299e70cb8542787884c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 75e0636b11..ab13a71e1c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -feadd4024228e578811447c4b2d2b60780ff3d3681f80ca903585aa7b289d758 \ No newline at end of file +8fa3915dbfdd9a5b0fcf4d695590b3de7e7d19d2917924186ca889cd60bf0eb9 \ No newline at end of file From b8688e131e34655491538b35aabd861181d64b6a Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 18 Nov 2023 11:23:01 +0000 Subject: [PATCH 217/347] Another assertion fault fix, similar to [a9443dbfbe25e588]. dbsqlfuzz 2d9af4e94aca188e0092900eec711401c5d51687. FossilOrigin-Name: 3afaeac56dff58db596360bf6f8dca97cb31405f73be8e189e8c0e6a1e5b239d --- manifest | 17 ++++++++--------- manifest.uuid | 2 +- src/select.c | 3 ++- test/alter.test | 12 ++++++++++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index d410f901e3..7dd2b95095 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sSQLITE_ENABLE_SETLK_TIMEOUT\sis\sdefined,\suse\sa\sseparate\smutex\sin\sos_unix.c\sfor\seach\sshm\slocking\sslot. -D 2023-11-17T19:01:38.613 +C Another\sassertion\sfault\sfix,\ssimilar\sto\s[a9443dbfbe25e588].\s\sdbsqlfuzz\n2d9af4e94aca188e0092900eec711401c5d51687. +D 2023-11-18T11:23:01.258 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -725,7 +725,7 @@ F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c b3e8bb88f7a9572200fdb89cb1ef31605f45b7b2eb876d12d5102c068de905f0 +F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -824,7 +824,7 @@ F test/aggnested.test ce85a6af7d59c3109e35c5f03b2cd11da1a9b1417371e2f942102d0f0d F test/aggorderby.test e6b98dbbf3ababa96892435d387de2dcf602ef02c2b848d2d817473066f154ba F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13 -F test/alter.test 5d3c2a662c54362193b08961f6a65178c434905ec0fda8e0a86ebd99ea04d3cc +F test/alter.test 3c00eff1e2036b9f93e9cd0f3d3e63750ac87ecb5bc71b9d7bd07cbf2ac4c494 F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807687 F test/alter3.test ffc4ab29ce78a3517a66afd69b2730667e3471622509c283b2bd4c46f680fba3 F test/alter4.test 716caa071dd8a3c6d57225778d15d3c3cbf5e34b2e84ae44199aeb2bbf50a707 @@ -2140,9 +2140,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 8fa3915dbfdd9a5b0fcf4d695590b3de7e7d19d2917924186ca889cd60bf0eb9 4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36 -R 884416859de09ce6a1726fd2a660e929 -T +closed 4098df9652d90f2d22d5591d915d672c5413471f7916223510ba6fd932bfdd36 -U dan -Z 1df6f9700e3b6c8de400aaea321ae923 +P 64691df980cdf73ddc00aabe15baa6c018e1d76b0e87f55ab33a9348fd5680bd +R 8af98c097067dfe83db584d766ed658e +U drh +Z 7563e749ca2ec0ee7c02d0cc723df3ee # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index eeddf6891e..0be11c80c6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64691df980cdf73ddc00aabe15baa6c018e1d76b0e87f55ab33a9348fd5680bd \ No newline at end of file +3afaeac56dff58db596360bf6f8dca97cb31405f73be8e189e8c0e6a1e5b239d \ No newline at end of file diff --git a/src/select.c b/src/select.c index f6a9526776..e5312c7ee7 100644 --- a/src/select.c +++ b/src/select.c @@ -2319,7 +2319,8 @@ void sqlite3SubqueryColumnTypes( NameContext sNC; assert( pSelect!=0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 ); + testcase( (pSelect->selFlags & SF_Resolved)==0 ); + assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed || IN_RENAME_OBJECT ) return; diff --git a/test/alter.test b/test/alter.test index da562ce316..9201f40adc 100644 --- a/test/alter.test +++ b/test/alter.test @@ -970,5 +970,17 @@ do_execsql_test alter-21.1 { do_execsql_test alter-21.2 { SELECT name, type FROM sqlite_schema ORDER BY name; } {e table r1 trigger t1 table} +do_execsql_test alter-21.3 { + DROP TRIGGER r1; + CREATE TRIGGER r2 AFTER INSERT ON e BEGIN + SELECT unknown_function(a ORDER BY (SELECT group_concat(a ORDER BY a) FROM (SELECT b FROM t1))) FROM t1; + END; + ALTER TABLE e RENAME TO t99; +} +do_execsql_test alter-21.4 { + SELECT name, type FROM sqlite_schema ORDER BY name; +} {r2 trigger t1 table t99 table} + + finish_test From 52c41ed41bcadfce1c0b5bb18501a283aedc0293 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 18 Nov 2023 12:06:21 +0000 Subject: [PATCH 218/347] Adjust an assert() in fts5WritePoslistData() so that it only applies if there have been no prior errors. dbsqlfuzz 25dca9b2568f67dc78a0e32ff280133fe71994bd. FossilOrigin-Name: 257cdbab90c6db8ccc9a8fd5df556b69c3a35a329d39cd4642c792d7359a54a5 --- ext/fts5/fts5_index.c | 2 +- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index c7c02cf6fe..f64dbbce31 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -4404,7 +4404,7 @@ static void fts5WriteAppendPoslistData( const u8 *a = aData; int n = nData; - assert( p->pConfig->pgsz>0 ); + assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK ); while( p->rc==SQLITE_OK && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz ){ diff --git a/manifest b/manifest index 7dd2b95095..b090e67c56 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Another\sassertion\sfault\sfix,\ssimilar\sto\s[a9443dbfbe25e588].\s\sdbsqlfuzz\n2d9af4e94aca188e0092900eec711401c5d51687. -D 2023-11-18T11:23:01.258 +C Adjust\san\sassert()\sin\sfts5WritePoslistData()\sso\sthat\sit\sonly\sapplies\sif\sthere\nhave\sbeen\sno\sprior\serrors.\ndbsqlfuzz\s25dca9b2568f67dc78a0e32ff280133fe71994bd. +D 2023-11-18T12:06:21.202 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,7 +94,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 F ext/fts5/fts5_hash.c 076058f93327051952a752dc765df1acfe783eb11b419b30652aa1fc1f987902 -F ext/fts5/fts5_index.c 01b671fedd2189f6969385d96facc4c06d9c441f0f91d584386a62b724282f9f +F ext/fts5/fts5_index.c 4a44705a7edddbd2d650db5d9ed939fafaef376dbdf60e685326855a69d388bf F ext/fts5/fts5_main.c a07ed863b8bd9e6fefb62db2fd40a3518eb30a5f7dcfda5be915dd2db45efa2f F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae @@ -2140,8 +2140,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 64691df980cdf73ddc00aabe15baa6c018e1d76b0e87f55ab33a9348fd5680bd -R 8af98c097067dfe83db584d766ed658e +P 3afaeac56dff58db596360bf6f8dca97cb31405f73be8e189e8c0e6a1e5b239d +R 9cf44b62577ea28a6c89efc96ebe6c29 U drh -Z 7563e749ca2ec0ee7c02d0cc723df3ee +Z 76fce07162fdeb3d4e5e596aa33e5ef1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0be11c80c6..f8f8ebe658 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3afaeac56dff58db596360bf6f8dca97cb31405f73be8e189e8c0e6a1e5b239d \ No newline at end of file +257cdbab90c6db8ccc9a8fd5df556b69c3a35a329d39cd4642c792d7359a54a5 \ No newline at end of file From 046a7f94930f1c2f4af8f8ac47a3126be81980c7 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 18 Nov 2023 17:20:04 +0000 Subject: [PATCH 219/347] When ENABLE_SETLK is defined, avoid ever blocking on the lock mutex in os_unix.c when requesting an exclusive lock. FossilOrigin-Name: eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe --- manifest | 14 ++--- manifest.uuid | 2 +- src/os_unix.c | 158 ++++++++++++++++++++++++++++---------------------- 3 files changed, 97 insertions(+), 77 deletions(-) diff --git a/manifest b/manifest index b090e67c56..448e007095 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Adjust\san\sassert()\sin\sfts5WritePoslistData()\sso\sthat\sit\sonly\sapplies\sif\sthere\nhave\sbeen\sno\sprior\serrors.\ndbsqlfuzz\s25dca9b2568f67dc78a0e32ff280133fe71994bd. -D 2023-11-18T12:06:21.202 +C When\sENABLE_SETLK\sis\sdefined,\savoid\sever\sblocking\son\sthe\slock\smutex\sin\sos_unix.c\swhen\srequesting\san\sexclusive\slock. +D 2023-11-18T17:20:04.644 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -709,7 +709,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c 1f6a930e8469b3709728690d089b55deb2fe605ba860941ddc21a2345e51bce4 +F src/os_unix.c dc5404b56da7fb13cf272ddb94c3753cf9e82d32a65cba35dbb6aadcb849419c F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334 @@ -2140,8 +2140,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 3afaeac56dff58db596360bf6f8dca97cb31405f73be8e189e8c0e6a1e5b239d -R 9cf44b62577ea28a6c89efc96ebe6c29 -U drh -Z 76fce07162fdeb3d4e5e596aa33e5ef1 +P 257cdbab90c6db8ccc9a8fd5df556b69c3a35a329d39cd4642c792d7359a54a5 +R c4cd41341f8782719a82292d54af9847 +U dan +Z d62588e5cdbd2512931321f6949cd27c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f8f8ebe658..92300531cb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -257cdbab90c6db8ccc9a8fd5df556b69c3a35a329d39cd4642c792d7359a54a5 \ No newline at end of file +eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index 82c81a4b12..dab03c97fb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5056,95 +5056,115 @@ static int unixShmLock( || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) ){ - /* Take the required mutexes */ + /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if + ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any + ** other thread is holding this mutex, then it is either holding or about + ** to hold a lock exclusive to the one being requested, and we may + ** therefore return SQLITE_BUSY to the caller. + ** + ** Doing this prevents some deadlock scenarios. For example, thread 1 may + ** be a checkpointer blocked waiting on the WRITER lock. And thread 2 + ** may be a normal SQL client upgrading to a write transaction. In this + ** case thread 2 does a non-blocking request for the WRITER lock. But - + ** if it were to use sqlite3_mutex_enter() then it would effectively + ** become a (doomed) blocking request, as thread 2 would block until thread + ** 1 obtained WRITER and released the mutex. Since thread 2 already holds + ** a lock on a read-locking slot at this point, this breaks the + ** anti-deadlock rules (see above). */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT int iMutex; for(iMutex=ofst; iMutexaMutex[iMutex]); + if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){ + rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]); + if( rc!=SQLITE_OK ) break; + }else{ + sqlite3_mutex_enter(pShmNode->aMutex[iMutex]); + } } #else sqlite3_mutex_enter(pShmNode->pShmMutex); #endif - if( flags & SQLITE_SHM_UNLOCK ){ - /* Case (a) - unlock. */ - int bUnlock = 1; - assert( (p->exclMask & p->sharedMask)==0 ); - assert( (flags & SQLITE_SHM_EXCLUSIVE)==0 || (p->exclMask & mask)==mask ); - assert( (flags & SQLITE_SHM_SHARED)==0 || (p->sharedMask & mask)==mask ); - - /* If this is a SHARED lock being unlocked, it is possible that other - ** clients within this process are holding the same SHARED lock. In - ** this case, set bUnlock to 0 so that the posix lock is not removed - ** from the file-descriptor below. */ - if( flags & SQLITE_SHM_SHARED ){ - assert( n==1 ); - assert( aLock[ofst]>=1 ); - if( aLock[ofst]>1 ){ - bUnlock = 0; - aLock[ofst]--; - p->sharedMask &= ~mask; + if( rc==SQLITE_OK ){ + if( flags & SQLITE_SHM_UNLOCK ){ + /* Case (a) - unlock. */ + int bUnlock = 1; + assert( (p->exclMask & p->sharedMask)==0 ); + assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); + assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); + + /* If this is a SHARED lock being unlocked, it is possible that other + ** clients within this process are holding the same SHARED lock. In + ** this case, set bUnlock to 0 so that the posix lock is not removed + ** from the file-descriptor below. */ + if( flags & SQLITE_SHM_SHARED ){ + assert( n==1 ); + assert( aLock[ofst]>=1 ); + if( aLock[ofst]>1 ){ + bUnlock = 0; + aLock[ofst]--; + p->sharedMask &= ~mask; + } } - } - - if( bUnlock ){ - rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); - if( rc==SQLITE_OK ){ - memset(&aLock[ofst], 0, sizeof(int)*n); - p->sharedMask &= ~mask; - p->exclMask &= ~mask; + + if( bUnlock ){ + rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); + if( rc==SQLITE_OK ){ + memset(&aLock[ofst], 0, sizeof(int)*n); + p->sharedMask &= ~mask; + p->exclMask &= ~mask; + } } - } - }else if( flags & SQLITE_SHM_SHARED ){ - /* Case (b) - a shared lock. */ - - if( aLock[ofst]<0 ){ - /* An exclusive lock is held by some other connection. BUSY. */ - rc = SQLITE_BUSY; - }else if( aLock[ofst]==0 ){ - rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); - } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - aLock[ofst]++; - } - }else{ - /* Case (c) - an exclusive lock. */ - int ii; - - assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ); - assert( (p->sharedMask & mask)==0 ); - assert( (p->exclMask & mask)==0 ); - - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. */ - for(ii=ofst; iiexclMask |= mask; - for(ii=ofst; iisharedMask |= mask; + aLock[ofst]++; + } + }else{ + /* Case (c) - an exclusive lock. */ + int ii; + + assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ); + assert( (p->sharedMask & mask)==0 ); + assert( (p->exclMask & mask)==0 ); + + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. */ + for(ii=ofst; iiexclMask |= mask; + for(ii=ofst; ii=ofst; iMutex--){ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); } #else From 73e3e0c21f8d14c8e41edcb813e388cfcf3b3fad Mon Sep 17 00:00:00 2001 From: mistachkin Date: Sat, 18 Nov 2023 18:36:26 +0000 Subject: [PATCH 220/347] Correct conditional compilation issue seen with MSVC in the Win32 mutex subsystem. FossilOrigin-Name: 6f8f4bfe607f1405d313bb88a33490621002f63e8c02c980f4c083630ad3a6d2 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/mutex_w32.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 448e007095..392b5ac5ad 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sENABLE_SETLK\sis\sdefined,\savoid\sever\sblocking\son\sthe\slock\smutex\sin\sos_unix.c\swhen\srequesting\san\sexclusive\slock. -D 2023-11-18T17:20:04.644 +C Correct\sconditional\scompilation\sissue\sseen\swith\sMSVC\sin\sthe\sWin32\smutex\ssubsystem. +D 2023-11-18T18:36:26.123 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -702,7 +702,7 @@ F src/mutex.c 1b4c7e5e3621b510e0c18397210be27cd54c8084141144fbbafd003fde948e88 F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c f7ee5a2061a4c11815a2bf4fc0e2bfa6fb8d9dc89390eb613ca0cec32fc9a3d1 -F src/mutex_w32.c 38b56d0bc8d54c17c20cbaaad3719b0c36b92fd07a7e34360d0c6a18d5589912 +F src/mutex_w32.c 28f8d480387db5b2ef5248705dd4e19db0cfc12c3ba426695a7d2c45c48e6885 F src/notify.c 57c2d1a2805d6dee32acd5d250d928ab94e02d76369ae057dee7d445fd64e878 F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 @@ -2140,8 +2140,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 257cdbab90c6db8ccc9a8fd5df556b69c3a35a329d39cd4642c792d7359a54a5 -R c4cd41341f8782719a82292d54af9847 -U dan -Z d62588e5cdbd2512931321f6949cd27c +P eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe +R d3865ac837f15e7bc624ba5814b306bb +U mistachkin +Z ddd4e2fcb7a611d175dfb5f8a98abd62 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 92300531cb..adf4be982a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe \ No newline at end of file +6f8f4bfe607f1405d313bb88a33490621002f63e8c02c980f4c083630ad3a6d2 \ No newline at end of file diff --git a/src/mutex_w32.c b/src/mutex_w32.c index e0e0dfb06c..7eb5b50be1 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -87,7 +87,7 @@ void sqlite3MemoryBarrier(void){ SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); -#elif MSVC_VERSION>=1300 +#elif MSVC_VERSION>=1400 _ReadWriteBarrier(); #elif defined(MemoryBarrier) MemoryBarrier(); From 4c9a02c9f07446442e77f79227981cda52c3eeb3 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 20 Nov 2023 00:20:56 +0000 Subject: [PATCH 221/347] Cherrypick shell1.test fix. FossilOrigin-Name: 39e30c5f9cc6dcac003255734e3ce1ac5b05349ea1a25e1c108b5f6d1d97612b --- manifest | 15 +++++++-------- manifest.uuid | 2 +- test/shell1.test | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 4d3bb6ee32..6c8f8636c9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sand\smake\smore\srational\show\sconsole\sI/O\spackage\sfeatures\sare\sselected.\s(Motivated\sby\sFiddle\sand\sother\scommand-line\sutilities\sto\ssoon\suse\sthe\spackage.)\sNo\ssubstantive\scode-execution\schanges. -D 2023-11-16T18:31:05.379 +C Cherrypick\sshell1.test\sfix. +D 2023-11-20T00:20:56.905 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1566,7 +1566,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21 F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 2191c892d8256b1b6cccb4fca894cfc2e748450cf422e6ec80a320a0d538b6df +F test/shell1.test 0bb36232873d7df2ba627ed25cff1332f6d98c0f7f244b425702c1ab1850bd15 F test/shell2.test 35226c070a8c7f64fd016dfac2a0db2a40f709b3131f61daacd9dad61536c9cb F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f @@ -2142,10 +2142,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 2341f9b6a48634a94e90e849f579afd07d398c3e0304efccf07d18b41a0e40aa -Q +45b8061e7568ccca164fe000f1f0a0d984b1c28fad530bc9fdea35793a0f40bc -Q +957ebaa2be3056371d679f7a00aeba242db34218fb839ffd1d1197cad0c4a510 -R dab16a36061bd95f615790a1ad678bef +P 1cde05877b7e47cb2ab9de26edd5245eb5ff1d91e69b94f3b24944fd4f6ccd3e +Q +aaa73400c41f382bbbd4ea32ad196d271c7a5e800bfee9afd998037a14fc89b9 +R eaecfb623251eb332d80f340470747cf U larrybr -Z f345bc7daef2ffd72adafc6ced1c0785 +Z 74a79fdeb1bf75d668bd72604f628ab5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 47eb0416b6..f267e543a2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1cde05877b7e47cb2ab9de26edd5245eb5ff1d91e69b94f3b24944fd4f6ccd3e \ No newline at end of file +39e30c5f9cc6dcac003255734e3ce1ac5b05349ea1a25e1c108b5f6d1d97612b \ No newline at end of file diff --git a/test/shell1.test b/test/shell1.test index 19848549ac..fb42e45cf5 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -1067,7 +1067,7 @@ do_test shell1-5.0 { # set escapes [list \ \a \\a \b \\b \t \\t \n \\n \v \\v \f \\f \r \\r \ - " " "\" \"" \" \\\" ' \"'\" \\ \\\\] + " " "\" \"" \" \\\" \\ \\\\] } else { # # NOTE: On Unix, we need to escape most of the whitespace characters From 033127af7523b172a0b9c4c66db6941d63eecc45 Mon Sep 17 00:00:00 2001 From: larrybr Date: Mon, 20 Nov 2023 00:21:24 +0000 Subject: [PATCH 222/347] Cherrypick shell1.test fix. FossilOrigin-Name: 91c888eddbcb1b461f7d562c2f0eee57f1a2be7f9959b9d69e80fc7c1da20e9e --- manifest | 15 ++++++++------- manifest.uuid | 2 +- test/shell1.test | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 392b5ac5ad..83e8d73f70 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sconditional\scompilation\sissue\sseen\swith\sMSVC\sin\sthe\sWin32\smutex\ssubsystem. -D 2023-11-18T18:36:26.123 +C Cherrypick\sshell1.test\sfix. +D 2023-11-20T00:21:24.127 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1564,7 +1564,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21 F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 2191c892d8256b1b6cccb4fca894cfc2e748450cf422e6ec80a320a0d538b6df +F test/shell1.test 0bb36232873d7df2ba627ed25cff1332f6d98c0f7f244b425702c1ab1850bd15 F test/shell2.test 35226c070a8c7f64fd016dfac2a0db2a40f709b3131f61daacd9dad61536c9cb F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f @@ -2140,8 +2140,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P eb36d475e91bfdbf4a18b6fd9751fbcecf15d960dcd1c00d2d18b5bf1d7503fe -R d3865ac837f15e7bc624ba5814b306bb -U mistachkin -Z ddd4e2fcb7a611d175dfb5f8a98abd62 +P 6f8f4bfe607f1405d313bb88a33490621002f63e8c02c980f4c083630ad3a6d2 +Q +aaa73400c41f382bbbd4ea32ad196d271c7a5e800bfee9afd998037a14fc89b9 +R 2f292a953a71e724133a873c19fe621c +U larrybr +Z 82e3c6b97cf9016d92e3b8c651c803be # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index adf4be982a..ab439a7e57 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6f8f4bfe607f1405d313bb88a33490621002f63e8c02c980f4c083630ad3a6d2 \ No newline at end of file +91c888eddbcb1b461f7d562c2f0eee57f1a2be7f9959b9d69e80fc7c1da20e9e \ No newline at end of file diff --git a/test/shell1.test b/test/shell1.test index 19848549ac..fb42e45cf5 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -1067,7 +1067,7 @@ do_test shell1-5.0 { # set escapes [list \ \a \\a \b \\b \t \\t \n \\n \v \\v \f \\f \r \\r \ - " " "\" \"" \" \\\" ' \"'\" \\ \\\\] + " " "\" \"" \" \\\" \\ \\\\] } else { # # NOTE: On Unix, we need to escape most of the whitespace characters From 56bb07bad6fbb73c0f5631a6399bf1ba627c817b Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 20 Nov 2023 11:40:18 +0000 Subject: [PATCH 223/347] Fix an assert() in fts5 that could be true following an OOM or IO error in contentless-delete mode. FossilOrigin-Name: 3fe89238c31ca163d021ef1fecde594328815aaba7053fef9e9c789562a62f45 --- ext/fts5/fts5_index.c | 3 ++- ext/fts5/test/fts5faultG.test | 26 ++++++++++++++++++++++++++ manifest | 17 ++++++++--------- manifest.uuid | 2 +- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index f64dbbce31..ba3ce6536a 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -5664,8 +5664,9 @@ int sqlite3Fts5IndexOptimize(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); - assert( p->nContentlessDelete==0 ); + assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 ); pStruct = fts5StructureRead(p); + assert( p->rc!=SQLITE_OK || pStruct!=0 ); fts5StructureInvalidate(p); if( pStruct ){ diff --git a/ext/fts5/test/fts5faultG.test b/ext/fts5/test/fts5faultG.test index bdcc153ad2..33a41dcb38 100644 --- a/ext/fts5/test/fts5faultG.test +++ b/ext/fts5/test/fts5faultG.test @@ -46,5 +46,31 @@ do_faultsim_test 1 -faults oom* -prep { faultsim_test_result {0 {}} } +reset_db +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE t1 USING fts5(a, content=, contentless_delete=1); + BEGIN; + INSERT INTO t1 VALUES('here''s some text'); + INSERT INTO t1 VALUES('useful stuff, text'); + INSERT INTO t1 VALUES('what would we do without text!'); + COMMIT; +} +faultsim_save_and_close +do_faultsim_test 2 -faults oom* -prep { + faultsim_restore_and_reopen + execsql { + BEGIN; + DELETE FROM t1 WHERE rowid=2; + } +} -body { + execsql { + INSERT INTO t1(t1) VALUES('optimize'); + } +} -test { + faultsim_integrity_check + faultsim_test_result {0 {}} +} + + finish_test diff --git a/manifest b/manifest index 83e8d73f70..0215f6b53c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Cherrypick\sshell1.test\sfix. -D 2023-11-20T00:21:24.127 +C Fix\san\sassert()\sin\sfts5\sthat\scould\sbe\strue\sfollowing\san\sOOM\sor\sIO\serror\sin\scontentless-delete\smode. +D 2023-11-20T11:40:18.032 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,7 +94,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 054359543566cbff1ba65a188330660a5457299513ac71c53b3a07d934c7b081 F ext/fts5/fts5_expr.c bd3b81ce669c4104e34ffe66570af1999a317b142c15fccb112de9fb0caa57a6 F ext/fts5/fts5_hash.c 076058f93327051952a752dc765df1acfe783eb11b419b30652aa1fc1f987902 -F ext/fts5/fts5_index.c 4a44705a7edddbd2d650db5d9ed939fafaef376dbdf60e685326855a69d388bf +F ext/fts5/fts5_index.c 809407c520c6afc3c1a0b8080cbd9080e4d84442f52a31654cc0e3d3acccea17 F ext/fts5/fts5_main.c a07ed863b8bd9e6fefb62db2fd40a3518eb30a5f7dcfda5be915dd2db45efa2f F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae @@ -167,7 +167,7 @@ F ext/fts5/test/fts5faultB.test d606bdb8e81aaeb6f41de3fc9fc7ae315733f0903fbff05c F ext/fts5/test/fts5faultD.test e7ed7895abfe6bc98a5e853826f6b74956e7ba7f594f1860bbf9e504b9647996 F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e4710c77eb8ce7075 F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1 -F ext/fts5/test/fts5faultG.test 340e59d2c2c1c7c379224f3968ee8d09b0f64bf56c5194217d1ded887b9d47c4 +F ext/fts5/test/fts5faultG.test d2e5a4d9a34e08dcaadcaeafef74d10cbc2abdd11aa2659a18af0294bf2812d3 F ext/fts5/test/fts5first.test 3fcf2365c00a15fc9704233674789a3b95131d12de18a9b996159f6909dc8079 F ext/fts5/test/fts5full.test e1701a112354e0ff9a1fdffb0c940c576530c33732ee20ac5e8361777070d717 F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e @@ -2140,9 +2140,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 6f8f4bfe607f1405d313bb88a33490621002f63e8c02c980f4c083630ad3a6d2 -Q +aaa73400c41f382bbbd4ea32ad196d271c7a5e800bfee9afd998037a14fc89b9 -R 2f292a953a71e724133a873c19fe621c -U larrybr -Z 82e3c6b97cf9016d92e3b8c651c803be +P 91c888eddbcb1b461f7d562c2f0eee57f1a2be7f9959b9d69e80fc7c1da20e9e +R aacc5daf648064d816701ed024ac5a70 +U dan +Z d2599b0e9e0464d6a1e8d44fb28dd315 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ab439a7e57..97e9012bae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -91c888eddbcb1b461f7d562c2f0eee57f1a2be7f9959b9d69e80fc7c1da20e9e \ No newline at end of file +3fe89238c31ca163d021ef1fecde594328815aaba7053fef9e9c789562a62f45 \ No newline at end of file From 0fcaf16f20a1a9c38aa740f41941f86134b7ada8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 20 Nov 2023 13:06:59 +0000 Subject: [PATCH 224/347] Convert an assert in OP_VCheck into a branch that aborts the opcode, as this can happen on some very obscure conditions, as discovered by dbsqlfuzz. Test case in TH3. FossilOrigin-Name: 7946c79567b0ccd3a00d12390e99896d7dc11407d0b52bc39338a16a493f56f6 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/vdbe.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 0215f6b53c..717bc4e7da 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sassert()\sin\sfts5\sthat\scould\sbe\strue\sfollowing\san\sOOM\sor\sIO\serror\sin\scontentless-delete\smode. -D 2023-11-20T11:40:18.032 +C Convert\san\sassert\sin\sOP_VCheck\sinto\sa\sbranch\sthat\saborts\sthe\sopcode,\sas\sthis\ncan\shappen\son\ssome\svery\sobscure\sconditions,\sas\sdiscovered\sby\sdbsqlfuzz.\nTest\scase\sin\sTH3. +D 2023-11-20T13:06:59.104 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -795,7 +795,7 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c b22cc9f203a8c0b9ee5338a67f8860347d14845864c10248bebe84518a781677 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 -F src/vdbe.c 04b827a4ef155cb529bb36f828d46407eeb39cbfa4ce6a5e6eb862040d247e59 +F src/vdbe.c 319af2cf092d20e233e8ad4267ae49bfe33c50ac4db4ee7e47af898f824c2368 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c b07df805110dc6e81f2a3f9cd4e83f56ea523277a59bcec489a12b740c1079e7 @@ -2140,8 +2140,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 91c888eddbcb1b461f7d562c2f0eee57f1a2be7f9959b9d69e80fc7c1da20e9e -R aacc5daf648064d816701ed024ac5a70 -U dan -Z d2599b0e9e0464d6a1e8d44fb28dd315 +P 3fe89238c31ca163d021ef1fecde594328815aaba7053fef9e9c789562a62f45 +R 023252c7b46c4247f78e158002469cb0 +U drh +Z 2185395a736aba37940a68778fd45ab3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 97e9012bae..bfd0c92312 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3fe89238c31ca163d021ef1fecde594328815aaba7053fef9e9c789562a62f45 \ No newline at end of file +7946c79567b0ccd3a00d12390e99896d7dc11407d0b52bc39338a16a493f56f6 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 4ea5080323..38155a170c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -8184,7 +8184,7 @@ case OP_VCheck: { /* out2 */ pTab = pOp->p4.pTab; assert( pTab!=0 ); assert( IsVirtual(pTab) ); - assert( pTab->u.vtab.p!=0 ); + if( pTab->u.vtab.p==0 ) break; pVtab = pTab->u.vtab.p->pVtab; assert( pVtab!=0 ); pModule = pVtab->pModule; From 4f77a270325fe4a582f93f341516db5e2507b064 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 20 Nov 2023 15:54:00 +0000 Subject: [PATCH 225/347] Back out an incorrect change to the sqlite3ExprCompareSkip() function that was added way back on 2019-08-22 for [44578865fa7baf97|check-in 44578865fa7ba] and which was only today discovered to be incorrect by [forum:/forumpost/45ec3d9788|forum post 45ec3d9788]. FossilOrigin-Name: f5b3eb0fc8936ba274a7654ff6dfa7d4654bd8dbca7f3a5ec1134b0b5260d59d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 4 ++-- test/whereG.test | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 717bc4e7da..35f27161bd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\san\sassert\sin\sOP_VCheck\sinto\sa\sbranch\sthat\saborts\sthe\sopcode,\sas\sthis\ncan\shappen\son\ssome\svery\sobscure\sconditions,\sas\sdiscovered\sby\sdbsqlfuzz.\nTest\scase\sin\sTH3. -D 2023-11-20T13:06:59.104 +C Back\sout\san\sincorrect\schange\sto\sthe\ssqlite3ExprCompareSkip()\sfunction\sthat\nwas\sadded\sway\sback\son\s2019-08-22\sfor\s[44578865fa7baf97|check-in\s44578865fa7ba]\nand\swhich\swas\sonly\stoday\sdiscovered\sto\sbe\sincorrect\sby\n[forum:/forumpost/45ec3d9788|forum\spost\s45ec3d9788]. +D 2023-11-20T15:54:00.219 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -675,7 +675,7 @@ F src/date.c 3b8d02977d160e128469de38493b4085f7c5cf4073193459909a6af3cf6d7c91 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 -F src/expr.c 88629faed0b576b7ffa3d82ce44cbcee4ed476a2bf1ea4e1d6bf1260e03b19cb +F src/expr.c e9a491c7f156e5b25641c28af11b735a424e108a21b9f83b6f3e51c99a8141d9 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00 F src/func.c 472f6dcfa39cf54f89a6aec76c79c225fb880a6c14469c15d361331662b9bf43 @@ -1967,7 +1967,7 @@ F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6 F test/whereD.test c1c335e914e28b122e000e9310f02d2be83e1c9dbca2e29f46bd732703944d1b F test/whereE.test 7a727b5d5b6bc8fa4cef5206e90cc0363e55ca7f0566f6fbad0206e43170f59e F test/whereF.test 926b65519608e3f2aa28720822b9154fb5c7b13519dd78194f434a511ab3dac5 -F test/whereG.test b2a479f425f7d0a432df7e842e8484560908ef703fe0fd407888ff85e7097238 +F test/whereG.test 649d5ad02a87a76ec2ac8de9441e2c83a4dd0f29e459a31215c0533788c6bf07 F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2 F test/whereI.test c4bb7e2ca56d49bd8ab5c7bd085b8b83e353922b46904d68aefb3c7468643581 F test/whereJ.test fc05e374cc9f2dc204148d6c06822c380ad388895fe97a6d335b94a26a08aecf @@ -2140,8 +2140,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 3fe89238c31ca163d021ef1fecde594328815aaba7053fef9e9c789562a62f45 -R 023252c7b46c4247f78e158002469cb0 +P 7946c79567b0ccd3a00d12390e99896d7dc11407d0b52bc39338a16a493f56f6 +R 8d8b41bdc285c44f242899112be36ddc U drh -Z 2185395a736aba37940a68778fd45ab3 +Z ae51455ef4d78dc1dd1b05ab3dc3c13a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bfd0c92312..7f8179d075 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7946c79567b0ccd3a00d12390e99896d7dc11407d0b52bc39338a16a493f56f6 \ No newline at end of file +f5b3eb0fc8936ba274a7654ff6dfa7d4654bd8dbca7f3a5ec1134b0b5260d59d \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index f9234734d5..8a664ffb9c 100644 --- a/src/expr.c +++ b/src/expr.c @@ -6043,8 +6043,8 @@ int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){ */ int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlite3ExprCompare(0, - sqlite3ExprSkipCollateAndLikely(pA), - sqlite3ExprSkipCollateAndLikely(pB), + sqlite3ExprSkipCollate(pA), + sqlite3ExprSkipCollate(pB), iTab); } diff --git a/test/whereG.test b/test/whereG.test index 6ca363ed8b..c154058233 100644 --- a/test/whereG.test +++ b/test/whereG.test @@ -311,6 +311,20 @@ do_execsql_test 8.9 { do_execsql_test 8.10 { SELECT * FROM t0 WHERE likelihood(t0.rowid <= '0', 0.5); } {} +# Forum https://sqlite.org/forum/forumpost/45ec3d9788 +reset_db +do_execsql_test 8.11 { + CREATE TABLE t1(c0 INT); + INSERT INTO t1(c0) VALUES (NULL); + CREATE INDEX i46 ON t1(CAST( (c0 IS TRUE) AS TEXT)); + CREATE VIEW v0(c2) AS SELECT CAST( (c0 IS TRUE) AS TEXT ) FROM t1; +} +do_execsql_test 8.12 { + SELECT quote(c0), quote(c2) FROM t1, v0 WHERE (0 < LIKELY(v0.c2)); +} {NULL '0'} +do_execsql_test 8.13 { + SELECT quote(c0), quote(c2) FROM t1, v0 WHERE (0 < LIKELY(v0.c2)) IS TRUE; +} {NULL '0'} # 2019-12-31: assertion fault discovered by Yongheng's fuzzer. # Harmless memIsValid() due to the code generators failure to From a4050f14341069dfb7e8ca0e7009306d6742064b Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 21 Nov 2023 12:02:04 +0000 Subject: [PATCH 226/347] Fix the trace3-4.4 test to be more rebust against timing quirks. FossilOrigin-Name: 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/trace3.test | 27 ++++++++++++++++++++------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 35f27161bd..721aac63bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\sout\san\sincorrect\schange\sto\sthe\ssqlite3ExprCompareSkip()\sfunction\sthat\nwas\sadded\sway\sback\son\s2019-08-22\sfor\s[44578865fa7baf97|check-in\s44578865fa7ba]\nand\swhich\swas\sonly\stoday\sdiscovered\sto\sbe\sincorrect\sby\n[forum:/forumpost/45ec3d9788|forum\spost\s45ec3d9788]. -D 2023-11-20T15:54:00.219 +C Fix\sthe\strace3-4.4\stest\sto\sbe\smore\srebust\sagainst\stiming\squirks. +D 2023-11-21T12:02:04.720 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1816,7 +1816,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7 F test/tpch01.test 4479008f85f6f8f25f7ab2cb305d665752b4727fa28a8df3d8e0ad46520c62ff F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 -F test/trace3.test ae2004df24b585fed9046cc0bae4601762bc6fc4aa321d475f1350bba5047f31 +F test/trace3.test 4f418ed30d15d9d17dcf13a17f0bd99a92e3038e038798e35db7525f82f4c281 F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f439 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 @@ -2140,8 +2140,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 7946c79567b0ccd3a00d12390e99896d7dc11407d0b52bc39338a16a493f56f6 -R 8d8b41bdc285c44f242899112be36ddc +P f5b3eb0fc8936ba274a7654ff6dfa7d4654bd8dbca7f3a5ec1134b0b5260d59d +R c3df342b9429d8c9fdce76751979c422 U drh -Z ae51455ef4d78dc1dd1b05ab3dc3c13a +Z 57a5e144d820d25acd5cfb831cc09e5a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7f8179d075..73e608bdbe 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f5b3eb0fc8936ba274a7654ff6dfa7d4654bd8dbca7f3a5ec1134b0b5260d59d \ No newline at end of file +8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 \ No newline at end of file diff --git a/test/trace3.test b/test/trace3.test index e9935acfb8..496cc2360a 100644 --- a/test/trace3.test +++ b/test/trace3.test @@ -132,14 +132,27 @@ do_test trace3-4.3 { list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds } {/^-?\d+ 1$/} do_test trace3-4.4 { - set ::stmtlist(record) {} - db trace_v2 trace_v2_record 2 - execsql { - SELECT a, b FROM t1 ORDER BY a; + set cnt 0 + while {1} { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 2 + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set stmt [lindex [lindex $::stmtlist(record) 0] 0] + set ns [lindex [lindex $::stmtlist(record) 0] 1] + if {$ns<0 || $ns>9999999} { #less than 0.010 seconds + incr cnt + if {$cnt>3} { + set res "time out of bounds. Expected less than 99999999. Got $ns" + break + } + } else { + set res 1 + break + } } - set stmt [lindex [lindex $::stmtlist(record) 0] 0] - set ns [lindex [lindex $::stmtlist(record) 0] 1] - list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds + list $stmt $res } {/^-?\d+ 1$/} do_test trace3-5.1 { From 8a4cecaea2dd09f7174a0838875a6e47c71fcf62 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 21 Nov 2023 17:51:58 +0000 Subject: [PATCH 227/347] Make edits directly to the JSONB BLOB when the input to json_replace() is a JSONB. FossilOrigin-Name: d69c6acef54a81f46a97a05d443fe648635b4b70772069d6705ef829b718e985 --- manifest | 12 ++--- manifest.uuid | 2 +- src/json.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 5ede8a53d8..7339368a54 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\senhancements\sand\sfixes\sinto\sthe\sjsonb\sbranch. -D 2023-11-17T17:03:45.388 +C Make\sedits\sdirectly\sto\sthe\sJSONB\sBLOB\swhen\sthe\sinput\sto\sjson_replace()\nis\sa\sJSONB. +D 2023-11-21T17:51:58.335 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 949c4a36244d402d1e5f9c1815d9ce2cf8e94cf27ce7419e2d1b9dd5a97a2baa +F src/json.c 9be29c023ce9c839a507ddfd572dbca226c9707ba6b0714da004e00a7312a000 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2142,8 +2142,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 68d551730be0a3ea9579646ed4836c73554c83ca7f2303b69a18843f1750f1a7 ce6a75622ea5bca517bc6613e738aa670c9e1dd863596220eded5c2379c616c7 -R 3f0ae99fa66c7ee3590aa56d01ed6b36 +P 162f0509ef27bcd3ec87629640281a71c773e7c3bbd2cd0df76faf481531e7f1 +R 38409c37dc02439917dd59332bdf58d4 U drh -Z c061d55cfc7406269213a15ddaf22fe9 +Z 4892279bd4eeae014e8915fe6a430955 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 475b441c30..4a62c55635 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -162f0509ef27bcd3ec87629640281a71c773e7c3bbd2cd0df76faf481531e7f1 \ No newline at end of file +d69c6acef54a81f46a97a05d443fe648635b4b70772069d6705ef829b718e985 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 89109e0039..def5e63d30 100644 --- a/src/json.c +++ b/src/json.c @@ -4304,6 +4304,143 @@ jsonRemoveFromBlob_patherror: return; } +/* +** pArg is a function argument that might be an SQL value or a JSON +** value. Figure out what it is and encode it as a JSONB blob. +** Return the results in pParse. +** +** pParse is uninitialized upon entry. This routine will handle the +** initialization of pParse. The result will be contained in +** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically +** allocated (if pParse->nBlobAlloc is greater than zero) in which case +** the caller is responsible for freeing the space allocated to pParse->aBlob +** when it has finished with it. Or pParse->aBlob might be a static string +** or a value obtained from sqlite3_value_blob(pArg). +** +** If the argument is a BLOB that is clearly not a JSONB, then this +** function might set an error message in ctx and return non-zero. +** It might also set an error message and return non-zero on an OOM error. +*/ +static int jsonFunctionArgToBlob( + sqlite3_context *ctx, + sqlite3_value *pArg, + JsonParse *pParse +){ + int eType = sqlite3_value_type(pArg); + static u8 aNull[] = { 0x00 }; + memset(pParse, 0, sizeof(pParse[0])); + switch( eType ){ + case SQLITE_NULL: { + pParse->aBlob = aNull; + pParse->nBlob = 1; + return 0; + } + case SQLITE_BLOB: { + if( jsonFuncArgMightBeBinary(pArg) ){ + pParse->aBlob = (u8*)sqlite3_value_blob(pArg); + pParse->nBlob = sqlite3_value_bytes(pArg); + }else{ + sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); + return 1; + } + break; + } + case SQLITE_TEXT: { + const char *zJson = (const char*)sqlite3_value_text(pArg); + int nJson = sqlite3_value_bytes(pArg); + if( zJson==0 ) return 1; + if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ + pParse->zJson = (char*)zJson; + pParse->nJson = nJson; + if( jsonConvertTextToBlob(pParse, ctx) ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + sqlite3_free(pParse->aBlob); + memset(pParse, 0, sizeof(pParse[0])); + return 1; + } + }else{ + jsonBlobAppendNodeType(pParse, JSONB_TEXTRAW, nJson); + jsonBlobAppendNBytes(pParse, (const u8*)zJson, nJson); + } + break; + } + case SQLITE_FLOAT: + case SQLITE_INTEGER: { + int n = sqlite3_value_bytes(pArg); + const char *z = (const char*)sqlite3_value_text(pArg); + int e = eType==SQLITE_INTEGER ? JSONB_INT : JSONB_FLOAT; + if( z==0 ) return 1; + jsonBlobAppendNodeType(pParse, e, n); + jsonBlobAppendNBytes(pParse, (const u8*)z, n); + break; + } + } + return 0; +} + +/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent +** arguments come in parse where each pair contains a JSON path and +** content to insert or set at that patch. Do the updates +** and return the result. +** +** The specific operation is determined by eEdit, which can be one +** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. +*/ +static void jsonInsertIntoBlob( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv, + int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ +){ + int i; + u32 rc; + const char *zPath = 0; + int flgs; + JsonParse px, ax; + + assert( (argc&1)==1 ); + memset(&px, 0, sizeof(px)); + px.nBlob = sqlite3_value_bytes(argv[0]); + px.aBlob = (u8*)sqlite3_value_blob(argv[0]); + if( px.aBlob==0 ) return; + for(i=1; i0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); + }else{ + JsonString s; + jsonStringInit(&s, ctx); + jsonXlateBlobToText(&px, 0, &s); + jsonReturnString(&s); + if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + } + return; + +jsonInsertIntoBlob_patherror: + if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonPathSyntaxError(zPath, ctx); + return; +} + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ @@ -4987,6 +5124,10 @@ static void jsonReplaceFunc( jsonWrongNumArgs(ctx, "replace"); return; } + if( jsonFuncArgMightBeBinary(argv[0]) && argc>=3 ){ + jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); + return; + } pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); if( pParse==0 ) return; pParse->nJPRef++; From 664fe310b5749c18fceb7c249f45b2b8cfc7dc21 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 21 Nov 2023 18:23:43 +0000 Subject: [PATCH 228/347] Fix the translation of JSON5 numeric values from BLOB into text. FossilOrigin-Name: 40c4fb441f220982e4d61fd42597cf18546791a302fbcc8eec2eed29ee15ef35 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 0e2a5d70fc..7e945f8442 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\srecent\strunk\sfixes\sand\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-11-21T17:54:55.986 +C Fix\sthe\stranslation\sof\sJSON5\snumeric\svalues\sfrom\sBLOB\sinto\stext. +D 2023-11-21T18:23:43.037 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9be29c023ce9c839a507ddfd572dbca226c9707ba6b0714da004e00a7312a000 +F src/json.c c5d4b35c598f50e5be9191260ae92be4073d17bff84daa0dd64c31edbb7dce7e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2142,8 +2142,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 d69c6acef54a81f46a97a05d443fe648635b4b70772069d6705ef829b718e985 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 -R e8c6337278c34718864f8e382e5ab75f +P 6d78d50ed2357e6c943c1ef97b1d2ea0902cbadef90c2c35dccdbdc2bdf8702f +R 2ba901c1c280b3893e91bda5d71d76cb U drh -Z 78714a658254db90406116c46e75110c +Z 574e66b6116d2f9186df90de5e4b8fb3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d87fbc1eaf..7e841c3b50 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6d78d50ed2357e6c943c1ef97b1d2ea0902cbadef90c2c35dccdbdc2bdf8702f \ No newline at end of file +40c4fb441f220982e4d61fd42597cf18546791a302fbcc8eec2eed29ee15ef35 \ No newline at end of file diff --git a/src/json.c b/src/json.c index def5e63d30..69950766da 100644 --- a/src/json.c +++ b/src/json.c @@ -3375,6 +3375,8 @@ static u32 jsonXlateBlobToText( if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; + }else if( zIn[0]=='+' ){ + k++; } for(; k Date: Tue, 21 Nov 2023 19:05:22 +0000 Subject: [PATCH 229/347] Correct blob-to-text rendering in some corner cases. FossilOrigin-Name: 7822e0e59f9b611fe6289cc762b0aff61f9b87c3a82c60de110f447589a2c125 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 6 +++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 7e945f8442..ad10087655 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\stranslation\sof\sJSON5\snumeric\svalues\sfrom\sBLOB\sinto\stext. -D 2023-11-21T18:23:43.037 +C Correct\sblob-to-text\srendering\sin\ssome\scorner\scases. +D 2023-11-21T19:05:22.846 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c c5d4b35c598f50e5be9191260ae92be4073d17bff84daa0dd64c31edbb7dce7e +F src/json.c 54e4261d68a463bb8e8adf7ce0ba9c8ad2853870ce53e4e5f903c6a97eb8b644 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2142,8 +2142,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 6d78d50ed2357e6c943c1ef97b1d2ea0902cbadef90c2c35dccdbdc2bdf8702f -R 2ba901c1c280b3893e91bda5d71d76cb +P 40c4fb441f220982e4d61fd42597cf18546791a302fbcc8eec2eed29ee15ef35 +R e0de56e45777fbb83b14bb9ecc453e36 U drh -Z 574e66b6116d2f9186df90de5e4b8fb3 +Z 0c3d75110c96246c67d1b5a0fbc45fae # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7e841c3b50..0085f7c96b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -40c4fb441f220982e4d61fd42597cf18546791a302fbcc8eec2eed29ee15ef35 \ No newline at end of file +7822e0e59f9b611fe6289cc762b0aff61f9b87c3a82c60de110f447589a2c125 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 69950766da..199bc5e18d 100644 --- a/src/json.c +++ b/src/json.c @@ -3372,6 +3372,7 @@ static u32 jsonXlateBlobToText( u32 k = 2; sqlite3_uint64 u = 0; const char *zIn = (const char*)&pParse->aBlob[i+n]; + int bOverflow = 0; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); k++; @@ -3382,11 +3383,13 @@ static u32 jsonXlateBlobToText( if( !sqlite3Isxdigit(zIn[k]) ){ pOut->eErr |= JSTRING_MALFORMED; break; + }else if( (u>>60)!=0 ){ + bOverflow = 1; }else{ u = u*16 + sqlite3HexToInt(zIn[k]); } } - jsonPrintf(100,pOut,"%llu",u); + jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); break; } case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ @@ -3519,6 +3522,7 @@ static u32 jsonXlateBlobToText( j = jsonXlateBlobToText(pParse, j, pOut); jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); } + if( x & 1 ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) pOut->nUsed--; jsonAppendChar(pOut, '}'); break; From 27fea97e4451ddb181a4d0cda5b6aeb1865159ea Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 21 Nov 2023 20:13:08 +0000 Subject: [PATCH 230/347] Direct editing of JSONB using json_insert() and json_set(). FossilOrigin-Name: fffb7a9538838e26991e6f16ea3138346a30c33ea6c3d3834680ee6d1f6eece2 --- manifest | 12 +++++----- manifest.uuid | 2 +- src/json.c | 66 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index ad10087655..2fb4d4c6c1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sblob-to-text\srendering\sin\ssome\scorner\scases. -D 2023-11-21T19:05:22.846 +C Direct\sediting\sof\sJSONB\susing\sjson_insert()\sand\sjson_set(). +D 2023-11-21T20:13:08.271 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 54e4261d68a463bb8e8adf7ce0ba9c8ad2853870ce53e4e5f903c6a97eb8b644 +F src/json.c 6530a0ad967e10973ba46db399b46e20fc243b2b495cd53226aa31a9ce1b1e46 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2142,8 +2142,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 40c4fb441f220982e4d61fd42597cf18546791a302fbcc8eec2eed29ee15ef35 -R e0de56e45777fbb83b14bb9ecc453e36 +P 7822e0e59f9b611fe6289cc762b0aff61f9b87c3a82c60de110f447589a2c125 +R 42a0f1601a18c87c6ad9e79182203b90 U drh -Z 0c3d75110c96246c67d1b5a0fbc45fae +Z 7d1450dc466726ea7e03ad211ff3e833 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0085f7c96b..eba39323db 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7822e0e59f9b611fe6289cc762b0aff61f9b87c3a82c60de110f447589a2c125 \ No newline at end of file +fffb7a9538838e26991e6f16ea3138346a30c33ea6c3d3834680ee6d1f6eece2 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 199bc5e18d..69557aa721 100644 --- a/src/json.c +++ b/src/json.c @@ -349,10 +349,10 @@ struct JsonParse { }; /* Allowed values for JsonParse.eEdit */ -#define JEDIT_DEL 0x01 /* Delete if exists */ -#define JEDIT_INS 0x02 /* Insert if not exists */ -#define JEDIT_REPL 0x04 /* Overwrite if exists */ -#define JEDIT_SET 0x08 /* Insert or overwrite */ +#define JEDIT_DEL 1 /* Delete if exists */ +#define JEDIT_REPL 2 /* Overwrite if exists */ +#define JEDIT_INS 3 /* Insert if not exists */ +#define JEDIT_SET 4 /* Insert or overwrite */ /* ** Maximum nesting depth of JSON for this implementation. @@ -2586,12 +2586,12 @@ static int jsonBlobExpand(JsonParse *pParse, u32 N){ ** ** Return true on success. Return false on OOM. */ -static int jsonBlobMakeEditable(JsonParse *pParse){ +static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ u8 *aOld; u32 nSize; if( pParse->nBlobAlloc>0 ) return 1; aOld = pParse->aBlob; - nSize = pParse->nBlob + pParse->nIns; + nSize = pParse->nBlob + pParse->nIns + nExtra; if( nSize>100 ) nSize -= 100; pParse->aBlob = 0; if( jsonBlobExpand(pParse, nSize) ){ @@ -3859,7 +3859,7 @@ static u32 jsonLookupBlobStep( u8 x; if( zPath[0]==0 ){ - if( pParse->eEdit && jsonBlobMakeEditable(pParse) ){ + if( pParse->eEdit && jsonBlobMakeEditable(pParse, 0) ){ n = jsonbPayloadSize(pParse, iRoot, &sz); sz += n; if( pParse->eEdit==JEDIT_DEL ){ @@ -3941,6 +3941,32 @@ static u32 jsonLookupBlobStep( j += n+sz; } if( j>iEnd ) return JSON_BLOB_ERROR; + if( pParse->eEdit>=JEDIT_INS ){ + u32 nIns; + u8 aLabel[16]; + JsonParse ix; + testcase( pParse->eEdit==JEDIT_INS ); + testcase( pParse->eEdit==JEDIT_SET ); + memset(&ix, 0, sizeof(ix)); + ix.aBlob = aLabel; + ix.nBlobAlloc = sizeof(aLabel); + jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); + if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey) ){ + nIns = ix.nBlob + nKey + pParse->nIns; + assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); + memmove(&pParse->aBlob[j+nIns], &pParse->aBlob[j], + pParse->nBlob - j); + memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); + k = j + ix.nBlob; + memcpy(&pParse->aBlob[k], zKey, nKey); + k += nKey; + memcpy(&pParse->aBlob[k], pParse->aIns, pParse->nIns); + pParse->delta = nIns; + pParse->nBlob += nIns; + jsonAfterEditSizeAdjust(pParse, iRoot); + } + return j; + } }else if( zPath[0]=='[' ){ x = pParse->aBlob[iRoot] & 0x0f; if( x!=JSONB_ARRAY ) return JSON_BLOB_NOTFOUND; @@ -3987,19 +4013,21 @@ static u32 jsonLookupBlobStep( } if( j>iEnd ) return JSON_BLOB_ERROR; if( k>1 ) return JSON_BLOB_NOTFOUND; + if( pParse->eEdit>=JEDIT_INS && jsonBlobMakeEditable(pParse, 0) ){ + testcase( pParse->eEdit==JEDIT_INS ); + testcase( pParse->eEdit==JEDIT_SET ); + assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); + memmove(&pParse->aBlob[j+pParse->nIns], &pParse->aBlob[j], + pParse->nBlob - j); + memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); + pParse->delta = pParse->nIns; + pParse->nBlob += pParse->nIns; + jsonAfterEditSizeAdjust(pParse, iRoot); + return j; + } }else{ return JSON_BLOB_PATHERROR; } - if( pParse->eEdit==JEDIT_INS && jsonBlobMakeEditable(pParse) ){ - assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); - memmove(&pParse->aBlob[j], &pParse->aBlob[j+pParse->nIns], - pParse->nBlob - j); - memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); - pParse->delta = pParse->nIns; - pParse->nBlob += pParse->nIns; - jsonAfterEditSizeAdjust(pParse, iRoot); - return j; - } return JSON_BLOB_NOTFOUND; } @@ -5183,6 +5211,10 @@ static void jsonSetFunc( jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } + if( jsonFuncArgMightBeBinary(argv[0]) && argc>=3 ){ + jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); + return; + } pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); if( pParse==0 ) return; pParse->nJPRef++; From cf72606a7d05c7fc3b51ba596db013d51eb73d4b Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 21 Nov 2023 22:36:32 +0000 Subject: [PATCH 231/347] Inserts invalid JSONB should return "malformed JSON", not a json path error. FossilOrigin-Name: 306ee66fbd0231a9f5b229e5630d5cc66c9cf08b466d2d9204e79e1f44a16374 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 8 ++++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 2fb4d4c6c1..35eb7e15dc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Direct\sediting\sof\sJSONB\susing\sjson_insert()\sand\sjson_set(). -D 2023-11-21T20:13:08.271 +C Inserts\sinvalid\sJSONB\sshould\sreturn\s"malformed\sJSON",\snot\sa\sjson\spath\serror. +D 2023-11-21T22:36:32.538 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -685,7 +685,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6530a0ad967e10973ba46db399b46e20fc243b2b495cd53226aa31a9ce1b1e46 +F src/json.c f8cfdf1d024a7b2c5ad3eae15903a7a83a9b2c8ab61716d219a57600544b712c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2142,8 +2142,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 7822e0e59f9b611fe6289cc762b0aff61f9b87c3a82c60de110f447589a2c125 -R 42a0f1601a18c87c6ad9e79182203b90 +P fffb7a9538838e26991e6f16ea3138346a30c33ea6c3d3834680ee6d1f6eece2 +R 99205893bc97f055f5ba354365513ffb U drh -Z 7d1450dc466726ea7e03ad211ff3e833 +Z ff4b069c664b897025c4d05de8c19bcd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index eba39323db..0b616dcb8b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fffb7a9538838e26991e6f16ea3138346a30c33ea6c3d3834680ee6d1f6eece2 \ No newline at end of file +306ee66fbd0231a9f5b229e5630d5cc66c9cf08b466d2d9204e79e1f44a16374 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 69557aa721..54e221392e 100644 --- a/src/json.c +++ b/src/json.c @@ -4427,7 +4427,7 @@ static void jsonInsertIntoBlob( int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ ){ int i; - u32 rc; + u32 rc = 0; const char *zPath = 0; int flgs; JsonParse px, ax; @@ -4471,7 +4471,11 @@ static void jsonInsertIntoBlob( jsonInsertIntoBlob_patherror: if( px.nBlobAlloc ) sqlite3_free(px.aBlob); - jsonPathSyntaxError(zPath, ctx); + if( rc==JSON_BLOB_ERROR ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + }else{ + jsonPathSyntaxError(zPath, ctx); + } return; } From c0314bc01d25830031a8a925d682035066a993ed Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 22 Nov 2023 17:11:39 +0000 Subject: [PATCH 232/347] Correct the URL for loading sqlite3-worker1-bundler-friendly.mjs from sqlite3-worker1-promiser-bundler-friendly.js, so that the promiser can be used in bundler-using environments. Problem reported via email. FossilOrigin-Name: 753a75218913c3b9c7ec5438387107369c34d1775d68b04d19ae18197e558605 --- ext/wasm/api/sqlite3-worker1-promiser.c-pp.js | 13 ++++++++----- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js index bad599673f..1f88b713a9 100644 --- a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js +++ b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js @@ -247,9 +247,8 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function(){ //#if target=es6-bundler-friendly - return new Worker("sqlite3-worker1-bundler-friendly.mjs",{ - type: 'module' /* Noting that neither Firefox nor Safari suppor this, - as of this writing. */ + return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{ + type: 'module' }); //#else let theJs = "sqlite3-worker1.js"; @@ -267,8 +266,12 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = { } return new Worker(theJs + globalThis.location.search); //#endif - }.bind({ + } +//#ifnot target=es6-bundler-friendly + .bind({ currentScript: globalThis?.document?.currentScript - }), + }) +//#endif + , onerror: (...args)=>console.error('worker1 promiser error',...args) }; diff --git a/manifest b/manifest index a6dac2dd9a..ef2bf18773 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sconsole\sI/O\schanges\sfor\sWindows\sCLI. -D 2023-11-21T18:26:06.197 +C Correct\sthe\sURL\sfor\sloading\ssqlite3-worker1-bundler-friendly.mjs\sfrom\ssqlite3-worker1-promiser-bundler-friendly.js,\sso\sthat\sthe\spromiser\scan\sbe\sused\sin\sbundler-using\senvironments.\sProblem\sreported\svia\semail. +D 2023-11-22T17:11:39.095 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -595,7 +595,7 @@ F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 595953994aa3ae2287c889c4da39ab3d6f17b6461ecf4bec334b7a3faafddb02 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91ccfafc38b400fe36e90bdda88e F ext/wasm/api/sqlite3-wasm.c d0e09eb5ed3743c00294e30019e591c3aa150572ae7ffe8a8994568a7377589f -F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f +F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 569d4e859968e65f55dec5fac0b879828a23c8b179162cc7812fec19f844dd21 F ext/wasm/api/sqlite3-worker1.c-pp.js a541112aa51e16705f13a99bb943c64efe178aa28c86704a955f8fd9afe4ba37 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e @@ -2143,8 +2143,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 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 448d6a1182d29940d5d34be2ce67df5601b688cd902dbbe97e95073f982a49ce -R 764d6a65ddcbf1b62de463cc42b71475 -U larrybr -Z 9d8c4f0e647c59f8c21d5587e8bd311b +P 935a8a8ee76d0014df42c1480e044fd1c2dfc26e78abb587d99d861d2ae5eb27 +R 88b228f6756043549fa673c8abfcfa61 +U stephan +Z 2f087f3dd80678f00ca104b71330863c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 094c6b93ac..9e88622647 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -935a8a8ee76d0014df42c1480e044fd1c2dfc26e78abb587d99d861d2ae5eb27 \ No newline at end of file +753a75218913c3b9c7ec5438387107369c34d1775d68b04d19ae18197e558605 \ No newline at end of file From 5c268bbf67d8bfd5383a16c4618b9aaefd86abeb Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 22 Nov 2023 19:02:54 +0000 Subject: [PATCH 233/347] Fix tokendata=1 and xInstToken() APIs for detail=none and detail=column tables. FossilOrigin-Name: 37b271c19d772bd06524db816ded03377b426efed7a7783c8a96f6fb156ecd86 --- ext/fts5/fts5Int.h | 7 ++++ ext/fts5/fts5_expr.c | 48 +++++++++++++++++++-- ext/fts5/fts5_index.c | 67 ++++++++++++++++++++++++++++++ ext/fts5/test/fts5origintext.test | 51 +++++++++++++++-------- ext/fts5/test/fts5origintext2.test | 2 +- ext/fts5/test/fts5origintext3.test | 60 ++++++++++++++++++++++++++ manifest | 21 +++++----- manifest.uuid | 2 +- 8 files changed, 224 insertions(+), 34 deletions(-) create mode 100644 ext/fts5/test/fts5origintext3.test diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 19d9579eb7..317d66db99 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -545,6 +545,13 @@ int sqlite3Fts5IndexLoadConfig(Fts5Index *p); int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); +/* Used to populate hash tables for xInstToken in detail=none/column mode. */ +void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); +int sqlite3Fts5IndexIterWriteTokendata( + Fts5IndexIter*, const char*, int, int iCol, int iOff +); +int sqlite3Fts5IndexIterHashifyTokendata(Fts5IndexIter*); + /* ** End of interface to code in fts5_index.c. **************************************************************************/ diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index ae851a877a..9889bccb32 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -2985,6 +2985,12 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ return 0; } +static int fts5QueryTerm(const char *pToken, int nToken){ + int ii; + for(ii=0; iipExpr; int i; + int nQuery = nToken; UNUSED_PARAM2(iUnused1, iUnused2); - if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; + if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE; + if( pExpr->pConfig->bTokendata ){ + nQuery = fts5QueryTerm(pToken, nQuery); + } if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; for(i=0; inPhrase; i++){ Fts5ExprTerm *pT; if( p->aPopulator[i].bOk==0 ) continue; for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ - if( (pT->nFullTerm==nToken || (pT->nFullTermbPrefix)) - && memcmp(pT->pTerm, pToken, pT->nFullTerm)==0 + if( (pT->nQueryTerm==nQuery || (pT->nQueryTermbPrefix)) + && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 ){ int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff ); + if( rc==SQLITE_OK && pExpr->pConfig->bTokendata ){ + int iCol = p->iOff>>32; + int iTokOff = p->iOff & 0x7FFFFFFF; + rc = sqlite3Fts5IndexIterWriteTokendata( + pT->pIter, pToken, nToken, iCol, iTokOff + ); + } if( rc ) return rc; break; } @@ -3027,11 +3044,23 @@ int sqlite3Fts5ExprPopulatePoslists( const char *z, int n ){ int i; + int rc = SQLITE_OK; Fts5ExprCtx sCtx; sCtx.pExpr = pExpr; sCtx.aPopulator = aPopulator; sCtx.iOff = (((i64)iCol) << 32) - 1; + /* If this is a tokendata=1 table, clear out the hash tables of + ** full-terms. */ + if( pConfig->bTokendata ){ + for(i=0; inPhrase; i++){ + Fts5ExprTerm *pT; + for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ + sqlite3Fts5IndexIterClearTokendata(pT->pIter); + } + } + } + for(i=0; inPhrase; i++){ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; Fts5Colset *pColset = pNode->pNear->pColset; @@ -3044,9 +3073,20 @@ int sqlite3Fts5ExprPopulatePoslists( } } - return sqlite3Fts5Tokenize(pConfig, + rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb ); + + if( pConfig->bTokendata ){ + for(i=0; inPhrase; i++){ + Fts5ExprTerm *pT; + for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ + sqlite3Fts5IndexIterHashifyTokendata(pT->pIter); + } + } + } + + return rc; } static void fts5ExprClearPoslists(Fts5ExprNode *pNode){ diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 887bb75dac..f206b8116d 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -6877,6 +6877,73 @@ int sqlite3Fts5IterToken( return SQLITE_OK; } +/* +** Clear any existing entries from the token-map associated with the +** iterator passed as the only argument. +*/ +void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL ); + if( pIter->pTokenMap ){ + pIter->pTokenMap->nEntry = 0; + } +} + +int sqlite3Fts5IndexIterWriteTokendata( + Fts5IndexIter *pIndexIter, + const char *pToken, int nToken, + int iCol, int iOff +){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5Index *p = pIter->pIndex; + assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); + if( pIter->pTokenMap==0 ){ + pIter->pTokenMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); + } + if( p->rc==SQLITE_OK ){ + Fts5TokenMap *pMap = pIter->pTokenMap; + int ii; + for(ii=0; iinToken; ii++){ + if( nToken==pMap->aToken[ii].nTerm + && 0==memcmp(pMap->aToken[ii].pTerm, pToken, nToken) + ){ + break; + } + } + if( ii==pMap->nToken ){ + fts5TokenMapTerm(p, pMap, (const u8*)pToken, nToken); + } + if( pMap->nEntry>=pMap->nEntryAlloc ){ + int nNew = pMap->nEntryAlloc ? pMap->nEntryAlloc*2 : 32; + Fts5TokenMapEntry *aNew = (Fts5TokenMapEntry*)sqlite3_realloc( + pMap->aEntry, nNew * sizeof(Fts5TokenMapEntry) + ); + if( aNew==0 ){ + p->rc = SQLITE_NOMEM; + }else{ + pMap->aEntry = aNew; + pMap->nEntryAlloc = nNew; + } + } + if( p->rc==SQLITE_OK ){ + Fts5TokenMapEntry *pEntry = &pMap->aEntry[pMap->nEntry++]; + pEntry->iRowid = pIndexIter->iRowid; + pEntry->iCol = iCol; + pEntry->iOff = iOff; + pEntry->iTok = ii+1; + } + } + return fts5IndexReturn(p); +} + +int sqlite3Fts5IndexIterHashifyTokendata(Fts5IndexIter *pIndexIter){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + if( pIter->pTokenMap ){ + fts5TokenMapHashify(pIter->pIndex, pIter->pTokenMap); + } + return fts5IndexReturn(pIter->pIndex); +} + /* ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). */ diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 07e5b4fb2e..845e8145db 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -21,9 +21,13 @@ ifcapable !fts5 { return } +foreach_detail_mode $testprefix { + sqlite3_fts5_register_origintext db do_execsql_test 1.0 { - CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", detail=%DETAIL% + ); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); } @@ -85,7 +89,9 @@ db func document document sqlite3_fts5_register_origintext db do_execsql_test 2.0 { - CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", detail=%DETAIL% + ); INSERT INTO ft(ft, rank) VALUES('pgsz', 128); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); } @@ -117,7 +123,9 @@ reset_db sqlite3_fts5_register_origintext db do_execsql_test 3.0 { - CREATE VIRTUAL TABLE ft USING fts5(x, tokenize="origintext unicode61"); + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", detail=%DETAIL% + ); CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance); INSERT INTO ft(rowid, x) VALUES(1, 'hello'); @@ -136,7 +144,8 @@ do_execsql_test 3.1.3 { SELECT rowid FROM ft('HELLO') } 3 do_execsql_test 3.0 { CREATE VIRTUAL TABLE ft2 USING fts5(x, tokenize="origintext unicode61", - tokendata=1 + tokendata=1, + detail=%DETAIL% ); CREATE VIRTUAL TABLE vocab2 USING fts5vocab(ft2, instance); @@ -169,7 +178,7 @@ sqlite3_fts5_create_function db querytoken querytoken do_execsql_test 4.0 { CREATE VIRTUAL TABLE ft USING fts5( - x, tokenize='origintext unicode61', tokendata=1 + x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL% ); INSERT INTO ft VALUES('one two three four'); } @@ -183,19 +192,23 @@ do_execsql_test 4.2 { do_execsql_test 4.3 { SELECT rowid, querytoken(ft, 1, 0) FROM ft('one TWO ThreE') } {1 two.TWO} -do_execsql_test 4.4 { - SELECT rowid, querytoken(ft, 0, 2) FROM ft('"one TWO ThreE"') -} {1 three.ThreE} -do_catchsql_test 4.5 { - SELECT rowid, querytoken(ft, 0, 3) FROM ft('"one TWO ThreE"') -} {1 SQLITE_RANGE} -do_catchsql_test 4.6 { - SELECT rowid, querytoken(ft, 1, 0) FROM ft('"one TWO ThreE"') -} {1 SQLITE_RANGE} -do_catchsql_test 4.7 { - SELECT rowid, querytoken(ft, -1, 0) FROM ft('"one TWO ThreE"') -} {1 SQLITE_RANGE} +if {"%DETAIL%"=="full"} { + # Phrase queries are only supported for detail=full. + # + do_execsql_test 4.4 { + SELECT rowid, querytoken(ft, 0, 2) FROM ft('"one TWO ThreE"') + } {1 three.ThreE} + do_catchsql_test 4.5 { + SELECT rowid, querytoken(ft, 0, 3) FROM ft('"one TWO ThreE"') + } {1 SQLITE_RANGE} + do_catchsql_test 4.6 { + SELECT rowid, querytoken(ft, 1, 0) FROM ft('"one TWO ThreE"') + } {1 SQLITE_RANGE} + do_catchsql_test 4.7 { + SELECT rowid, querytoken(ft, -1, 0) FROM ft('"one TWO ThreE"') + } {1 SQLITE_RANGE} +} #------------------------------------------------------------------------- # @@ -210,7 +223,7 @@ fts5_aux_test_functions db do_execsql_test 5.0 { CREATE VIRTUAL TABLE ft USING fts5( - x, tokenize='origintext unicode61', tokendata=1 + x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL% ); INSERT INTO ft VALUES('one ONE One oNe oNE one'); } @@ -239,5 +252,7 @@ do_execsql_test 5.3 { {0.0.0 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5} } +} + finish_test diff --git a/ext/fts5/test/fts5origintext2.test b/ext/fts5/test/fts5origintext2.test index 7cf8d80071..26f9864098 100644 --- a/ext/fts5/test/fts5origintext2.test +++ b/ext/fts5/test/fts5origintext2.test @@ -13,7 +13,7 @@ # source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5origintext +set testprefix fts5origintext2 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { diff --git a/ext/fts5/test/fts5origintext3.test b/ext/fts5/test/fts5origintext3.test new file mode 100644 index 0000000000..57b5984f4d --- /dev/null +++ b/ext/fts5/test/fts5origintext3.test @@ -0,0 +1,60 @@ +# 2023 November 22 +# +# 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. +# +#*********************************************************************** +# +# Tests focused on phrase queries. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5origintext3 + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +foreach_detail_mode $testprefix { + reset_db + + sqlite3_fts5_register_origintext db + fts5_aux_test_functions db + proc insttoken {cmd iIdx iToken} { + set txt [$cmd xInstToken $iIdx $iToken] + string map [list "\0" "."] $txt + } + sqlite3_fts5_create_function db insttoken insttoken + + do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", tokendata=1, detail=%DETAIL% + ); + } + + do_execsql_test 1.1 { + INSERT INTO ft VALUES('Hello world HELLO WORLD hello'); + } + + do_execsql_test 1.2 { + SELECT fts5_test_poslist(ft) FROM ft('hello'); + } {{0.0.0 0.0.2 0.0.4}} + + do_execsql_test 1.3 { + SELECT + insttoken(ft, 0, 0), + insttoken(ft, 1, 0), + insttoken(ft, 2, 0) + FROM ft('hello'); + } {hello.Hello hello.HELLO hello} + +} + +finish_test + diff --git a/manifest b/manifest index 7763b46700..e729bc7cf1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\squerying\sa\stokendata=1\sfts5\stable,\sdo\snot\suse\sa\sprefix\scursor\sfor\sthe\scase\swhere\sthe\sterm\shas\sonly\sone\svariant. -D 2023-11-16T21:11:56.608 +C Fix\stokendata=1\sand\sxInstToken()\sAPIs\sfor\sdetail=none\sand\sdetail=column\stables. +D 2023-11-22T19:02:54.078 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -88,13 +88,13 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h e27cdb10e38d87cb041dcb56cef97addf7d902aeab07e84e7102f5fc65d3357c -F ext/fts5/fts5Int.h 88ab1ee1eefa6f98e4c7fd3c96c99ef76ed2819cc3058736c87bb01e4a301628 +F ext/fts5/fts5Int.h d330c2e20051c300b26325b8ba29aa89e99d301c80e2f51092e5bb70346a17cd F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c 4b50ed0c724cb160f086e20e964ed2d57b99d0d3c1cb1b029901c0300b11bd9f +F ext/fts5/fts5_expr.c 0d846134eafeeb1f0724b9c8cc02a2ef9c4082519aa3923173deadd5155910b1 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 70fa4a6d8a062ca4b63a62d0721d72ce2f6336413c6e8b0703881c708797d24d +F ext/fts5/fts5_index.c 7b87808d788238eff4a0a68728e6ed49817e71bbfb328a18050d7d8e92a5d66a F ext/fts5/fts5_main.c f151eb2c6d27418d907c88cd623ad4508bdcf518a79d504e850270754c228b74 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -188,8 +188,9 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 908a1fb6b1106e4b6ed0f9cf683c2ad7f986cce1aea1e0a13b3309c6f568932b -F ext/fts5/test/fts5origintext2.test a654c77f1548ccd8eab7f6d07230655c0070cdf32dcd4740ccdf496f77d5282c +F ext/fts5/test/fts5origintext.test 7caef7634889bab8b44d145141c0d9325299398fb89b116bccd6262fde5659db +F ext/fts5/test/fts5origintext2.test 26482f4af1f2785cb01d06af9aae202289b6e8cf7b708d18aea305b459c2f302 +F ext/fts5/test/fts5origintext3.test 87a212b8235794348c56cb70f21e122d182a5af688c56057b90b7c151d0aa347 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2144,8 +2145,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 a34b26fe7f60b74e7ae5cf64900920a3d352a20da2496401bcbc27041689cd07 -R d7c277a055a404d272fdcb5090bf371a +P d711c96ba855686d6881a50498418de3492144f005684b5ae55bca24413dce47 +R 161c23f360cac1706a2c5f6b11155312 U dan -Z 0e1bf556ad9eba9db356685a09c7ab31 +Z 281a4d74e3cce55af028079774718a8b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6373f95ef5..38bacc7670 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d711c96ba855686d6881a50498418de3492144f005684b5ae55bca24413dce47 \ No newline at end of file +37b271c19d772bd06524db816ded03377b426efed7a7783c8a96f6fb156ecd86 \ No newline at end of file From af54826e4a76259a37d396e88dc3c582ef76664c Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 22 Nov 2023 20:02:55 +0000 Subject: [PATCH 234/347] Defer building xInstToken() hash-table until it is to be used. FossilOrigin-Name: 9b005085ff4a53cda0a1dff0c836630d6d3b95b9c40658ffd2a886f3e1b37faa --- ext/fts5/fts5Int.h | 1 - ext/fts5/fts5_expr.c | 14 +------------- ext/fts5/fts5_index.c | 24 ++++++++++++------------ manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 22 insertions(+), 35 deletions(-) diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 317d66db99..9d2622448d 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -550,7 +550,6 @@ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter*, const char*, int, int iCol, int iOff ); -int sqlite3Fts5IndexIterHashifyTokendata(Fts5IndexIter*); /* ** End of interface to code in fts5_index.c. diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 9889bccb32..6589d1b3e6 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3044,7 +3044,6 @@ int sqlite3Fts5ExprPopulatePoslists( const char *z, int n ){ int i; - int rc = SQLITE_OK; Fts5ExprCtx sCtx; sCtx.pExpr = pExpr; sCtx.aPopulator = aPopulator; @@ -3073,20 +3072,9 @@ int sqlite3Fts5ExprPopulatePoslists( } } - rc = sqlite3Fts5Tokenize(pConfig, + return sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb ); - - if( pConfig->bTokendata ){ - for(i=0; inPhrase; i++){ - Fts5ExprTerm *pT; - for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ - sqlite3Fts5IndexIterHashifyTokendata(pT->pIter); - } - } - } - - return rc; } static void fts5ExprClearPoslists(Fts5ExprNode *pNode){ diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index f206b8116d..a4150d3d5b 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -628,6 +628,8 @@ struct Fts5TokenMapToken { }; struct Fts5TokenMap { + int bHashed; /* True once hashed */ + int nEntryAlloc; int nEntry; Fts5TokenMapEntry *aEntry; @@ -6372,7 +6374,6 @@ static void fts5SetupPrefixIter( ** index contains all the doclists required, except for the one ** corresponding to the prefix itself. That one is extracted from the ** main term index here. */ - assert( iIdx==0 || pMap==0 ); if( iIdx!=0 ){ int dummy = 0; const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; @@ -6402,7 +6403,7 @@ static void fts5SetupPrefixIter( *ppIter = p1; }else{ - if( iIdx==0 && p->pConfig->eDetail==FTS5_DETAIL_FULL ){ + if( iIdx==0 && p->pConfig->eDetail==FTS5_DETAIL_FULL && bTokenscan ){ pMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); } assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); @@ -6473,7 +6474,6 @@ static void fts5SetupPrefixIter( pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); - if( pMap ) fts5TokenMapHashify(p, pMap); fts5MultiIterNew2(p, pData, pMap, bDesc, ppIter); pMap = 0; } @@ -6867,7 +6867,15 @@ int sqlite3Fts5IterToken( const char **ppOut, int *pnOut ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - if( pIter->pTokenMap ){ + Fts5TokenMap *pMap = pIter->pTokenMap; + if( pMap ){ + if( pMap->bHashed==0 ){ + Fts5Index *p = pIter->pIndex; + fts5TokenMapHashify(p, pMap); + if( p->rc ){ + return fts5IndexReturn(p); + } + } *ppOut = (const char*)fts5TokenMapLookup( pIter->pTokenMap, pIndexIter->iRowid, iCol, iOff, pnOut ); @@ -6936,14 +6944,6 @@ int sqlite3Fts5IndexIterWriteTokendata( return fts5IndexReturn(p); } -int sqlite3Fts5IndexIterHashifyTokendata(Fts5IndexIter *pIndexIter){ - Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - if( pIter->pTokenMap ){ - fts5TokenMapHashify(pIter->pIndex, pIter->pTokenMap); - } - return fts5IndexReturn(pIter->pIndex); -} - /* ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). */ diff --git a/manifest b/manifest index e729bc7cf1..dfaf7bb154 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\stokendata=1\sand\sxInstToken()\sAPIs\sfor\sdetail=none\sand\sdetail=column\stables. -D 2023-11-22T19:02:54.078 +C Defer\sbuilding\sxInstToken()\shash-table\suntil\sit\sis\sto\sbe\sused. +D 2023-11-22T20:02:55.862 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -88,13 +88,13 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h e27cdb10e38d87cb041dcb56cef97addf7d902aeab07e84e7102f5fc65d3357c -F ext/fts5/fts5Int.h d330c2e20051c300b26325b8ba29aa89e99d301c80e2f51092e5bb70346a17cd +F ext/fts5/fts5Int.h 782151060d176be22861f57bf38e087a82cfb0dfc4b2fa6f9ccbc2641b6d01e3 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c 0d846134eafeeb1f0724b9c8cc02a2ef9c4082519aa3923173deadd5155910b1 +F ext/fts5/fts5_expr.c 5d557c7ebefaeac5a5111cc47d4fee8a2fc6bc15245d5c99eebf53dd04bf794e F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 7b87808d788238eff4a0a68728e6ed49817e71bbfb328a18050d7d8e92a5d66a +F ext/fts5/fts5_index.c 710b022dcdf152eb7bbbc3f83eb662e1e67c25e0643416096ed070b10d7829fb F ext/fts5/fts5_main.c f151eb2c6d27418d907c88cd623ad4508bdcf518a79d504e850270754c228b74 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -2145,8 +2145,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 d711c96ba855686d6881a50498418de3492144f005684b5ae55bca24413dce47 -R 161c23f360cac1706a2c5f6b11155312 +P 37b271c19d772bd06524db816ded03377b426efed7a7783c8a96f6fb156ecd86 +R 0d85011f1dd994f228f63f6f67d53fdc U dan -Z 281a4d74e3cce55af028079774718a8b +Z f3ab4e11005318d9a7fa0f164d5ead9f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 38bacc7670..81d2b68945 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -37b271c19d772bd06524db816ded03377b426efed7a7783c8a96f6fb156ecd86 \ No newline at end of file +9b005085ff4a53cda0a1dff0c836630d6d3b95b9c40658ffd2a886f3e1b37faa \ No newline at end of file From 41c9e0b767dbd637ac8c5f7b82d9190799d24cc2 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 22 Nov 2023 21:15:05 +0000 Subject: [PATCH 235/347] Add documentation for new fts5 auxiliary function APIs. FossilOrigin-Name: 9be8969edd49e3da96fb8ac2279aff6fe2e215d6ac55162b4734aca1b6316580 --- ext/fts5/fts5.h | 27 +++++++++++++++++++++++++-- ext/fts5/fts5_main.c | 16 +--------------- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 5a2008882f..9feedbba19 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -261,6 +261,27 @@ struct Fts5PhraseIter { ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. +** +** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase iPhrase of the current +** query. Before returning, output parameter *ppToken is set to point +** to a buffer containing the requested token, and *pnToken to the +** size of this buffer in bytes. +** +** The output text is not a copy of the query text that specified the +** token. It is the output of the tokenizer module. For tokendata=1 +** tables, this includes any embedded 0x00 and trailing data. +** +** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase hit iIdx within the +** current row. +** +** The output text is not a copy of the document text that was tokenized. +** It is the output of the tokenizer module. For tokendata=1 tables, this +** includes any embedded 0x00 and trailing data. +** +** This API can be quite slow if used with an FTS5 table created with the +** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 3 */ @@ -300,9 +321,11 @@ struct Fts5ExtensionApi { void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); /* Below this point are iVersion>=3 only */ - int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char**, int*); + int (*xQueryToken)(Fts5Context*, + int iPhrase, int iToken, + const char **ppToken, int *pnToken + ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); - int (*xPhraseToken)(Fts5Context*, Fts5PhraseIter*, int, const char**, int*); }; /* diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 5ef80719ae..9f19b24b88 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -2360,19 +2360,6 @@ static int fts5ApiInstToken( return rc; } -/* -** xPhraseToken() API implemenetation. -*/ -static int fts5ApiPhraseToken( - Fts5Context *pCtx, - Fts5PhraseIter *pIter, - int iToken, - const char **ppOut, - int *pnOut -){ - return SQLITE_OK; -} - static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) @@ -2400,8 +2387,7 @@ static const Fts5ExtensionApi sFts5Api = { fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, fts5ApiQueryToken, - fts5ApiInstToken, - fts5ApiPhraseToken + fts5ApiInstToken }; /* diff --git a/manifest b/manifest index dfaf7bb154..8adb17e981 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Defer\sbuilding\sxInstToken()\shash-table\suntil\sit\sis\sto\sbe\sused. -D 2023-11-22T20:02:55.862 +C Add\sdocumentation\sfor\snew\sfts5\sauxiliary\sfunction\sAPIs. +D 2023-11-22T21:15:05.459 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -87,7 +87,7 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7 F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h e27cdb10e38d87cb041dcb56cef97addf7d902aeab07e84e7102f5fc65d3357c +F ext/fts5/fts5.h 5e5630fc81e212f658afaa5b2650dac939d2729d0723aef1eeaff908f1725648 F ext/fts5/fts5Int.h 782151060d176be22861f57bf38e087a82cfb0dfc4b2fa6f9ccbc2641b6d01e3 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 @@ -95,7 +95,7 @@ F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532 F ext/fts5/fts5_expr.c 5d557c7ebefaeac5a5111cc47d4fee8a2fc6bc15245d5c99eebf53dd04bf794e F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 F ext/fts5/fts5_index.c 710b022dcdf152eb7bbbc3f83eb662e1e67c25e0643416096ed070b10d7829fb -F ext/fts5/fts5_main.c f151eb2c6d27418d907c88cd623ad4508bdcf518a79d504e850270754c228b74 +F ext/fts5/fts5_main.c 55b53085dbd1693b5735463198a8d124dfbc27f08311c839637b44b8254ef7cb F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -2145,8 +2145,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 37b271c19d772bd06524db816ded03377b426efed7a7783c8a96f6fb156ecd86 -R 0d85011f1dd994f228f63f6f67d53fdc +P 9b005085ff4a53cda0a1dff0c836630d6d3b95b9c40658ffd2a886f3e1b37faa +R 2f037553902ade27b74f0aa5f4f0c2dc U dan -Z f3ab4e11005318d9a7fa0f164d5ead9f +Z 56eb427eba85d076d9cd44ffbac445d7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 81d2b68945..eca2a6957f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b005085ff4a53cda0a1dff0c836630d6d3b95b9c40658ffd2a886f3e1b37faa \ No newline at end of file +9be8969edd49e3da96fb8ac2279aff6fe2e215d6ac55162b4734aca1b6316580 \ No newline at end of file From 92752a31d9ed95dbadd445a238f02dd983dd37cc Mon Sep 17 00:00:00 2001 From: larrybr Date: Wed, 22 Nov 2023 21:24:50 +0000 Subject: [PATCH 236/347] For CLI build with unused function warnings on, #ifdef out a console I/O function, fPutbUtf8(). FossilOrigin-Name: da36f90d94b3b55de71aa7965421ea850ac677e9e63d60744633c4dc59448de4 --- ext/consio/console_io.c | 10 ++++++---- ext/consio/console_io.h | 2 ++ manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c index 781ae7fc7e..90772413e2 100644 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -549,10 +549,11 @@ zSkipValidUtf8(const char *z, int nAccept, long ccm){ #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ #ifndef SQLITE_CIO_NO_TRANSLATE +# ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ assert(pfO!=0); -# if CIO_WIN_WC_XLATE +# if CIO_WIN_WC_XLATE PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); if( pstReachesConsole(ppst) ){ @@ -562,12 +563,13 @@ fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); return rv; }else { -# endif +# endif return (int)fwrite(cBuf, 1, nAccept, pfO); -# if CIO_WIN_WC_XLATE +# if CIO_WIN_WC_XLATE } -# endif +# endif } +# endif SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept){ diff --git a/ext/consio/console_io.h b/ext/consio/console_io.h index 2c0e486cd1..98a87db3db 100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@ -163,9 +163,11 @@ SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); ** ** Returns the number of accepted char values. */ +#ifdef CONSIO_SPUTB SQLITE_INTERNAL_LINKAGE int fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated output. */ +#endif SQLITE_INTERNAL_LINKAGE int oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ diff --git a/manifest b/manifest index ef2bf18773..05793e799a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sthe\sURL\sfor\sloading\ssqlite3-worker1-bundler-friendly.mjs\sfrom\ssqlite3-worker1-promiser-bundler-friendly.js,\sso\sthat\sthe\spromiser\scan\sbe\sused\sin\sbundler-using\senvironments.\sProblem\sreported\svia\semail. -D 2023-11-22T17:11:39.095 +C For\sCLI\sbuild\swith\sunused\sfunction\swarnings\son,\s#ifdef\sout\sa\sconsole\sI/O\sfunction,\sfPutbUtf8(). +D 2023-11-22T21:24:50.362 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,8 +50,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 0d9e79742930dadc13f4819fa0fe602676ac32c825304d498171c5922a3863ce -F ext/consio/console_io.h d9ba2de923f11348919cccc2ba161ccbf2e417b866d2b7d072329e6c702eb242 +F ext/consio/console_io.c 5ac3b110d83bba8e12ed6cce79ee61d927db1d8ae1c3ec7439ca3192ca9fec35 +F ext/consio/console_io.h 3228dff1717481202a24f6dcf45ce0b75e7a778bf6877089518a44e1473b76a3 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@ -2143,8 +2143,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 935a8a8ee76d0014df42c1480e044fd1c2dfc26e78abb587d99d861d2ae5eb27 -R 88b228f6756043549fa673c8abfcfa61 -U stephan -Z 2f087f3dd80678f00ca104b71330863c +P 753a75218913c3b9c7ec5438387107369c34d1775d68b04d19ae18197e558605 +R b587086460a6ac7590f679aa25138fac +U larrybr +Z cc13daeb8a62f26641951c271bc4d098 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9e88622647..b47914e2fe 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -753a75218913c3b9c7ec5438387107369c34d1775d68b04d19ae18197e558605 \ No newline at end of file +da36f90d94b3b55de71aa7965421ea850ac677e9e63d60744633c4dc59448de4 \ No newline at end of file From a328c4272447db29e6ed8c13f2663e0e6d0650d1 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 22 Nov 2023 22:59:28 +0000 Subject: [PATCH 237/347] Minor doc fix in src/betreeInt.h, prompted by an email report. No code changes. FossilOrigin-Name: 30d49aaed20454fe006e2f89db58f0c0b2433c99b2676a0b9583f56b7adb7fdb --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/btreeInt.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 05793e799a..e73a273b18 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C For\sCLI\sbuild\swith\sunused\sfunction\swarnings\son,\s#ifdef\sout\sa\sconsole\sI/O\sfunction,\sfPutbUtf8(). -D 2023-11-22T21:24:50.362 +C Minor\sdoc\sfix\sin\ssrc/betreeInt.h,\sprompted\sby\san\semail\sreport.\sNo\scode\schanges. +D 2023-11-22T22:59:28.482 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -669,7 +669,7 @@ F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645 F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c f3b09c5414de3a11db73e11e1d66f4c5e53c9e89876ff3b531a887ab656ca303 x F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240 -F src/btreeInt.h ef12a72b708677e48d6bc8dcd66fed25434740568b89e2cfa368093cfc5b9d15 +F src/btreeInt.h 3e2589726c4f105e653461814f65857465da68be1fac688de340c43b873f4062 F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e @@ -2143,8 +2143,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 753a75218913c3b9c7ec5438387107369c34d1775d68b04d19ae18197e558605 -R b587086460a6ac7590f679aa25138fac -U larrybr -Z cc13daeb8a62f26641951c271bc4d098 +P da36f90d94b3b55de71aa7965421ea850ac677e9e63d60744633c4dc59448de4 +R 3ef6d89fc15fe0585e3af316ac8b0b81 +U stephan +Z 2cbcfc9303e321d5bde61219a7b5db62 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b47914e2fe..9eb5910a33 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -da36f90d94b3b55de71aa7965421ea850ac677e9e63d60744633c4dc59448de4 \ No newline at end of file +30d49aaed20454fe006e2f89db58f0c0b2433c99b2676a0b9583f56b7adb7fdb \ No newline at end of file diff --git a/src/btreeInt.h b/src/btreeInt.h index 563e15f8ac..67a7db25c2 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -64,7 +64,7 @@ ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter -** 28 4 Reserved for future use +** 28 4 The size of the database in pages ** 32 4 First freelist page ** 36 4 Number of freelist pages in the file ** 40 60 15 4-byte meta values passed to higher layers From 5d175eb0206a16b2062482c9ec41c1203a7cf394 Mon Sep 17 00:00:00 2001 From: larrybr Date: Thu, 23 Nov 2023 07:08:49 +0000 Subject: [PATCH 238/347] Fix output redirect bug reported in [forum:/forumposts/cbf4933cfeee74bb|the forum]. FossilOrigin-Name: ce542fee6f0150bbd13505dcd26b21a9d1d23eb2dc8e78e63f9da6b4298ec028 --- ext/consio/console_io.c | 6 +++--- manifest | 17 +++++++++-------- manifest.uuid | 2 +- test/shell1.test | 13 +++++++++---- 4 files changed, 22 insertions(+), 16 deletions(-) mode change 100644 => 100755 ext/consio/console_io.c diff --git a/ext/consio/console_io.c b/ext/consio/console_io.c old mode 100644 new mode 100755 index 90772413e2..da62321da8 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@ -343,8 +343,8 @@ static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. ** In either case, ppst references a caller-owned PerStreamTags ** struct which may be filled in if none of the known writable -** streams is being held by consoleInfo. The ppf parameter is an -** output when chix!=0 and an input when chix==0. +** streams is being held by consoleInfo. The ppf parameter is a +** byref output when chix!=0 and a byref input when chix==0. */ static PerStreamTags * getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, @@ -357,7 +357,7 @@ getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, ppstTry = &consoleInfo.pstSetup[chix]; pfEmit = ppst->pf; }else pfEmit = ppstTry->pf; - if( !isValidStreamInfo(ppst) ){ + if( !isValidStreamInfo(ppstTry) ){ pfEmit = (chix > 1)? stderr : stdout; ppstTry = ppst; streamOfConsole(pfEmit, ppstTry); diff --git a/manifest b/manifest index e73a273b18..c5892e1e30 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sdoc\sfix\sin\ssrc/betreeInt.h,\sprompted\sby\san\semail\sreport.\sNo\scode\schanges. -D 2023-11-22T22:59:28.482 +C Fix\soutput\sredirect\sbug\sreported\sin\s[forum:/forumposts/cbf4933cfeee74bb|the\sforum]. +D 2023-11-23T07:08:49.865 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -50,7 +50,7 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 5ac3b110d83bba8e12ed6cce79ee61d927db1d8ae1c3ec7439ca3192ca9fec35 +F ext/consio/console_io.c 097323f60037d70523421f0248958d2280851f8ff2f9a443d4ee1474c4769118 x F ext/consio/console_io.h 3228dff1717481202a24f6dcf45ce0b75e7a778bf6877089518a44e1473b76a3 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 @@ -1567,7 +1567,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21 F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 0bb36232873d7df2ba627ed25cff1332f6d98c0f7f244b425702c1ab1850bd15 +F test/shell1.test c7127a5e780ffc9e14c476773127fdf292c6db226529c44c1676f37b3793123f F test/shell2.test 35226c070a8c7f64fd016dfac2a0db2a40f709b3131f61daacd9dad61536c9cb F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f @@ -2143,8 +2143,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P da36f90d94b3b55de71aa7965421ea850ac677e9e63d60744633c4dc59448de4 -R 3ef6d89fc15fe0585e3af316ac8b0b81 -U stephan -Z 2cbcfc9303e321d5bde61219a7b5db62 +P 30d49aaed20454fe006e2f89db58f0c0b2433c99b2676a0b9583f56b7adb7fdb +Q +2ab256bc0bb351c16feaf0d4917ad9c3f55e39821cc7d319d86c6f0752188af7 +R d91cba5021090c578862032ea108381c +U larrybr +Z c5c429bf4e3762c78a8478accbfbf9e1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9eb5910a33..a7e665316d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -30d49aaed20454fe006e2f89db58f0c0b2433c99b2676a0b9583f56b7adb7fdb \ No newline at end of file +ce542fee6f0150bbd13505dcd26b21a9d1d23eb2dc8e78e63f9da6b4298ec028 \ No newline at end of file diff --git a/test/shell1.test b/test/shell1.test index fb42e45cf5..5d4243f47a 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -496,11 +496,16 @@ do_test shell1-3.14.3 { # .output FILENAME Send output to FILENAME do_test shell1-3.15.1 { - catchcmd "test.db" ".output" -} {0 {}} + catchcmd "test.db" ".output +.print x" +} {0 x} do_test shell1-3.15.2 { - catchcmd "test.db" ".output FOO" -} {0 {}} + catchcmd "test.db" ".output FOO +.print x +.output +SELECT readfile('FOO');" +} {0 {x +}} do_test shell1-3.15.3 { # too many arguments catchcmd "test.db" ".output FOO BAD" From 226aebae0741f08130a4faad61d3e7dbebd2893c Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 23 Nov 2023 11:18:06 +0000 Subject: [PATCH 239/347] Fix an fts5 problem that could occur when mixing regular and secure delete operations on a single table. FossilOrigin-Name: 8eb3f40021994f0d25ef9d246873796c84886e5951bb8ab991e1e2df31998484 --- ext/fts5/fts5_index.c | 22 +++++---- ext/fts5/test/fts5corrupt5.test | 85 +++++++++++++++++++++++++++++++++ ext/fts5/test/fts5secure8.test | 51 ++++++++++++++++++++ manifest | 18 +++---- manifest.uuid | 2 +- 5 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 ext/fts5/test/fts5secure8.test diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index ba3ce6536a..c467addb8a 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -5137,18 +5137,24 @@ static void fts5DoSecureDelete( iOff = iStart; - /* Set variable bLastInDoclist to true if this entry happens to be - ** the last rowid in the doclist for its term. */ + /* If the position-list for the entry being removed flows over past + ** the end of this page, delete the portion of the position-list on the + ** next page and beyond. + ** + ** Set variable bLastInDoclist to true if this entry happens + ** to be the last rowid in the doclist for its term. */ + if( iNextOff>=iPgIdx ){ + int pgno = pSeg->iLeafPgno+1; + fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); + iNextOff = iPgIdx; + } + if( pSeg->bDel==0 ){ - if( iNextOff>=iPgIdx ){ - int pgno = pSeg->iLeafPgno+1; - fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); - iNextOff = iPgIdx; - }else{ + if( iNextOff!=iPgIdx ){ /* Loop through the page-footer. If iNextOff (offset of the ** entry following the one we are removing) is equal to the ** offset of a key on this page, then the entry is the last - ** in its doclist. */ + ** in its doclist. */ int iKeyOff = 0; for(iIdx=0; iIdx Date: Fri, 24 Nov 2023 13:26:42 +0000 Subject: [PATCH 240/347] CLI .output/.once to not redirect ".timer on" results. FossilOrigin-Name: ce766ed51f772a960d0b7a52c113b55c7ae90ef35050496d8e2a77547eab1a4d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 4c702072de..388b7e23be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sfts5\sproblem\sthat\scould\soccur\swhen\smixing\sregular\sand\ssecure\sdelete\soperations\son\sa\ssingle\stable. -D 2023-11-23T11:18:06.516 +C CLI\s.output/.once\sto\snot\sredirect\s".timer\son"\sresults. +D 2023-11-24T13:26:42.641 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -730,7 +730,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e -F src/shell.c.in 345be14456aeadfaaeced4149563a4a6d70942d8a38a1cd27b75d4e491757028 +F src/shell.c.in a492f9209fe62ce3d1048802d59cd6e38e8444f88573fe1aebaadcd22e04156b F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2144,8 +2144,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 ce542fee6f0150bbd13505dcd26b21a9d1d23eb2dc8e78e63f9da6b4298ec028 -R 7126d4e6870c2158e65f7921d89e795b -U dan -Z 5fb4ff5e4016aed71f63e7eb452805c7 +P 8eb3f40021994f0d25ef9d246873796c84886e5951bb8ab991e1e2df31998484 +R 4fe37cac3670dbe6545f005ad05562df +U larrybr +Z 1757b64100a32f4365167da6f792b0f0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b038b2cdec..8ab89684f9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8eb3f40021994f0d25ef9d246873796c84886e5951bb8ab991e1e2df31998484 \ No newline at end of file +ce766ed51f772a960d0b7a52c113b55c7ae90ef35050496d8e2a77547eab1a4d \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 6242074127..0ffd2b8d78 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -360,7 +360,7 @@ static void endTimer(void){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); - oputf("Run Time: real %.3f user %f sys %f\n", + sputf(stdout, "Run Time: real %.3f user %f sys %f\n", (iEnd - iBegin)*0.001, timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); From d47719ee02da2ddfd22cfcdb4c2c8ca55eefa08a Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 24 Nov 2023 14:01:56 +0000 Subject: [PATCH 241/347] Omit the precompiled binary from the source tree. FossilOrigin-Name: 4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 --- manifest | 13 ++++++------- manifest.uuid | 2 +- sqlite3_x86.exe | Bin 2658816 -> 0 bytes 3 files changed, 7 insertions(+), 8 deletions(-) delete mode 100755 sqlite3_x86.exe diff --git a/manifest b/manifest index 388b7e23be..e9e178fbf2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C CLI\s.output/.once\sto\snot\sredirect\s".timer\son"\sresults. -D 2023-11-24T13:26:42.641 +C Omit\sthe\sprecompiled\sbinary\sfrom\sthe\ssource\stree. +D 2023-11-24T14:01:56.311 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -659,7 +659,6 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F sqlite3_x86.exe 871926af39a0a54f90df3ad8a4270a15cb01be59af42e81afd220525812be3d3 x F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2 F src/alter.c 30c2333b8bb3af71e4eb9adeadee8aa20edb15917ed44b8422e5cd15f3dfcddc F src/analyze.c d4cc28738c29e009640ec20ebb6936ba6fcefff0d11aa93398d9bb9a5ead6c1f @@ -2144,8 +2143,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 8eb3f40021994f0d25ef9d246873796c84886e5951bb8ab991e1e2df31998484 -R 4fe37cac3670dbe6545f005ad05562df -U larrybr -Z 1757b64100a32f4365167da6f792b0f0 +P ce766ed51f772a960d0b7a52c113b55c7ae90ef35050496d8e2a77547eab1a4d +R 58dcc92da6c87abfc48a164925361d3d +U drh +Z 249598a61f7a5bf493f3b02aeff2946e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ab89684f9..6e79ab95a0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce766ed51f772a960d0b7a52c113b55c7ae90ef35050496d8e2a77547eab1a4d \ No newline at end of file +4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 \ No newline at end of file diff --git a/sqlite3_x86.exe b/sqlite3_x86.exe deleted file mode 100755 index 6112fbbae012ba75bb016c5cd5ef8da75c57b4b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2658816 zcmeFZd3?>s7yo}FK}fhk5CkDt8@0RC61xbshLBLPw1`l96I+9j1aSpbwJS;$Rn=Bi zRY}AawX2<$(z+3Qky!8V`Fba@^waO}_xtnr_&uTz=bo9f%$YMYXU@#LwZpq}OwJ~g z$%VfThsm^+U;Sxj!2f&T>SQt%F2Aj?X>)-?6}CFHKUAS>#Gns-MvNLhaMXK4efqsO zY}oKfpZ5p&jEWlOGiaDkK&N1zp~L$Rs9va0K})vkIg5XJ`rDHe-{kyFnsapHWy(o& zPHik|{GQ%;$M`+I@wCBr{F~_vpq${HDa6-gwIRt@_PLvo>X8G+Mndd*u40x!>Z>NhD}Q`UroPoj^?xt& zJ(DS{2Fld;n*3hk_mw}*a7gtmHq*f;(0wYIOo9A*@cYUi_>!s*%3`ci3<5TqJoycI z^A#?`yUnWCtl2MoAoGm5W@-i>>P&Z@`e@Fh+Qw5TBZg z@*DF%d@WuxgQD?xf0ToSY#mB%L`lgCyTuf<`&cWyU3B{j>fn{cb-75-T$vI$$ zI#D-Q*s#wKE7l#%5>6ZE#awt(8B^MX^AJ|-qM?yt6f1P2cwsQbm22T^zmmHB55YdX z2yjlRU`qRBHSIoG50>#Ye931^o6`1GrOCk6FrTad*x8!mN0VW>Z=!fP6p4>d)A-xR zVXn0Su{G*K*b|hl>Ht=GF5qf!8eiKUEb=LQD;@&e)whv10I>t0PP+ou;{k;8=fV02 z`|1s_zeb{J**_G&-()hSJ^h>FyP1IZq{@5+Ti!lRr<HuwRoP97}+(usn6^T#;zD41IShLwzcVH$H~YAqKv- zPGEIEhNY;KE^7kc#7tUSpqlF-vO(GV6i-cB~g>X`wV?ck|WN?V}bcHb)5&m zk`RaNk2?TOJD@(m0byV`Sh@4?P2K}mW;)cB-H^3hMc?FE6uZqqwnsA>Us8ZU-q#=M z)dCcseF3&$DAYfkz``|i(uX3E&=`H6&7kgui^P2SsWq&L95DnLvN2H}@_FyEJKx4g(6{}Af3>n2m$P>s=IfwvX{K0k&;*$FVaZUj7Tj;dpYU|yxh zk9~=3h%0qXH9U>?V*Rxe@U8U%_+F>y^|ztm`JXT!2?YG~C5@N926M`Bz=@*(pI_19 zC$SZWk$BhD#=sv>Q+#!n~vK zt<@AbbAY-sZK(@6je_+bQny-0I!9AXk`vv$k-anzLSknKO)L(JLyV(*>>Tz?ZS zgMNkDDU8m1Q-I=qA6PE`LtQIBSg!a|_g6l!J0H_Vw-KoCc?(djHR=-!Qn!CO%#-A> zwr7wXsRi3TT3xM#XoRR~Ci)6%$Q~~%fFsHw7OF|TPnHhUVxQc@3916c=|RX2^`fq^6WEw2Snj(~ zTrMa6(;5~>FY4B{ptYlKq3Y~cs9LRY+n$Ln8^UO9z53gA1`>^q0A}@qrIwVQDJ1oY z6zgg0_^AZNQ}2WQz6Q;`MTm2UZ}dO3QKU53>{zH%z648D7^yaqV&F3x^7fXqJ5rZk z4eTuqyw^l27>n$-^(ta2paPASBu2Ev?Y7543FOz~Z5&Rl0= zJ|{t_zX*v2&1fi80m?}uvrOaGYB!pjt%8u}C=w5^I}@MB!kp@ls_H+(6221VnG312 zDS7(-8WOX%10EGeZ2b}lC)^>leVgL=%4mtXftGEj5xcV+Q1U$B?PY-0vfvv9?p~MR zD^(I;lRK|zN=-_o_)`L4O$hor=122H89wAQWc|m{j|zW5{i+{WPYYO5NmQ*{EoZl) zSgSQ4^&oZeA}kvQ7_}HsUomI=QW}p`rr@b!@3pj%mIC#drp4GfXgMv{^|=nUlVa?j zg}}OZg)bt8Vl_=dSHqwl;LZkPo>sJ8m zdMhMie}OQuKV3+u3z&EkTbjng@^d4^_WTG~{3&%kJ3y^?4(tQP*gp7?T0G6~7(0LJH!C^E{(@=D3k8J)W$ZoWv`Kth7T9+=hp$<_@9AyTJ8UZye7(&DM zU}>hAb6=TJ1tqD2s#Bb&aDC)&CP?yVnyi}-*|Wo;7N3As_li+>b}p)ZIY{Ge6-u8S zhOi+T%_Eew*$zNRQA58kgYTy1P+{#t&ELl*a}-9Zy$j*`X6hFdf+*(ZQp;O1*JrFiW15Qt) zGsy{v_3wkkR+|&;P6f-QCJwnHH^7_qgWYUs_-rZzHxb3Xt9s=0zV;xBhRK zFKDUlDg{=o7pD0SLgJ~Mom?8`Vme^;41)RLUFteDfH}_|>L#3`ZecjgPqdW>_C@I- zg_hIxDNaj+*-24!!YOQC*d2+*^N=X_5Yq;2rY?RthPxM_Zmhi4dnYVO-ywEW8Oj8O zpayr)w=W6mqX_u!>3FkLIb4Wxu6}=^s;7eGAf*sLO$K}OLkNE;Zru3{v2Du$CDW)2 zk?eORX~Pz$KHiJ0c?p`Io`l*z7*JiR%IpDj zl6^F%+zU(%*ZdFsRW4 zsE5)Z)X)K;VK9U|Db#)If@UA}w}H}`Ls~uGU!d--5U?-ygB86_KML;wnJqS0nS)cilHo9tuh)+gUn`ex|;1Xc#J_FR!qVslEyU9p=9ZYe+ zS`(|b9{Re=oo}zF)6Y(*@x2uD`=YdVF+lkzFgyJX!S@?j!hC3`r!o{@Wq9fG-(uZI zoY)D=oxhy`9uP)$rL}Ro_^6SNg2{aWC6)19x(uIdBb@lLoReG}=662=Bq#|issrVv z%24y>rSa8sD3(#iy;l*vNO=hUS~FKaK-KLCsNJT5y`jYVyM++ET~QhwNxO|TBW72i zXw|vr!WNj%DOf&>q&P+E_mQG#X%F~*bfL9#BVf514(72MLNU#fd^(V?(8ls+cc>-) zMyzFJly25BchpQ&oiB{U+qD7pbP^f*HiWaEAa=hRps;+EsQLH9`(PWdLanY7+CByN z2+im9D{zigxu{1T8v0oS`dckp`$ns=q1O2|4d~no5N^qdeu~~d-+(Z&EV9*GB5NHD z^Dm#%nX4&a3l#-E90b_9l;Uhn!jA@nMa!xwh2gvR0Huld0rlmQ{ArztkjLp@=kX4TO$5CCmvzti-zzru3ts;u?=5o513pV^ytl$S#;e@r{l& z`B*EZzfzZ}I*Cj#29}%x7^ofg$$VHc?oP%}fZuC>W|3TfZq?b+dw{HULx~ z19Nvp`CsJ8HI1Ow&?%wUHx#RPK=u=zSC{+t8^nb zrUCjcjsQEJOx=^WpqlOi;wLyUpatn$d)-DwG)3w9rVw_0g2ZXvZnacS{j(bCahEpg z6a}lU0Ch~EzH1&Cq*I6cRf_5ITDva+=X)SqV?MH*bsJjwFcJrI)rPxDP)7sFjh$Z(y z?2R=Pzv%?up1y#^qhP7=In*f4rhZDiPOF#oY6Al2(&RZW99vS?hPxDAzr6)tlG4F0 zI*2(GJlbkuKkPxBzc%Y8#{uKz9H+;$cCj)NE3`Kje}^Xf*MxdR>2DPsj5lie^@_&N zK_Ao5r1P-+G7D;3$(H;8*^0Win4ul)u}+2|ODN9y9kIuMqF}3TdK>9*TdE-n617)c zk-2L{z49)E<9||Ypc}qA%Kx4zm^W`vT@CHxm0~C^upxU^>DwDh47)VMf~)nYtDyWmj`d&UgxeexbHI^qcS%a08t6c5|$k!JMQ#i~M=L1^LL*lM8 z*u@L*d3;3i0}Zm@CDcE1#Wc@oKw}yH_!jLBaKIP*Gt}^b)RpxDTwMs*pNVYyMi44| z4qyL%G<2q@WXD1+*#JW2EBI>deL$M7Ixp9OrD9EnZ2bYKRj1N!v69s7vV%3%Zk6#C z3jEcX(03th)28&R#J>bsx8(-LId4^=*Z|A>E;N zo9I9m{XOApXonD}H=y21fC`?Z$pTs~HZk9-0obR@fxmR$-cvWx zcZ~~O&6lk@$4|cw^O`^D^wbO#SZ|=YpiU=aH&ZuQQ=pG#T_fEPgycg@4;QeLRiF<1 z4%u2hFb{14R`CPs1~sI3ZxJju-8~f7Ez=c+i~^&$P%C(RZH5+^ z`}=mp9{i|wHLuQS3S@qWRc*#W4ON=r+K{^UGAM>A1^b{rva_wIN>+$Gt3`K7SznnM zv^LcP=38q4D|Jz}S$_Fx4A_1bIrb85Y>+4a)Y-sUi#T29w`F>a=%cOo@d2nuYbXzs1^+XKbZuPlI#myzwIN058!!n!!>{!ze7yof44 zWh0{lok)-L3N_Rn>X!NNRkuKR))akR2Vh!vZL_zlA<<6>)slj+tS(F4=Q3@B4kl|0 z01n!qZq+cZP++|DgyP4>I@$+zOL06^VfgGnfZcz<@>vPA^iufxV>-U?Es3^|DSn_F zq;xI79JzQ=E5N}P7&K-c5`A_;NKvZcr4`^g8dVFYLRh4mzyLjpHc?dhNYNr*3F(mb z)IARY#QlV-n0REXD6iY6PLJ$_g7;QnUp=kRHy2R!dJpXD&9XEK1-rF9?boxXX$)Y< zQV4%VqN+g{V6`vQry7cSZ&GwEi_+D)q8_X8nmC=h-ny`#Gzc-@^E5Q&JlHnfF4PRe zj0nZQ%(tlfxC>ymjz`Ydvy=3S!8ZI#f?+7 z0QvR;THS=&Q%@Sf{n1i72rx@2=i=p9FjhxU`fAHP&h&jcRNYIz2|4>#B|LjXww9c~`)WHWY^~FzJ1GNRVm_e z3w1RUP|)WOfa_*#agrmdwS!PK0Hxh^IX|i@7KC+#rST0|&TH4bmIh1sK&VZX-1$aA z{XtvcfEhHoQh8^)C-6;|rDJvA7^WTKctNoDbwf2?VPr`@sE;BcG~5h!;T#elKL9JN z1vX|7Ec*&W&8JzsSW~5!Mn6bn9;S4r@*D8&)RJ7gn8r)~4A%4+b)hoX-xI@q z-c$%?Ez`Z~!l)vEdS1BXL^OPx#eBXk)U7iil&nS@33?RxBaXTmv0zSlshh4OE3^nk zc9u_l!6xZBE}NU^ zT^tNnVI)dFet_5@?czPYhi}eDc~Gil&{o@>a~OmOfuGjH*P zK-ZfLFeecSDEFD>DIk9wBR!BWl~=Td9aNq#z2u)j1_ZdsI)Q zp}#nANnu@>_R|UI++b9FItM~k1+8|v7x3*z-I^qdt&hXEsS=uZPlT{tZ^EDVgc{Hp zi6#k71SnnIcRGpE#?9edpu^8aZQD^g>QBCjmH-8X%YMkFXkQ%|iCC2r5Uy$RcOOo1 zaBuVtR+L|0T&sT%p@q`Wx6+XKQ`2_g*A#1R0dvyqd_$ScKE<#M4=iZXo7OBRsT-^3 zB#WLP@@W_|wOmr)1G}X>r>z>F@(ryO`UU0>3c!51FoKzU;pjhE?;#K3`o3Yr#^wPDQE*(Kj&z}wx?Qcm{W z*XGq)pHN)y1=dP0U7O|D#xio0)^Fcw5YF60Y;hpP!;1WMK87V+Nm1P@fC5TC7B51! zR|l|vbgMD!6ZpD)1GUs!)P)GlQNA|wTiOl%oF-T35OPw{_JIaqx@`H|36?GWVQ%sc zU{3@121n7~{PBQKv{B8FeUlY;m-r!8Nz=^d9!&=5PCrGYo3c$NV z;TtbYt-5+~(Ot{wU#Tml#o0(&#%SM)(nb8f{@7Av4|Urw0D4>pT-b$#_b61I_#UuT zd3(Q`5I#|U@20wx$|xvNhLLHiPd6&*Rw!W;>LZl6J~<88q0e=kbXWCrPl_d{BKzGJ z7=E!l*xDL^aU9LUHn9`a4zo;HSU9SGYNC>}dpb&PXRBgBqIlEp>T*0BraI zzA@^;l*Uk(-9*+^X?!`|J#;n!8Wf^fH3;Tw)1cne2Li^MW}rTnNtRE?eE|4cfx^8r z%%OMb!h-rp6jd+Z(K2Y+1Lo_`sOzo7+SCbPUrN#CIHnbuNHO0U6r>%)pgZ+p4%P5< z*M@eh5rm&Yz;5b&fAVc~1Yrr-ipe|gg=Ix=jQ~uodXQwTt8TL@mdz^?Wk`g*RHgoV0Zx+lEkN|VlSAz>auLU3;a#o03G zsE%6xgXmF14;nhC4SYx?iastB=W1Ulo63ayNf+Ilv?Z+3ZAzU3fSnJ~w_9hImRc!Y z6(jEGq`vVTid)W^h~6#0#>>cB%2`bh&@xO1lT2ObJFi1Qs`iD8$6#658p1I-w(T9L zdlb@sRLD5}1Dby<54Jf1Fh*ZMwAD8;4aR{zQM3(L8h>FjpyXP>bu+-CuTS1`ryqWL zq5M^M8FN+w+8sh7_ztZ-yaeCD>hO&@jjAGYalWqsE3ZNLW+hmoEihk~JL7a2H(mX` zt{ccbZh(&suW7w?*C%Oh&Y*NgFC^+zrtY_J6#SxDx4904F;`IJGM zN5GfR2JqqU)V0(lTeEf$E;UE2yfcI19)-TA-_h=$J;1K$Qak@)S}Xk+^@*DylvirM zOt(x+Cqtcf1bxenP@Ja=(<_I;qW4gDUh~vQ8&~$EF6no~KGTLfW;exjjbKH6N;9G| z#VUSCm~=iWtLeB?$IZ$Ekocq~gra&LIIMI!>Ih2vK0+e(D-vZw4jex#n|ss>1$dE47pPz8A!vnmzg;r2gg&{Msaw-2gW+p^6}|^*yp{TU zNBLS{CT9Fz2CUEtI#b*mYJ(qPw%WmZ>nX*m#4BAV|03GPO37>Y_EVSI50K|=>PigitT}U;d;cT%OwZBp?;wgb8+1_El@5xGMrpV!>K3ezCQY6hP^;^xbvKo|GPS64li~jQmtT&ry7|6N2*^^o4ezSWdTAnFWk`H8 z9uTT8&(`SvZ?K}p;3+VFq34=Yo4_WXL*kvGC`~^LwbQQvpOO^Er9v3r4;J%DWS99; zT&sg;iqiPcjTu{^gwh-CHyiGX=gTT2^fbP*-&yRM%+0MQz7}b?sVMvm{QP zDJoA^Tn_ez4-)Tb>xei@opWixXe-6mdRD9)O0mOx0{RZBveLI@S^<5u*gsb8eX%22 zK0k!|#Txzk@6q^}7O;G(wST$<*nz8n!D}hLuiNAY3QHN0=<_+isNUD0{8|p`-9IUY zo&sA?0nIZ<(?-dCbh@1K=X7^KEeGlYeE=PjdU* z)WXS}MlZkA=VE#FFK#+{K-h0ZwoVn8r>>%I@_zK)Rg~Je6VOYW>(Novz1JF0T5;`; zZU-w|0eh@?)mk@!4~xQlEe)2RG?7v@`Fk1L=W(zJ8u*&yVEMEj616LUEv$}$va12( zJ7UJ=(eQQHOL3wSmgipr;`gI!O=l$f$?ezlvD@>n0lz7VsQ9wc6WR6JHS(ynF8T~-wbJR7ht##zObk=TSa3JY?H|#j;ksfK<_KUi=w8O=vDjFF0PpOyj} zy%K7b=b|>I*jxV)rk|)KHUHY_tAjhCG@hbt#Hw4X>LMI@ht_sWK}|7t`w+gRhp3xg z1$~Etz%I4{w9&{!e+}Px4{Yu#Eo=24cqpB^Pt?_GZIN~E1)+awH1};s@tF*-s}ovH zB?$|~GE|q3*IH71s^^eZzih9cF7YltyYNzPMEm+XD8v zH+*BY)bedJvHN|8ma1mNiWi_RMjro4yV_s6e&4G1o zqBKq+W3)neF}*mA);A|#$;i^b0VZoIU$~9tAep;bJ8=yq;3bDq*K8xeI1r7KhsGC# zdgE>Q9xKR<4MwRU+j$cbeYN`?)>12>kuH4;Sr27j->Wl0x|iw}iNumWkbPTq8^?&n zhKI^1O6{EiX4MLv7XVAUB@{EK0P44g&t(vVb+-U1y33t$41IR_Dq#Ui4=dH}ph)vn zvu=^v=&#GJk;-xpE00|_3PR~*n7>Shuyz!L3p&bl)ER89E;HZI*{_P`-?4^t;b)x# zei#Zkt3yrI#$ZL}!(4a;*tbgG27HUeP&vD!cB^(;I1RKeU3FZVEg$&lYW%!z)Zf<; z;4>w6E2?4X+oiDJo(I@L1=V``)^w3hZHJ1ZV0i${l@dFv>6Oiy_=Qo(xZ%e~+^5FUSpzTt}eX`16R?xSy23DlqM zfr2-G04wtuSaG>HR?%Yec9h;w&bs3w#j8EgvQyMU-l$rqi+IlsVCBDtP@*z~;Z76- zln*7`1naHy`MsOScG3PeS>KXeQOvofoTk+Z#D-RNBC6<0rDZ`Zy;hpKqk6PhQwHYE zx={*MkY1$m2-XR=Qxm`>C1de7P-UJ3^QLZKUiw0^g*p>qhxsGzERLP&UQzvs9xBbWXJ`!7&#=$Z}$;7@@5Ij#&Y^5~ikv_7! zsK|d}7<~1w(Rc}^mvcWy%Llr(+Hw~yd);9fz5qh_ISAkAI^jSB-5RT#u|!elPXtUn zMH|%>J5xReysfVYtLU^{RO2>dJ9TGtW=cMdY^;{m&Z87}>0M)rHs&_EgG&CEx^hFn z9@Ip(rLvpM+F*~nQ&&=xYh`U@-7moxuIO#mO?i8rVS|4JtDr0Ik|R+XD(^-sAG)Ds zfA>6~+y#J(UdgIB;QL-ThNCqn4=XiorE^7>rW8+VJMOCZ;;Y^23(+#(;zVMos5e15y4!T>yjLSO$`e&*bYXu@W8PmU;rDgE9CQ`+la+=p zx(s&iICUKsQ~XAJI}V_wkIqcvbVHS{9C+3a_%7@7(e}DH&(If9%eAS$`7_j#%fO0> zI=3tm1r)d^%84U}P-p9azH|DV_kiw3-j>ZbHTjbdqUzH>Vg5M+(`swnQh!5%V+_pO z#ZvDiTBc~J9k>l&nm?+hE`iW2FQA=nKiYbMRaIB>D?ZQMg3?v@;47shp~7WY9LkGB zN5JyqT?lLEg4NY%mDj4>UWz8Kw}2&N4aKRtycivik#na~_nmgNB zQ!A=mS^)mm&iSEc_SK3=gsuk*Sc}sAI+xW~x9;d7;LB&&+*O^qR{%bbs)+UM2(@23 ziWh!DtYLGoSqhd9WOHSG2k?uQ=|IJ%@p@#f{RM=vx`4fA0R*bk-*$K6l%-3%={h1m ze1g&ix_;WMZO3^s`hHyt;jhmy+(q+r=WPsH5RTHV-5{JP02n8ijP3|aq{8N~6<`}= z?zdgRI%{ZmcS7HMg~R;X#nUzUuYQNB)(0V!P>l8XhPrk-$M;k|SxnbnmuE{=2u40_ zNb%i2F{qsKh|iVpZVE=?2dOG?gVt7$gHX0Lgh1UB&eJ!VU&+OVyTUU0Pp}s4Xsu2U z`10#E^xh9Nv}h@O?#j#?N#E(K@RgRIx5>}Lb>1J`4fSFD;j5>coiz$ew-->Hpztth zF4X)c2u(FbZ?&SX$0}4gDWuKS_FlU)#pgv}nb;5NVt15&AKEK zep#yn`KW_1*T0B*&tE7Oi$$MrQ}j7~0=PPX;&C+|`I)Fo0U!Pin7S2$O}Fvie?c*2 z2aU(+>b^@DO$KY7-)#k0_YeKpSq%j-5?ig8kWsn@YV`qx=Sq9tT!8G%Vt|@TA?9j> zO!Gx{jFOHfI>-(w2Dq+uUR;hnq)#pGp8<0{PF-G|FIx@+9MT%RTOG}3f0Bg$m0C4D zFt(XY-2xp7>ox(D9z(mG?*Q5>c^aq3oL?0MdR0Wh&_`-qZ5;gpLX1M`%H|NJYGV%2 zcvRBkM(b~}uYufN`8aj^5i;47gc z#WlU5ozu46T32R^Q;>*m12#)_9fzX+ijfF^Ox-7nCFOL7Tz3%EvF`y!TtmwTy2ZJk zm%XBv2H<2kEQ#{aT;-Vkf&m4J)4_h4FYEll>W>BU^mXDa{xOzb9|5@3627)_(|evY z)I}E>t2DhE>6y1=e^`cRQrA;^-WEk=lRn|ulouoG=)jTs3uffig>IOrO?#syYzUaU zCg$=}5Ip3%hd%&*|DC$I4(d+Vrnog0EMfp)mDc6gzXAG;q@n(LllxOoZxhdB!DuyJ zQ2l*dA;?c(scahs^UyB=qjaJlr?W=Q6@bmV5W7(c=AO#J=k1^$8#h5%+=-rVeV=wm zETmXvE||08=~SJ;Taw`nV#5uG`S;r;loe??7`O+3c%z zxokPIe;%j!rS2Ji)s@d~`PPEaSPrwdVK-Q>?7FaSN!sdg`_}J(oAY6byNLQF znoXtE(Dyo$<(Vf7l+xX_!k0G*zTwL#9{+*jqdzEGcF<%~r4R}+?=E>L&P0L;t-c)k-Y86+iuo;-EuF?ED^< z8{QNvsiA?8C&T1DbP|U9rbAlddTRuSS2c4qrI!0bM=1_UC zC%R>ywhVpc+`*PZKZOvj(wO- zBlOQ0Eqd>nuQWMO9Zb*>xyt(}2#|;T<{~j(XN@b>(Nb@?i8Me~-S`%Xhzk@Q`6zBP z?q2i2T&Wv;w`PHjlh{2gt+mVt@YJBZ;e~pq7-XO8F0zcgdq+cia|xiuD4O(m6S2SQ zq2-SsV4kgP#H_c8J~2?c6@+iaQ-=1VO<*B9R~*+jt{ZBB719L0-Uq_6LQc%_aIi(X zyXl_-c1f>uwL;39jQ=-KF39yjt_N~Gkn4e559E3v*8{m8$n`+32XZ}->w#Pkw#Pk;yl(B9&)Gmpt+O6Y3w+_A}LfT^2p$aasAZgz*O$rQWWH9S5k(%Jmg z6SGG?bCJNB@s5cFy4f?obU0#nyTsn|=rsBH9E2tu=#^0R4v5_gT}CS@eR}t_9<-Yi z#OjeE=LC1NRSWWoFwwYUhs12V8_YWy&kP6KUWLU2m18x)zi?oa&ylCrr5E4O_6njZDq4G?{GrM>M)lNK*fl@ zk%b+R`CS}!@;gpsaqsguwl~&MGx`eV8A@ImH9X@IVykhUd%i z@W@deVvDqRhnmYbGc_3bZ~cw3^gE)8CI(nsgF@_svNR?}TU>4VV>2C*9|aH2ALJ;m z&cjkQ$PsC&YHvK!;jrBa3AJxy5JPNd?A1P@BGf&0w`WePMtlG6Pi$6url=xW$_qNi zxMVB0rS$C6n}PLUO#P>6Og&R*&@X7PNlk{>&1)SF#|RfkO?$l<8f$N<8N1u7PjA@W zv)K>6-v5wLTVuI;hkO<5aBN$_uQ?#v;%N@J7t4<$s?=a7dxcqO%S?=90`Cn9wMXQW zOWk9eIB4Ax6j_tjE%x>!#1tB0PsKl>wmbGla}5)1dtVqmb!wK;hP{5F_CCzvEPJO- zdfnczY|j6%_lE4flx^>(|FAcYePrTm_HNDlU-m8?o^5ZbY$E+kq*`b^9i*@Tl^Y~`}YL%&YEE7jS1$Ce_aiGT@%Cbx3eZ#Z(Czc zF3qyjA;E_KZNbbW*DU?EJJzKCSr;$nQAn1KPL2aPI?F$OF^M9E&CRtNQq&KT5GRr5lmu-B*>^6f# z>}yzR|7m=Q9ODC>vh9}DRqhxa#O{Yoc`c$>;?Musuj#f;)9hdNYX-hze;)hO8L!#j z()l&}Hyhn?eQE#r?6$H0Z&uEK+8>h5llXtI|Ly;^fAWnsMBkhc7iS5eWQ((OkS7dn?RlAMww-R2_=n0^woaJK#>{xOEgltw~k~+muKA&53!Y zJ12I?2#HTM&zb>gUx3BSlxh5Wo6fLL5I^IS%<=7@B~I(XmgaAtn1Abhc7yc7wmnt$ zwOJJVr?c~};<>T*|^A3t<9n@xirngS#YkVNOwBmV&A;n82boQ41JEYxZ3|Dz%dzu>|LXc zDVg#*e_RfKvuytFa`-Jd{D$5oS=I4LV++t1m$Vg%tot2WXN-r7H7T}%!~AKIQHE}F zjeX>ZEXYpKIcpbvWkCkph=Sxp7Wa_QZdQje$q2G2V;R8aVOYCz; zWVH}SaW6(NHZxCDNruTYh>G@9kR{~bsc}g!XmzCLlf+?1Y^o#Nwl6&1(Z}5CaPI^+ zOmy#+(9XrkO|tDB=bmjaBbRM&nJiasWA`u_*^;@{uAu+vK+X=Jye4PAH@}v@B&TGj zFh;Ml@*j5EfUG-pP>z__HSM_L+H?uG=vSU>@ITk|+RX<~3U=cK`=Ap5@2jFMe} z?q+0t+Vkr6FMfDSN>LC__UY!R=_uaa7&CLL=iThLj3m~jS8qDfGbIZe=qte+G$a(w zp89M5c_8g&s_bR2%f_voWUZmaQyW*-B-8jRvb*EB5LL_kZSrpHM0ZD`w?~wZu~Pk$%Oqu-X%=L6V)wUY+J0xBYX3~0Q=;#cP~Vo)r)TdJ za1U#$&BGYK{~bLSWAqqE_mD33LWcN@Y?bvAU~%16d%!@Z?oYM&F}FHOl?!3E)qc&V z8BPuJM3st5iYz*#xl_XyPEmzolk>#x%EO>swI-#MNs8U?l-1KLKS|$D^-+Xd+zl5O)(p?!a6`_^?v7qX;QHlmtC4EA{i*;^YalFKSpQj^bmT<40|`gx+gZEf1%!s3aZ zmM%e#V@zWsKXI{$fmrX_Imp(mnd9g^vj^_DXYP^LRIDExyjNOrQmL0?7~8ljx=kTb z%^b!382wevjHV*1MI2;~JBl}=PJ2V7cf2`t46e^A|e1_SPJm$?5L}j9Y^htSwE|~gC`UZ1FAnS)sgSG(pL|h z=x!@m#nslurA&~Ey)Q1jXYQT%PSJj*d*&i8mWy(PD}u2%e6rg&$L(MlW%t({_pR1c zsC_1_F_rESwLG*dx(oOSaIVNQskqE$-nv#PB$MUpGR50~+0)4XbDD+01LLX`Qs3b5 zE>k=R%&^9u^a`Vx8moV{BW3EUhqhB$OiK)=9(iNv1WZO{#0{Ko|M4Hq5br*{V|V+! z>J0p%$!Sehrt1-V+bQx*tt}7R(JFU>!?W^7?-Ft6BVDJLZr;NG$f&zLd+&8|GWAKB z?(Wpb(1fMh9bH4L4{dF|ocAY;@@AF>98PHKWsIdk(`1}M|Kd_3JrHnB9L0*BUbuHc z3-1(_Yo}6^U4CV(gvK!uv&iA?nk|NBF#`c*^*#kS>CNOU=(0gQfS{p*D$@=JKZO zcva!{f@a7vWQd0y(;(OQ$u$)K&!dcBkOT%(hM_WP$K4XQp}^6$hL3Jr>Aqz2>f8g>zN4RW@?KRM*^7vw5BcDkD;lnjk%NHZZ3 z0TK^1=pKki_^UQFqP?nXyr_QB18s}Rf}|KR3*w~Mxaxmc}r4|z1k9^%NpX} zHS$c*z<5VwzJ1!iP3A56g6suzSR#v>w;&f}&s>}>6m=`6N%g1}+0~bGs?Bk8bA%7& z)V&b?Ij1J-R#rnwJg{s4mbHrNBFh?80k8IomD6%$A{!Ydjd@Y_%343!7WB!fe^s|N zseZmb=6S@vJ_>+_Pqe?;l4BZ}NDFiy(=4XGDe7^RWKHO%uo(Wb9-onqEyRoYX-Q(z zHHt}W>og;uIpBcp7!xqvw(G?tioH=M=Ws#FF6lX-o_r$=x8|4>XQaNt-9l`eEkmfc{ho0?fm|Tew%rm*xyOL_qY2KhF7y15UWDebDnnm!NRp z_8g}C+gtpM^An@fd9c%K@;X0N%$9cUYN?8qS?8zY=D32?V{zOXJd|^O>Vo?Aub-c^ z)M8;tbgxfK-jl`~xm}`5`IqOXe@Wz=pUVHo`Kf&N`3c(V=cjW2d46ITy#1psHO({E z6B0Q>b!V!2*;4kpl-Js?lkOJOIoSS=aq=b)iflk)8(AQHPOF$k)lE^4ICc2M-!#v< zNj?@6UENVIGH=>t@D7}UT~$#i%BjO%m+GeUGVwQ~Jd7N(EGI2Sp0ij1wYAro+ei*$ z5qL}2rJDaA>cplAn_~a)u@QK+U+V5sCrWpF^{n(S#fX6+q2{>z_%D0QGRMuK@M6ab z%iggfC;abjH71kYY6;xzR-UiyR(({jA$euPYDe9_H>~iS4J$k+zt?`nc18J>{mOs; z===s}dN*U&GV;;?H#=8z{J<{t+Rl}dVbmFl((~>ZL6~QI`jS14NU`RI!$E10O7`b- zSY^=`5-QK|IfG61Sk;Ew?>)&{r0yJ69!3=%T-#pD2o^348b_dP(n;O=u>DwE5}O}w zRA5Yg^N!?Ld(D)@=F1!9%`Syf+WDeXFD3iX`XbsWe{N7^fTp zi3e^q5z7k-XM|1ZS^G~*rd3A=%A`XYncZxl3nVw|qZJmjqsP<;|Nj3ASE>rJ_d3AE0 zK$c-wwyg0XnASo7@*G+5bnt^}YR~ToH)vYVkA0COO3(H;ql7Ujv#Q^#-c^sEti3nCQ|xs7pd?SNr}*b;>m%zd~UQeK#VP zpRJRNb23JCneK&6&9bHt)>PH>K#k`N_qV3IHmmRbR+RH}uV%5kobatR)&EiS-sTwrz+-zPA>R+Z!;&8;q#DgWID4Mnf#1^(-;?;5}6#^Alsjj_`U9brRwhF zC!yvo`Gf3Jvtk&Np+HR2K2a^K57P@-AI3B_MOt$550@rE_67@$(Sj{sOrs`!qDlwy zV~YBd15CrLo1ly{rIRcbN!a>qs`w&#pk?k?jq&ueo>%hVw>6A(AvFGB&I;;gZnYQO zt2@;}5&f0rRi8?KGU-pe!yLDer4`Ix;~6yAp`(3$`@5ts}EtMuzgyFk^6JXvLK2oUhPU70uouxPdrq+$;J;OqU_{C?ha? zSns{ zj(^cJOT|}v78+#V@YFEX+kVBEn;tSXE|fZ>yWSCVjP6d&;fiA}#qRcqKOa?set6j% z7^>OhvKG0gE;o2#(t`I)u`lOb_}c6;&V%l}N8MbIZEYksJVAE9_Z^Ol-WF84+cywM zUWAK-lW#0#gkR=!8gFHD8~9Zgx0YYRMe8g(Vz(QFJQu?(uF<}Z(JuC+!A3)|nNAb( z8#e&yMSkjyjDIq(nCW@9mS$9pI|$dDBW+N`Jo?O-_yk4Ffz#gpUA)16RMli(DWu>! z)3P5@>~25fHEqWS+Q((6`mg>KdeOf&A!I>x%)>s92PURnklmG$Rxa$$g!GH}sBSaU zU`I&AQd+flF>df(wflS8SAF7egxF7wm6&JtqREMWK3r@?ne1OM>W(NETP8`Ymwkaz za^-O`Q5f@=xMs1JF&g#&_tKK7hMj}+M5^OJs*NiMbvLxA=M`CkV=e@Ce^9KcY4v9Dl2OG@4t1nS)uRbMf{p;nN zw3!FtR;HNxAyJJx2h(LA`?{QsANN*{ci4`Em;>P4nu&1dpKbun0m(u3Yb?PKTc&;V z7@47Dwg#ckBX*C=lN+ZLZpezf2 zG^$^*Fv1n~EEAQMHkM&y^6!+D>3OV<2({0`aMvmxk8o&&Cj!(oi(r$XasjJ{qD5C$ z?St&G{nf4|A)nqcKg5HsYx9}o5|}8jsj7i01U!RS+dB-$5=0%5)#qeMMD~1*+vyeV zIWM@QPT2CvW(o4J)v&Kxb_ua>HnL3@orzh;>Hw+u6E24*@0M><5&~Umx@wmFiGl7x z_TH>YSQ|lQ)$W~YT60+*NHlDgAEx=}E0x}T@WS@+7w&sK;QyCD z^zbNODvoQ~%0DD5CerjWeG&r;@TU7>T>8CK93DH-#T3;!+%Z*7GlF?|LZC}{LVjDI zYeH)mhAMl^TDu0>*BhA&M!LgnO7Pbf^@?7%mwIy?=OHg|p`MO=Y2vjqHoF&IcE`(Y zUSm#ph1u%`bJU4Gz4z&M&a_qjd8N_$173Iudw+5`nDtsEG*~A*!5ow*!|&=-jhTi+ z3@01*Tayy}v)bOY>F&LC~|QWKl;}|(kySS`MM(5*Bcp(hoe#wl$`bmtkrV_%W%bh(2#2M zQYtb_3es*!thyH|pJ6WpZEaxaC*^aGl#2gpxYi35YS~a>+#(aFYd0{AHst=N=|@?4 z%*&=V@F_XgRu0W-3VnJ5l(f)@?k{cMnx!BV@_vKNBmK)D?5HG}9Pb@G=%^&iUoHRk zukxAK%CU{r9aB{82>lMuYqy6o-21f;rbZQsh{le=F7^{tQf1q(#l1=cVdGiQcq&aO zq~Z&Mvh_vQ$kOLxUzjDjFR$42hY(+m6qwJT)S|wI;wR-L{q-$orRU$=r!p+Ap~Oxz zX$cku*;i$q(D8{KX=UFo1}8iop&=2Q4W@|(6Q66m;; z>kQ1`*NNJdOFJ@5jX8;LWRZ~2;D|ehcu7OtlL|-iJF4w0A7KXjCkDGa{>c0p5qU# ze_dO>_#5HgM?PF{$apy_xn#-Y@W1q59fNO*!H54`U+Pv9Is94U-__s^F?ih_mE2$F z{a^kO2LBs5{2u?yAKO?i(t5hY@8DwggKpDo12*3sSCv5vK4m~EJ+byy?otT@zKhxNBj^tMh6u+G|`;>MXk za{4f9pr17`$Qn4-I(3Bz6NlJ(ds%xov+kIuV($p+XSyj&h_kHahrQAoSkoHV-8wZ! zXvCZEMv|iT*i7n8>8d%>N*w;FBx+8 z!nc>F(1u9r#|4-ZrLyttUe~&diS@6Sw@&Q9P>=Rdf7H=irEfWhgdA;4d;rZ!OxnMoL<~*m+fToWjD89ldrqkjOk~6 zh$bKN>WjtQ9wc8W&bexc+lg937^N^vd+Soe04hCfPm*sJ<=Upb^}KoY5KH^QZs5s( z6?S`;u+(x%tt~YcOu}|cj7oe$nQJuX>3a@wyX7~bS*=&dBGih@qGubJ zQemmVlyS7y%q;FBe4kb);~1|jVb;CY1I!GQ8q?x^$k0V(9A&Q7@!ezHXE6C$9oAos zv09on)Ozn~*VW3qjICvm%@|SUl5_I)vdNd6-R|0K8a}t9w%8bcnJC88oS8LM%=?ryC5(S3Hx`+}(b)oy|Bw%X~m+vYp}EL?Ia`e1G@7&bW=W{-Gg< zu9J%G9IRO!Fvh^BnPdCCLbRnlA{T45XC9ql)RtPFLbP3E_!N(f23D))i7#hx@H;u5>-I|`WbVWC!EQ_e7ZPp6fv0cZBZOp3=6ivQaIQeo>w=`p=>DI_N5-8)B?I;#6{adADQENw$uS+|HnCgpE*5Od*adB-=tFSb<&n z@G;EN%owVC$?2t&FXhV~Do@)UYpQwm$x_Mo{BDPm|18A{CNk9GLlJ%(5fO}ml&vwiB4N#P*pz;t)fG^iGOt41K0~qW8qGNmf zMet`K%`}?LIBGr2f}4YWmaBdkVEynmkrhcZj{BZSm}$|O7Y}(5M-7kl4l@>`C(iUE zK*{;^y_t=Dl2Jpz;Ogk7aig0STXL;vOMm{I!-iwtd^eVS-8*{>{cIuKt*N-s*jbEy zo9>NZWIePeoG6%VFF~aIv!LxW?Rajxh?HKolf+7M^7SIthsjsWZV%{ff439NRIwyNdf{b~yF@17&MrAfW0edtLgbThnYQ z8Aq|e0=sWqmLW$nj#>8-QoFv`5{axc4JR-Oy5ipcS<#eTQpQ@3*g~Q+juMJJh_4Kx zHHFyQ)z)#WMvy6DbT66VE17($K=SpHZjWq6s>Oh0tvzGJ&f1KWDSH7D^YSF8mm>oB z@w8?O@pMbGb@VV+Ae%g`jIcdIW1i$|<=m3#U5vWbF@P2=__sa-IRvedw!mhJqqaNA z*Ygms3%EU|vzOqt9#2jy;C3|m?~+XFs&034BB-{9sz#)#YTl4uD*1ALx0K|YrT#DG z-aS6b>RSAs$qZp2BhMg%4l+P&MomrB=%hB7s0o)4(6||QpMijM@0EKzKYwu@rLHq0b zdEfVsm(M56^X$vod#}CrT5GSp_ImPOWHO7v>kes0xFZTq-@;H$!QqRAOf4FWvCJA3 z$rcuTM3)p-d7lvk%-0o@J5<2T_q|57K=^I?aa>^O?i8G%T+lqjS_>++`a8+9hqd=& z=(g_w>rphBSV1@FEnziDU%>mxf}K<@4tEG^qz5H^2W034FHyKg={JgwS`?RHoWkJw z*(NPWJxGS~vbX~z3w?mH3Lc-4#{m&6y@fm<)jz^k81ozmL!rcGqFc1xZ;N6>%1Zaza_Ogan3NppwEqa@*dwzt8@zX9p zC=qmIxoIn#t4yKoGIi!F8Z2_yR(Dq^b=Q`|O5?>i3r>9l%4tX`x^qIy0T>}C#0dO6 zOQ{IYqN+x+dUZaDWdl|7!|hkYn$VN!Y`BXx)lKKjQ=-bE7k)H_rSxWam+J&MFG#nK zQN1uvvxEIv&Gj(!`@Ga^Q4`|YO{1&OV`U4mMPq0VJy}c-bqNXzm4I4dcSyYbQX55| zDj~`!6545O+v7+2m9g$B^4_OX3PK1yx5zwcv(LWQpdwL%HQT*-Vd!>h zl?p*?ZC7g9z!4RaDFmjpUDQ1m^>UW`a+^n+`;elN&qB9%XdOsefzKMZCt{BfYgoKp zB%K+Mj`tm~=ZSJJDlWVa4y=n%@(GH7=Q&Vn#-6kAo751U!Jb)DNJO;WyyNMkF7iZr zs0~g80FMfQIgn|Ou`MkcZ`v9M3x`wXSu7JpiNZVJvrKQm*oNlo%7bO0@TPlQgY5ZX zwL4ycEKrF-fC!P1}(|S1T!GWfMttK z7-be7Ls9-m-(7@sodeH#SRvA8$y5}qP*Jd3pqUM7t|X!b$q!6T2>{s!>SAcTQkqIK za0CbnFO94-KSk?Wr&Qq8@c4D)6v8EE6r6?RP>rD-U%_$ce08{8iFD~~K@Y2?kV&Ws z_c95xJ`ui8YS0vAvK~7iDsNso{qFh9qVQf;C-WFS2<{cHY*BkN@E0);)9MVxD0-*3 zcC+PaQS3HP6k;{QTeLWkD?W=6a)&9aG~J`Qd9TA61UwnRynax$ReJNV-d67h5sF8j zfFr22EYb>1_l*e*z9Mf=`n?J8i-qLMq#{i#!2d#OJuFb*O+c4NQJ`TA0j7GP@tD?= zJb6#*y!SkLhZvP$ocv&)Mn3wHq|mR0w6*Z!8Nj9Uu+oL}Qo77zg;YR4R&_4w~=N8x4#mg+zgQM5^na)9PdL_91*(9?95n)`w^bq8DGQf|=Qf(6FblOI5=Nm!r>#+W2l9@y zvc(QoX>({0`jAxU7C{ZLO3V`5*e6ebD_5qqcD)zIQ_OlRHs4_F7xx_!>Qz%3VIpMg z^hlYI4zo6)6Uqu5DZsBYH}as?rqDK36p6phWNY0K&HCZc?V)S4?4oTUGYSk9;<1uunPgj^IJboc0QCCy3-x`VVX zXal^c&>~Rauf;s>kOD2CeklNST384|MgH)k^ipj&13_PO|v_;Ej*_Ml%0U~Hs@$M~} znP{xDa%B|(T=<091ttDjU}~jr8;@95S*#7HM^8vW*?@Jx0+v%`F?{}EDUDf&d?k1F zra|k}2)-IuJ1Y9>QDXlRbMBK$*$M9?F#@bI^F?ebID~2QZ-~*!AVpWk>YT5Xa;<7_ zQ5~ep9A+cL&=u;m!aaxA=sF}1+Hv)HJQQE(aFWK{66|O02&yAt-bFR4SP@^C?W9? z^@I^X#?m6AZe-yL`77C@b#QcYu{;ZFQ$!`E9U0~@%^b$AK#DRt_+xGT~2pXh0N{nJ=QH%Y!mw3LPUR{p)N(n-QNl}@SPu)J2Q|-rk)@g6_bRj`eJnT*w-Tt(#x})n zkgdJ6-Ao#@P>d=h<^u!?0|+t4YGhem9Zfwio%eng@eeANudH2j%srr@6iv;U?wJxk zk+;+JCKmk%l|sqV7RJL6*3tP#d883Be=US!o{(3Ybsfeut@L)3_I}iMB&||gcY-9* zDlw)$tftx4&Wp3!c?nH)14tw^^Hf5^4+~svy;hpJt)(-xk}uHC#g=`Dyar(h%;RF# z`(obL!C&QfA-}~cpA$6lOV~}9kXE8>ZL_t z4Vd0KWu;uG)a{1wOW{|T2c_o8qC3`ifMymI>_dNF#M)4`PB6B0j7z(l$NSC{dzzS9-!3(Y8@R(7iEEKcly>+Acw1nKZg81EP!D1cEIz zPQ$k+rau~y{=-D*pfa2p1-QJ^BDW#SM=8nU4D@YzRQk3&h`voDLR9$1Zd2qZRamn6 z01tcUgPOISgw4JzbzcK3mFloTZ;)mQm zZ&S+->vW}9uETp^q^dJ|UjXZsY17lmdaGn931d?x5-CST4h2!eXN1K_B4D;Cl}wf; zg%vE3o?@yF9~1d`A-Xna@wWVgnd$=yVZO|01551p09JfhFw!rhv8`K7y3)Eonk+i^ z;k=Wu&KHesujHhUPR1aew<~WCMgJ_-^@##ob7B5kQB!Mri#D=}PNvmx^J81(&O0DV z_i@(UW@%Dvy_)$lXDX$eEa{s0DnM)c4hq54h(%jjMQW5)+wN(>0W4b&YWi3OA|BKS zMEm;=G9m>`4~&e51mb~D)S2V4oL=vxH3=6YU4Ybu00))x59AGN3fNW(H~a8F@zfU#9T z+4~Cn@IXrj=uEmtF9k)+&!h4qJBI;lY5^M$Y`TC8FX3Hebpt+p z5SR+*$60*N^oLXO_QRaorSmA6O6Q)zfsZX+pVU|p^;0(IwrSBmmPO$Jb#s(QNf6V2 z43p%xL!w#38}PxvYlH47Sq_2wf%$?AP#>L`9qA)^O54^frtbhMbY5@X&Vm=2A(?3~ z;Saw?ch!t$Xj}#$wzd7^)4h3k7Q8!N7E0b5*(vR$i597;r`Y_&cubpy$D}_zpEIo^ zW8eq_``r={2=r&D(6>KNSAqBW!?S(i>9WBKuu>`o%M6`>EPInxZgy9cQ0kEo96VO{ zzMzZl38ds~j}-K>@!O(kvTr}7cgn^XFn+T)IoD^tl$!%d~~x2bINea=l?mPR~d8xf8ko(~GgKV7E>8OhQ9(VWW7@1!g{= zR^DEjD25{|o-!i4{pQ~E(es#raUePfF=IX)#|8$BDvmCjsyV0?vfKC3OPM=Zzo^7w zx8}3~87{}xvH+E%KszOk$`c@_L1xgJdv+@GKB86)UFpIVPIzyb7c|FL6;&t3?b3t@N*~i;Gr{b->7J18 z9hp8lLDXAdR|?u~DX(~;wv>a9O62HcX;1pY0CMz*N3fK4T&em(tF{9}!CssA&0Qd0 z7Jy=B^CHl9)k3itnO*Dw?R5eOPGaXR3h$Ea#Y%D1Sz`9nlMOq2MpOA-^X=s9<+STh zPGeb}U4asS%#HFV15HsU1cncgu(aBq5m>cX}o6OPo zR&${8pcaG~dj+MBx>*B3q5dFisDx8BeMTo{g*jlS%-oaixj-gK_GNF3mWdq=l*j0S znpjyTvJRBc)a!)OelyJ^8EI>*Op(D*3Wv{6Bj?`QNN#gBG#7EANLwCdRTM#^PV_~I z5@|%S7JdjZW$)Sb!eHG@On*2ceRLwLM%dxUv=vm#K{FX2+WpSt+;u|mjWU&#(mD7g zKmtKni%==VUY1NXOA{|9H>sweKN^=zCTO23?E~x7Po_581*M;&ZJPTEcA|yo;_y4_ zs?9}N->cz6$&`>jbMYuYAb3XdpB|QkA)B=QyfYIQ;_DPlwkidnFMV{BW=t1S^&twN z5^8ly2_DVJ3e}p+N%u?^s;4{?lmiQk49t$7**QY(!F43Mkg@}DHF+|&T$tIf1U>~1 zOF=nMXy-v~H;?cD8k+gBrwUIf9B#1fzIP#F1w~U1{MOmCB|FQk#{`8YAYP@aXrvR& zed#}rgvdp#$ci#M3ig237YjOpe1uZT2Oi~rbdF6zU*;Z)h_{KbFTGD@wG<{FQ1jIx z#kNT?J*g;x6!kOpEtCJ{)$5c!#C&rkkmJMT&DjH^?x_c`$^-tzN9nT%Xa zvu`+wqqY4{xwbb%h~Oy~#CC-7bw(H_p7*Azny+sL?%MoBddiM`7cCq;Qj~VJ6SHrm ziAF|)LUYJ|g#2M<$;4L=van@oFnDiQq;vh(Bwsyh{7TyEmOZLiyOPO=kr_?6;fF z3eCiR#d{9F|FJUD(2fg!owcS?TJ`qcXnk^n z;8GE1n@6G(oh;fZO-el9snQ*M3F(kr@H@&_6VI2~4b+(*B!1t&>4OJpGusfS8@$z; zOFSTagRd52PrmhYmQv=cV3}|+YQ&x{Fb=XDi49&SqJta0@w^>^HVTD$7Ybg_zf$2w z60TQ>z3zI2*RWcOvEu6$RxbgoAAr?bD|n-n_v)K%(h2%|wam>QmUJ+5g6$YBE|#tg ztu$g8T;pasiI-rs>|%ac8~Nn;C0j1g6H7Lz`pUgfqcucDnI-Gh3e_ulCcUMLnwERE zx?n7RLHX9=FVQ4-f;`-Cyu$1wveiIXD-I5!wfzI1M@Rrg*1?80y>%~(tbbrq$3|053Ey`7RF(`7Z z3NI_&%ywPDOH2PO5AXkCztg{md92o{KeJQy+dmI`%Rh*}eMoNG4U)T@T=O_Xt%+FQfcWZpjKEo5 zJt@O3oL-Lijj^Q+x{x_n#Bf{b05P;=WF)!$5&a!wcjzw5S4;bb;)ZNQp-vQ)I_v z;Vdx&XCpYUasR)v&bF4HHc}7sfEFm^ThI_Aiik>yf!?)`h(+ zlF~>{V>|SB-(dO%Nk?}t|7?TNTrs93KBBt(v&PS)HHw$m^1ZoYBssInPd5?^27|Gm z;`55m;%nT926B8W!7{!au}ZRFW0$7fR}nY3c*7)eN+Q+;136x4Z07XxRRnd}4SD+H zGzrr{DCG^a&j^&ixM6~fhv>V)^x0Ujtt#ib zX*tCBBxJwQR^_^GnkzO^#rWiMemi7PBiwuXQoX`2A zTO!G2?&LC$)sin*=km7%uxL3%uZ0f%1dBADoeBBemc@7MBAY&pXJ=d~k^HK%Iq_p{ z?kkDYG@^5)Q-x@9Rx|Oz@{f(w&p-m)Y|ijrDM+W zZuUDfvm_*9uy=gLmGMzT9;zhPPp^l#et8u$C6@EYjk|llR*hEREB|;yL^n6O3vEEK ztlHF(o?XYuziiX#%4vpU!<07vm8oeGn97%$ead)tXUc1)f;G~RKzv=tS<2~q)12>F zx&*Z`kCuzl1OZCU_q4v*L{d|J`Rg0=iik@`l4!6+uXoMz(58dvz4t%DuXNVc0X{Qp zz1C&K07RLvk$JCV&v)0lx>q_DSdUz$^2{#hGPgilf4f=lrtO_Y>4fEs1pDLQPD?(_f#ZXY&5~MV;7)c#m3eo`d!9-5?KT_t2noca3YgXz$BjX&n6)Wqt>;5p#|>D z`cTwbj|n9y#v_xs7{!rEBu-l~$~#Ze3)XN)y>-tunal#h09jv!DUoCz&*sbqEJYLj z9;5ZAfFmxhj~2QY7~7o$BE{^!y~;^EGY^r_Jb0ZE)2u4zoOjnb@9uNnN1XGXd(QjF zq3At*VT6lMF89U|uiL=h8$)*bm z4>VVJ9P#t#Rgtvu)7BGBDB#UB0h~ zKHuH=kqU@&OPy##lyO*Z-z=l?-Nta#=oYj2VkEp~=}q(pZo5lF@u%rGk@3LEf2k*M zGLBjStM)uyYblb*(_zkbn;+B3vF3DdjjOYn5T?2FYUtf(5(uMd49U$EB%%kLZT!rf z?!j)+S=0v(f~jhM#9CFQpz*FYG}CFTj-?imy}Bc{Dl|@gu?7yEFDU38VczL=eIx-P zbDy|^R&t&wk}hv{was+6OFF0LT)BC=6U4>6ozq<UmqAk~yH?th%OM5fZX z=RO`%-m6m6UFGSu-qiFQS66DTvpjv*cQ^g%Mn=AV#Qem*_E|>Z_-2??YW6s@v#5(+ z`cpG6&dM)e<&jh+G2kkV!ae4x)J=qo`^!#aY}FHnuks1|xt(W1?ER&i-~GneoA%r3 zN6Ht5{Eg3)pWP5h&CYS{k@&;z%TqTw-6hjDS7iv$dptEShekgxV(f`de@&#}A>n)O z2`uUv2GN6bv?|f}%Pg0g%KRlLu`(-aqBg+FWqNfI@xJW%&26Kv^TdpBd&_PD(x%)q zVL_RW+Lqm{hkibrVa(WXe#dR@GH-R8dy8gyif;Clr(?OxmK3FL>oDhg%X`gRyo8uH z=6#m<(7k5tFg58zQIpONs!3;us!3;us!4nc;PYzIQZyo(F6!WG3LQd>9<4;w6RRzb zcU-OYCpHtJj7VI(4b2;Kh(aTI?pn2f%ZBp0zPX8%J?vpjmoOVULu$*+NTIvC(MNRIa(}Zb5e~R5!DH0xiYo=nO-efg=BaTMUS?zQ#?V!XYLZwAqT@foH zn>r*hg||(S76Z;EHvzl?X0_Km?M#)NMAv)l0X%Re;cepdck)R=q9bvE@KC$Yu)kQY&5Ii=PIN^}HtvsdY6^P5$37xUKUBK}?Q zI%oW*@Uhm+y7H|i2ZVQdjc46u#Rdlc zaW?m8w4M{2-BWLt(NJ*xVXfGE_(Ir{&go94TF`K|?1P;m@fw3dav`xS35RQLl9_x$ zJ<0ru@kN$Tv52aNB$<7i(R#mnk^(I}nKRuG(~lDEIkg1VBzWjEC{*TQmW(LLP_6&A zB@rP`%Y)Stc7ix1lKWh@S?wlpXT2*;Og#d~LC)q}4}@4g&tuG;=K=ZLm3Ak3oIK?l zoA*J05H2U}8{CYv&Fy6N$E9YuQf?&>*L}lyc1~y<#yFHY9naF72{8mGD}wm1v}>p9 z@Q%M*v0_DEZ~9b@96wEQ?P*I>Ty8}N+mGbrbm#28T#oSkn}cKK2|DE^wEiiyg8+ys z5nW1wU?!;C*CTBSus67{?RulF10X4{9O{RFq%O`m78_#r@Nag@VO4&1I7u|djCocV z#mATO6lDpr3-W>&km5R!Byh-Fj|()+^B|c@a)DV$40-w5%taYT+nk}OGVYTjp_Q(NM-c|r*FK?fZS6B!o{}}L zv=|X?vg&`q@6(S_aL42P2hUsSiddIO9np=<58-Reglghn<6!c2YRr56M29oxgKs{9 z21OZx7xx`F9h(x3TBc6xeN)DL5>>U{`cx^bp57~jP5qIVsC7gqNvItdq!fKg746pP zd|_m4ChoPK=C#iHZ=EQL=Q-R|$EQ>ZFcIxCCH2+^f7TtBSdCZJTMM-wg0{k!W2_td zFo=(^3QwqROK%ndI%?gb8;~$aeAN%WBp)CNTKOL{fvg>q*j#)OZY-~`e!gCjiJcGl zF<{_DqAv@U4)^9_<;>7)hT;NTy>&)yod(3hhrC8DOD7WIs6)#I^<^>Qn{xzT^V6}@H52oukk5Tr&`daHFx&sEXoUq4`4NjT7O|@GeDU)3(MK)a>R>Q=5)?-Ijy^u4bQFixAEN0Sx#rK ztCO!)&2m$cAGI?U;Z>LkTj!(furw0=9ElL74Fg9e9JSttjA1usw~#0c*=Mx=mdYa5 zekIK*IrHzTVc~U(|0%I1{Er=To%tOt)@Ipa4XI{+&0X}i7HWqOYHzsSPTlFOtGB)c zDJY>a}+5Kx4%`wT%?EhEQ9MPBW$ zzEl2RlKHOJ+KNSA=+?TA2MCf64Kh)-tedZ8T@VBWMAi-0#{Rz%a^pnE1x3h>gpCE1 zkW-#dFpSN0wvd~EkYkK)B;4qtZtMg_FRdS~BwTY^i#TuKW*HpC*1y&>nQAiaJrmSJ zjf~ zrs@aNkodeUM%ajg#SS(Xzj3eYxW_}FT9{)#d@r3XbiN&WzG}Bvd4N}F$+&e zR2A@AW2HC3P-$emSgO6&KMQ-Ig_o64%&YY>rOysspA+ey<_E0LF{-oJMOlKg6x%IP z>h0w~y;L#je5KT01Z&FCtVxv`&EmaA_edDjH)yfmTCCa~U{G^;jasvGBKrZYwn|rg z#8&*@c{bvl@vC{AFb=zcwUJ{1EGCNZ-c#T)Ai$a!RA!1mJIViyd5FrXc{RT0C$#)V zvE4BB-T@r_^#b zFU%3=;)&C)#_t>3$0WLNzs^bYx{U2fN6t4Ln>}q~jO|fp`%xl?xbqGq-o4S--llSD zoW}Nf&gP>=`_UX@`vG2XvB=Hi!OkajUQVLZl{iWr4Xwt7=nz8+Y)NE&U`d!avS;nWGO<3f|8QevE zf33}VGa-q`S2VK;*xuf}qZ#68UNfSq_29$WzRUyorLd=qU(?X=oFH>*$NoK!9DZhqQsv62?wD>@uL z=sNv0wmJ+3Zu28!eoxUE>rC*>D`@U}H?1C$-4Qr*_ul{<66@@0V4^ z_H^530m#D@%@id1E#}#g{1@j7cjkxJ?9KoNM>Rfzlg3M_BXQQ<_}?HSV5aGGP)d)N z|2$H~w>g?)?vz8rYOaO(HecZSm{IX@6XOBoab}5pJ9L@g$frNn1ij1tj}VMtDTLF( z4@R+|W2mrvh`=I-Hb+$V`(>G6)OttQu{b3BLMg=ZVdUbP9qRh=^0zkxWh4OD57weqzv3AK z0O(bOk22Y33-PZ=SqdrsbCL2f;1qeJ?nymBp1y;4m{y=ndd(rC?nTu4vYfKRf8DYX z63?F~B3sVai#nB!BqB6W8nxb0N(Z0Dvb#UiHSo>LRB7dq&K_5toqcX+)k#eGabwPL z>*?!gl1&mTI)1tCc9rV(H?@qW+p#b6`OyDX_CunUX8XE9)S_-GLPBi8<5~QaDg+;9 z9?)sB#Sy6?IlYsQx->Ty@#U3KeB{Kq{}f|=4;pXkzaBzeCK>h%+`yD07r-Z_TsU?4ElLEk9}Ql0sADKcW42 zKExf`ydZ~9E%2bJxk@yGkWjQweSsn5+NFlZzEwiZ$oL-^4|VEH>WSNZuqdr(c0try z>>Nf8T2)a0tE1Wc303-NwonenDnT{&3KKa8IUgHfUZyJdMT-%|<9yhZ!@ zZbX|m2M3oEy4eL}@&yfP(jPunFAfu1{!J!RwzT`X>|ySd>kfT~Q{GLGOOW(@WBcUi zMo>n+HI*iU!C@Y9vJWoxup`~x-afzYJyOIdK}T z>zEF@9#o28zJTECkay6tYRunF`Kd@o%YVL-#RscYpAzpohU1q-1!u}EW2*`d0OwoMvGpn{U&h}Q{x0NC%MZ%u*P|BJ zx|nsm3*cQ^e$-m?6n8^@JQ4udwd%_U`PMh}f>bg@%#O1BMTR`gP-?Bu(Cvq?ICGbh zus`ZM$dZ{bkT(D*GUV6DkXq~ItA{a-sPzYoTLUuWKPlU=j@CqwL=hy4*%Wm~*scFq z)x`Tmb97}+GSBoKJ8;Stdqw?I+3>3lH>wW1hIcq~t(GJG#+-iZyQOxs$a3s>@k?v1 zlWb!m*2ICX#|mimPLK3=K#tT~U(v=mp(6BO!A`qs?1#+6Q1l*FRy@{#Y^*i;YZ>xq z#`yn~;0XuwIf4(JOYrK8jKc{&^#6w7L60JMU(wn5iHsum-!A;p-y(M(oHN8pY4cy1=eX=J>sVAKCh5!Ts+B?46_6VecHh z4twY5)qd~K`$Qahq~pC8(82j=lX%MsgHcR^Jay(TA5vOjT$R2%XrK72(>QhFFIL$g z0wjP9{Gi#D*BSS#?sUnSUDF6pKjejTcs-y31CeLS$#!Xn*8m!6@s}X(`=0BtL zcH7rw#EZBq3c>iTdq0t)EJTEHBM0{L*}86ezM1*xP}aqOwZMVx;<8~iqxB6hLn!zp zl!(j1eN3DE5t~YZT58R_LRM|z0MwK20jOQrOMvdMK87!t%rQiq&*!+UqrB4-K2~zD zaok4Frjx$Q9FDt3JuN@sgS{6fPGvTp-$?rL!x=Xs-K-}6nL%+5<3jw5^{+`&gExAi#>oaI|wh`ED~YhPx!!dx(BKAN^7H-;{qs0HLbB*2}yHtT}2;d+|YRp``Kv zIg3$a)AP-|c2HEbmH=zZ;7iA9@xu?q>42mT#kw><5C=Aq(K#OpL-_xbCkDd|T6ZXJ z#12PMou68Igk4`cW!13JcB0vjLw6VbUCq09aMJ6~2vF}i$N(+MJ{WF~);}A;2!;DZ zd;AhCXLq}!4NgZs2f=^I+>N2|GGReo%36laEVFveYqmvC-S-HJ)*Fh7159S)nwqs^ z@khK%u!Ln1N(}X0-yk`B{RsJ&=#WpK6>fS3`f0WnBEuM}-_i7suQ)@~%pFqz+HFR?<_+dNPgk|#e_hpTp>cpV_xW2Lo` zTNX$+t-Rs&3#g!rm+y;@Dc>I-Ev0M#B7RqeFce?SwE|(SfHdd%lS}iH-wuH@bxvT@ z#uaOs!OWUVZF*P$F+^-ixEXEQ)z2gdsudBiWzZ9TjXmEhI0$1F1NMC9Gv9t{+4Jqs z&Nu7X$Bfj~M(7D2)L}unx|XS>QC*mf7ArNHPt|c=+O@2!I-f2(x~fC+lN?$=xCXT8 zZd*{&axm`VTMo_ZuXdPK`8XqlVwa#g3(~3+?U`g%zFFlY<;EU>ls%Z*UU?_lm{&H1`Srwd6Rm%0y1;riZ3qotZB#6qtYobDwE(W5c z*w}2b&Z;s5-ylP$KVDIu>gB($@*fHA*o9_IiCI}1SwQ!ER+u$UpQ&cn1kB2yog5rY zrjA+TGb{aemh+q}k6G!pv;GLdX<%xCq&ax987Vev3eg1C2G@*8oXp%M_u+BlmJf#} zJ;?~T6*(k{dMUX>$}`Fv8j>pj1%G_EyQ++%FBzr4rjoE}AII`1SWoJkIK_`UV;5$= zst&uXn`N6UIypsyy<@YB)khN|M|@m`Pu6-L&x!HL)~9fOzGzta5sBRy?kUgqC2ReU zyJKI;ls;wkqY)%))dyq=^1nYhJ&>1Eu0@sdWIPVbs>f`)wYfT$vJY}z}fYr0~U zUDE@?s^ABO;S(55ssvc)$AU6BOBHYE&l-DsFYlV}k#b$rz5LIQx){|p-LKyi3>%`f z|d~CpI5ls!x*D=Ne?ef%av`We~?VG1hHEX@*Y`^(^%KEyd=aURrwWd3#mkImM zM=lnY?=c>DpDfsSmx1a@SNlL$(Q(DgeCG7v!~^DRJ~(7N;GlyjtT68r6axq@ewp~+ zL(1RMY^+7PV;QJibN$vg9VJ_G`g9=HYm1-?HD-7DSPU8=diA878DsCCB|Hnxe1<$D^V@cXX2Bj#PfqArFw zagRO3S`OnIF9q1aSbp@D1rZsUHSvoe^0X&LB?k)Ov{XaziyVm7Sxct??5>~^19t_G0%NFy6v$Un|3jpCaAKcmb)28y z6$HF>3?)^|fY8$9bePwYWNoSTCGF~(UP$9;!aAdCda=A_^VKRJ{4eoA;h_E~Yq&&R zIW5MdD#}av1dE9^nr}{X=e=HUUWd?TOHqCn{NaH^LEj=k@k_o7cH>;<~@OpSL^O|<9M&uS=}nD+wBB%C9Nk^mhg?L zr1!YWI_`F=?M1v)G^Nbl9_u?Wh=$NNa0hxsA2;yTM76CEBNcT+Xs*9dtg^7f?CRU! zx+{h{yJW{WP@B!aZy>*GTB+bXal}>OiqAFobxkXgEfWW%>Jxg2qfYJ_e9r@&eY^cr z1m1xKZI$lu?w0nhY5B5{IB!pRUlg{Kcd~aRpg{CSx+H?;d+G)Y zeao}kj#@X5&1A0k{_zUOhRF<0{CqBGcwPq9seOHtxoTVpK@2gZH}vx#a>Z{wK3Zg| zZ+=q5V^1scinFxbpsNzgRnNT zns=`sZ|+Nd*J=BYXD_R~WZqQ>_bG!sSDL+TGlQumLE%Jr5=|-pc;ofi`?qb2LsL@j z3(F*&bz%I%Xl=xLe2UUL<$g4rrWC!-Fyn>O=uBzKeNB0~X|MU2SzYMb$s;kmdK^%} zZ^5|Hn?uyX>f%KIHEYK(1jTi^Mzg&4OME&*Jd1zzJ__c36eVC4B4|B$PxqvAq4Yh} zOA*|bE-$3&A_k0CdX)-zw3zRwrmB6m|7N)ydRvL_rG`fW`firvOHrmOaweQXfe(xSzLLl&l%XQx)?}hxtyM zJ0KV{o^7v5^p7>}d!G{-ITE=68!=p$UC7kphm6+Wk(~Oz6Xp(FGYrx%GG{CQ1LG+t zd}01HJUIE1y95jgn9~(+kX7sIow|vjcQUK^&;lg98!$-j^Ngq5^AtlV-?QdZSV@$c z`P{ThI#m+_M%qUbRmnI^ADYiSHv$MuaT3$|G81B!BM89Od;pss^ES5#Z{sOfbA@w6 z+%W6>_z%0>_sDCkkNBTAf+^>$`z)1<^1S#M{)*EBA1M%LSzTbo8D&Md=0{PRt`A|n8af!UB(5m zwa5x|*v;dX`^C+#AtN<&Kdaxk??pt~Iq-ac1z!1IgZHIDc=v7qp7b;RdAK_;#SF!N zFYjN7|AS;C&g92H{is+Vab|Q3)St)E`I$UU+Ru26)(bxm{Z=h4c8r#1Rmyp|PO67I{3z6e*J$|{9%N+ylizbN_BIAqnfBrM&--ibe?sxU zNnbJ4hu#s27qDU~2l)S_0scR3fd7vY{?8VTZ^sK0D(^O0+Gs<1xtpbzZ;oSmUNnS0 zWStrZz5HVETe9Tn$GEm^az$r6U#?!Vmmr)c`+mW|5Jvn8*c! z2u=UF0Q>TJyOzlD_f6@R7IRXJMtdzrt|@+!xZ{CyV~ zS^Y*#nOu~?0=5$C{M|llL65RmAf+!$R{K)!-_kn{6EX4nt*;E^`O@OG*RFK9-mDd{r>rd4+ZEWK3<3N0Kwb?nlS@nZWSXF={7}ERT&@$D3u=RAF7AWWP{@{oA_9)r~G7Nu51*(*1`7UO4mniOB~Io z_U&vc zE*Mh=QWq^r%^GnVUt^cKSw=^iPp^wlPt6h?zam?MwvQpc(C?teOS7r8``SRN4>+!& zaKi1s{Q7l!V|i_xpXPi*BK3USykiHC4!QW6yCJvjq5Zt2W;vJ8I*uy1QGcytiF)X& z_Ikc~hlBS4^x6K2ERsOt#Y}xv>xN=FW1rpf?+AQoHC6RHSP>fgBI7Ph z>mK4KYCVMBBQ-ETeP3$KbIbjsRE|97q9=XXXgwi9v&u=AdS2jB7h!`b*_X#Jz>eX< ze)9-S8`{lK>gSXa>qgHHcz``_PD{0&MY}2^D@W8tt(XXl>f$16X5C6YU2fg3s^<56 z+b;eHzeVqp*K>=V^5a1O?z*1qd0Xk|39Fx#=l2xzz-bQ+Ny~QsMw+?Yk?PI4rrp@S zpWp*Rv}04VPNm%Ur7EWB@$|~aEqsUw7IflNaeQiOJ)ajyHo422JR1~%kv0HxU*c5B znz4yfQ@QKv{-3i_i>y@e)?V@d5@nj&8;sV8v`&x!Px(8>md}v(WCm%7mG3J$1Nu03 z$ChEV{82Q4SNvHaoPY+cUsmagab*b0QZC~c{ z<9;>0GISeJHs5IN2Fb$l;cRk&az`+WFnK8+`cgUcW#Q15twUe74Si`I`qGen$;<;~ z*+y4veP&vfBZt2v9O@2!ALQ>^{(jEih#6Im{Fzmb4xV4&?=zkqd3kxx9G5#UZv;=n z{^bsP8~!rVlQ(L3%J8?*IqVr5f&UnPFYq^yzdQKb%HIzDj_~IgIqaX4x17Ng*GTu^ zi#(0sFE`7oTvpk^lQP{Qs$T?!L-R%ZqDk)#>w8&1v8QAL z&bbe#^e?pe38Ez3EaDD5YZ|{%tAhWrnG7=*z98m8PjqEz*GvczkDCK{@?_86JWF?Y zZok8^q(j+WpKkw3m7@z|2)--JOL?qD-msu%rn{<@E`Yd`gsy6Rm{P4R&H4y?_$g;? zSG6*}bX6;J(n{)-NY#~->24)-eW@JS#Vw8vl}vZk61%W^}jn8?~15 zKcm|bpvU6hDH>P+^$*aK0o{D#(zt6@FIwqJm%{YFsH=y*5s!rrs+BIO6(gPTI+jEqXdyPJ8Pgl#u;qqlkUjBA3krg4e9@n_sL@iaGHGf{4^ zC!>YjMOHnPkz8qyk*&#Kl< zjF&(K){n4(*rdY_n#6kRUk{$m^c|kqj&A|#R)ENBTi7VGLh|Jz0SEuFtFpBEQ*3X7 z*&p&7wYKs*v}*w7fqqAG;+)NjzWQJgCR#@ob@}fV6qzdfexcX{MSMwoMxQtc`fw|!yT^4LynyvfwaV8i=z&4}5)UA2 ztcJiKR?Y|2h>fwFb*;e6dJz!4^+JC^b}hmGJ<`eMb9`lfgY5oGe*N2N-}}RFUwQDk zS4~e5dYZY&ZKfxpwDrAfrcv*F<|j`}k-k6Rhxk6xjgOjdnBpts`?XZ+dzZ**ha9ex z%M!0(9Cf|5blh%N$C7ae(D{NyUA&O(HeYhKg1?iw33j}&>4SynJJ~L`gd)6|EulsH zD7(41Uq-CVyE1eroYiq>ZG#jNyoinZpA;mo3@Q@MjQ7hp4ny15Nr8xpKareO$V)Ji ztS`1GO&qKM?}fYvtrrxY39^-+tPj;@#`~xrwJ!dXbk8jj3Z*B?@9SHvkWJ4^Z-;DO zUQky(W%Fx&`*qz%s5ySVq^Q)Ol1?6OX6agktuiDGM!XN_jpU`OH`U<{y7sD!QbZ1x4#O)!c?+1U8ajG&Cz zlk+ro8B-aZxxq{iseWmAUtc;yEu+QY|4o0AXYSQ*{tA(+ z6>JlV2S&6VzN$u~kAR`*fzhIP~s~~uBTW_Hpv8$iuf1K8v`N2W>gmI4; zY}9(nj4xsJ@R%>$rP$^wPdESYAsmSNdTK{Fns0c8z6CcOeEXL_ak8h+w=O}484!|> zf&wHB@N!nVl+ba2YF4#ZRwW_alJBYUCopHW+(luU2}oO^h*;Og2PsC>v&{Gq4;Wxt zPb^)6g)xh!Um!qas=ykb3l{u=yMMG3Z6Zv35E-PuIh0(CK~pKQe2q?YsM{Gpz6=6F z45enBWrDoI7jaWH?)x{oTbH@o`l&tnZUk3AjH_$O;KD-V1x%4Iv)W_rK=v3<$?ZY4 z_0G8C6Lpr?@(N1iYZHZ%%P~@~L@mneUJ#M_t;O#f>UmjhKLgT{L_coo;?ZK=`ZfbB z%i(;Bw>r%wwxTTza0!h3DYCDjVn|uqvjWIg$L`cr&*sYZ#>xCC_#m-$55sM@rxn6d_Wcs9q@Z<<1B#(C_-is zy|E#u>0(yXq)qP$JF44sS{PFN;)bnC_eiV#p;y9TF^_YY>z{-4#PgAkh9@Y{`xX~w z7ORqXXy$Y~8Wx2qxWO(c{Aj3uGVS7Q$Z|zwpI~9g;i%i(v+1a`zv-l-ndV1!&Iq}$ z=$%mi-0k3+MEbPjMjnkBU7Sr4jcyMm#&D4#*Gz}pCl0U7T+|+#>Ar%@QKaPPl;)$( z(9P~EPP|U?n2UBdAD!KtzGCy7kU`X&lP+Czpq@|H<8I%OT4T+f7wrzYbmLiX^0B%>So@c89yt@d(pw>cV|bMcU}?Qyf8G$`Y*Ygi#0!iUj>M{T}^-1`I$At zwT2hKAv5L+pRhJTfQr}yyqN*F$N>?f?eXEv)Fw}3 zWo_M7wg6_ZKULh1NSS21pr$bi!od(2@yAL{CZ?EQx1K?x9>wD%=kHbKYUx=I#qS9t2!crq^o+NB-%q(J~ay6r`UPI)x?sg_*6uOx^k?YM9qlm&e_WHK`_N%6&%qDMuAy%iK@na4)ae z0_8-~taF?DiuUlm$ptC*AyLf!f_zIZ@zM~*k{jGK)ho=-XS!0s8xnKjAj?yi{% zQResE4Iu=P^>xmN2jsy7dZ3bJcDDhq!M#L+J}g(FQ!c$#G{~BZG_-T0&uUZ2UX`R$ z8ax!wjCUgqWMZ$EvTH{^Cxvmk$~1@<2M7&Hcjr>MzQIogQX&m*vLe>Xjyl zmJd{jA2M@?(BtUc0?8eRf3$<4*A z%)N3+NVTydIkM-ibB8{1$Vh&uE^MfwUtte>{jg!b0TE?~{jGt*3|kyBCJh_>dENHl z?^F9!cjhj))yN~KG$K4SF1n_x<&_z&hp~&!$?$r_;mq!N>RIW3l7J*?=CjDTSI+(- zRxZ1KAm)RY&U^DAM|ErW@2TFr(Q{>LttWn7+pJMU6~3x@<7h|hA&_sKK#7ggorVyz zV10>=gOIN;8Lwd<;8QN(y|~FoYV$!kDRXP2$eGKu zmKA2zOiC&JxrM(~{I%MBGixGd zWFh_V*v{BP28Ap_4ot18vHh_XKaX0MXxkz>f!7fq-!KC#X6^sl4HBn4(cbb2H{pcT zvT+;6H>_4g|5erZq^3J1fTEbBa^hp;3lJ~&tG;7y<2;m;`b*3`9I#})w)WND((o7n zuv}wWvqI<*&`$4|+JZ; z0qf$U%4$Z$3d}e7>=SjSiJ$5Pja>IVrSjfSumo;>b1$fu@X_;dZQwQ1`~J>QWkaC^Hn6yv`Zo!&>d@a(7Fo@PWOO zH||1>c;ftTI+iK#SCxHO)|x=^%LCq|)rAU%6m6#{+B$gxV=zw>INOD8Fx61JLMtKG zzZ{V{7&w1c^r5yA?Q^vKN=Mf(r95Hkkf8}>zpBN46}d%)7m@;*hDuf7xq$-LP+<8& z6<~-M5_44UKM&;kBZJ+DT2pi%M5Jqd121XoSLneF&8pt5VtDGOm*Br(C6-(9vqRB` zlc}2{gQ}7-){$H-2Gy)a#PV@l_q?izQq+Rz$};N;sjjUf+4~`7A~C-Qg|UkK!saW9 zGAf6`gEvJq{#5^z{S%nj8@saBY7fYESOJxqZ_l%>+*o7OMjs_BUkYU>U9HjqcyBYt z&%^6hsprCb7P%B14$bO zlxhlS{}J2(J;r?(Lsl@q$G!pc`*x|d9A?TQOf?0|t;X?McmNC{#x>H>@%4XzgjuJ5!}(UVCL?8Ya$ZS zoF`MOzNfMm5OoWz{{_%GvXdj}gq5S1yDvG?tcD-NE-oiEU_8)D*+}NAGCNVlLJ1RV zw0?(F)+>$(Xnq~4JKxSXS|zqbSj4HXUi$SH-LK#LMiishFkwJAA?to5ZsPd>J&9Lo z{3z>#-SWa7xNGQ(k^ z+xj*Q)m!heGfF%kWDJC@Tr#Q$l`b>T&n?-0`c*%JK&9qy0)M2RSLuEhT7Oh#W7W?J z)x&_b7HJ=mE>=oEel2@sx+t-aS|rK#sRI>g})m2@5UQ(^)ERVr1?q%~(Z&eQK zVqT9JhS;YkZ{}B1Mxxwdw62oZCh@i9h_OVdtM%Vevj)+>z{&wSlWS8i&NYHK zTEuu}wNK@^zaU2fUHYx}u>lO$$*e9R)oVTX-;(Nqi*QDGg#Ml8Z#->Y#ov(raK2Y4 z_o@C5@;I@+B#&SvZ~%SYhp9xU_6=ws9+1RYcd5sELA~WCmy+PM_F%aNr07~Am*sZi z(o!x_U5AAJ;8}%J6j(gRL}br{gdz=%LY(byNgmuxg$n;MI2IPC&OEqKbycSH5$iv) z)mbl1lO$z~s$U`G(VVT%_93=3(Zn6?uP2&(j`(e66M@8-k#yX|oR5XU=tFY{K`ssbMQI$sMs!^Lp-_Og6_7 zQYU8cAH{GG?oYgwgMFk919;*kXJsPoE#KMr5>hu{&h$MkEh5JQN$+7^H_tijW)HNR zpv&WURZhuCZ$FQnGy2`mZ?U(L8y^VZ^Y)9ZPR9L9<+jHTd~F;>a2dJwacBGOpBRg~ zf0Ufb4IU)(+`?pC>9Fe)#LH;t1~CoK@E=qM!+2fMVpgU5T>g8?0y!YR5sU6bI$%2K zy4$RDXO3hkQ~!!iqGS%y2s#6<8o3!Lfdb46)cwGKIZa&C#M=YzEcz4F;;a1;_jz}s z>8p#jFn6`H@0UenFOdfiRVkg|txT7MwVZ=Pj<@<8mzYqOwYo@L6BgnJ`+#?A64(i6-J zxV#)$z&#-~Xe?S;Tnom|!KeRo_(aE7_b2^az$DngM7ia*W_(eNXJSMg{d`02N6M?d ze)iXHPkyew;OE*KKEL^YBR}*-`FhO=rnriE5^5p*Qk>7Pc$~TBEbmzK9%r;}fmvCI z%g{FVId`lviNUn@&K^F!fuID0l$s|l@S?&PDc{vWwIxQHDnkNP(j zH{zX01Sv7T&h*M^S|V#FpXHQqj)}n8t7d5W1UbkzabP;f+d|E^3W&f7meA_ZHR+c% z88xTX2$1Y8t;7eFSz{Hp3VJmIiUR31ED;sebs=ujEHTtM`XTYiRlM_s^@!QN4hKvv zKIN9$D7NuRZk4dcwY9ebTjh@Dgws@Z)ZJp+-Egx|TCJ_d)-S+&BlnvyWgmMNv_E3k za6qmA$;pjecfZ&i$&DEQf_fXg$F%C%FkNDf6kbGooMves5ZlZw4c}-tY~wf(kuMMy zGKiox7oI8-kAN-4mfwItj!V`A`&R7yMPxI~Vt68#h64YDj-+c%DGT)OG>1XS5IQ8~>C%AfTj?@D?Sciby| z_=WC6yK&D?qz{ah>VZVhge8f?{}*xp0v=^`?eXIY8DNyaj2bm)s!>Oc5;ZodCM2~1 zLO?~F7&5U1L0hy_N|o9f@Dc)c5@2|p$~m>I^kVJhoZ8Fj@w6844nzrFi`t%IZL4iP z?WU0yt;Kj_exL7p-^m5(<^O-4-}BQa%=^CkzW3T|t-Wr$C_6X0$gGe}g07)0HqzSM zr#5f;y*2qnQTppPOHJ+V*8ictr`OW&rb54cep9`V^veBYcmxA98AGjsEaY zqWy0MwFP8^rXeeWFn+*Jqu#+rqu#+jqu#3ycn3R-N(UQ^dS~~KdS|zfdS`c!dS^F} zdT%-4ef9zGa}IcqAMide|5E0a*zkUs0&se++& zZXHxal~eT$p{(~`PtmKa3Znk|$$Cd;Q02eI^y)vJH2ip?9=-7;f1aa?t@=N#mQ>n>C!)W@@2|L~D;Ir+J(fTh7 z_^j({qbx~p+%O(1Yb(rdI?n2beO!P*L$`387hnKg7lR_dfWJ;f`W7qds)Py48~Qpb zaDbqjQF{GbI|>sn1Za^KmrqXzpXhPsmFi*mt`a;n37(tGj~9hcr+eI&lUvNJ1=i(+ zS2En?SX;Z%5!kfz1dxR;2l`U$`N;?G`6NP0CW*FX-Tur%U*DejX;HczmB$>Wi@4?B z_u&Q__0?}+1<^r{Zvjj-GbfFxUnfZx&ouN^g8$80v}z5%en^S^RPa&p%H4FN%NBhC zTW#N0y3Z^ARx>c2oj8ww@n6bpDTndar*F0JkNfgBRB8BL(NlIp%htm7GI=9`wK=Tx z%lv9D6i?P#A0}h6iqE;ZVcka;VFiG}%G}kZ9m (^PAyf81jF?Y?)r>gKc+={<>3 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 z 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 z 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 z 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%0Ase0)QiMpU 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

    ;N-w}=lKx+j7!3!*L&fnlMd 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

    +6_kQD>i8o$6B)C5$yhz!6 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

    *s<%of~|B{j{cBYB_ALO1EzN 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

    )tWGp#J5>s`-C^ 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%0A6OOI-y@9|X2yL`zvZnH$DIkmjcwvp2Et|^y` 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 z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l 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%0DWyX=6|Lb!{WAhs?(J zSkc~}=p!{YdtWvp9te#_Sp^4KcbkhqR-7Rexp;z zhR7`7zUzh0qV_yHa86~H&*`|oC%WV{MB{63k*=Qz??b^)Pdkbmq11t6CX8!G z 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 Date: Fri, 24 Nov 2023 14:25:56 +0000 Subject: [PATCH 242/347] Fix jsonParseReset() to properly clear the JsonParse.aBlob element. FossilOrigin-Name: ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 27 ++++++++++++++++----------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index f916388b89..90ba0620cd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sprecompiled\sbinaries\sfrom\sthe\ssource\stree. -D 2023-11-24T14:03:20.819 +C Fix\sjsonParseReset()\sto\sproperly\sclear\sthe\sJsonParse.aBlob\selement. +D 2023-11-24T14:25:56.679 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c f8cfdf1d024a7b2c5ad3eae15903a7a83a9b2c8ab61716d219a57600544b712c +F src/json.c c6b23115b4561008363346a209b5c8a3538d967b35c63685b2e07dc7de1f1e31 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 a838ebcbbd9f554fd84a1d3176fb572faaef7d0ec0675f1a5bf9430865dafae0 4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 -R 0a66dd6facaa7d203b266fe2d20488a5 +P 7dbc2f496d7a362460bb4c262ecafe5f30e35a8744861163d12c996365c2142f +R 1676f3ca8710b44ac390888e709fad02 U drh -Z 62c86eba16d8545370a182d5ae7c09b8 +Z 41b7b01f3bc28811bfbbf765da2aea03 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e2ff938fb7..8430a0984f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7dbc2f496d7a362460bb4c262ecafe5f30e35a8744861163d12c996365c2142f \ No newline at end of file +ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 54e221392e..0eacca3089 100644 --- a/src/json.c +++ b/src/json.c @@ -865,6 +865,12 @@ static void jsonParseReset(JsonParse *pParse){ sqlite3RCStrUnref(pParse->zAlt); pParse->zAlt = 0; } + if( pParse->nBlobAlloc ){ + sqlite3_free(pParse->aBlob); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; + } } /* @@ -4311,7 +4317,7 @@ static void jsonRemoveFromBlob( if( zPath==0 ) goto jsonRemoveFromBlob_patherror; if( zPath[0]!='$' ) goto jsonRemoveFromBlob_patherror; if( zPath[1]==0 ){ - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); return; /* return NULL if $ is removed */ } px.eEdit = JEDIT_DEL; @@ -4328,12 +4334,12 @@ static void jsonRemoveFromBlob( jsonStringInit(&s, ctx); jsonXlateBlobToText(&px, 0, &s); jsonReturnString(&s); - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); } return; jsonRemoveFromBlob_patherror: - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); jsonPathSyntaxError(zPath, ctx); return; } @@ -4445,14 +4451,14 @@ static void jsonInsertIntoBlob( break; } if( zPath[1]==0 ){ - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); return; /* return NULL if $ is removed */ } px.eEdit = eEdit; px.nIns = ax.nBlob; px.aIns = ax.aBlob; rc = jsonLookupBlobStep(&px, 0, zPath+1, 0); - if( ax.nBlobAlloc ) sqlite3_free(ax.aBlob); + jsonParseReset(&ax); if( rc==JSON_BLOB_NOTFOUND ) continue; if( JSON_BLOB_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; } @@ -4465,12 +4471,12 @@ static void jsonInsertIntoBlob( jsonStringInit(&s, ctx); jsonXlateBlobToText(&px, 0, &s); jsonReturnString(&s); - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); } return; jsonInsertIntoBlob_patherror: - if( px.nBlobAlloc ) sqlite3_free(px.aBlob); + jsonParseReset(&px); if( rc==JSON_BLOB_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ @@ -4648,13 +4654,12 @@ static void jsonbFunc( x.nJson = nJson; if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); - sqlite3_free(pParse->aBlob); }else{ sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; } - pParse->aBlob = 0; - pParse->nBlob = 0; - pParse->nBlobAlloc = 0; jsonParseReset(pParse); } } From f5232b3a97e36539ff38702eea5d6494bc3a6350 Mon Sep 17 00:00:00 2001 From: larrybr Date: Fri, 24 Nov 2023 15:58:14 +0000 Subject: [PATCH 243/347] Get all CLI print calls which went to stdout in 3.44.0 to continue going to stdout. FossilOrigin-Name: e9951ede184ce07cf725152723d795f299922460715ab76225cd3071bf0f18ee --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index e9e178fbf2..74ef0bb128 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sthe\sprecompiled\sbinary\sfrom\sthe\ssource\stree. -D 2023-11-24T14:01:56.311 +C Get\sall\sCLI\sprint\scalls\swhich\swent\sto\sstdout\sin\s3.44.0\sto\scontinue\sgoing\sto\sstdout. +D 2023-11-24T15:58:14.760 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -729,7 +729,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e -F src/shell.c.in a492f9209fe62ce3d1048802d59cd6e38e8444f88573fe1aebaadcd22e04156b +F src/shell.c.in 7bb83293775e1a5586d65212997442bc7acc70a2f1b781745da64ec3c2e4ea97 F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2143,8 +2143,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 ce766ed51f772a960d0b7a52c113b55c7ae90ef35050496d8e2a77547eab1a4d -R 58dcc92da6c87abfc48a164925361d3d -U drh -Z 249598a61f7a5bf493f3b02aeff2946e +P 4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 +R c9c02224e257d585a65f6ab0d6ca2ee5 +U larrybr +Z dc01e7a4eb28e73d82b5d93b184ed26c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6e79ab95a0..1cf6dbe0cc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 \ No newline at end of file +e9951ede184ce07cf725152723d795f299922460715ab76225cd3071bf0f18ee \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 0ffd2b8d78..4a4a0e1fef 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -439,7 +439,7 @@ static void endTimer(void){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); - oputf("Run Time: real %.3f user %f sys %f\n", + sputf(stdout, "Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); @@ -11860,14 +11860,14 @@ static void printBold(const char *zText){ FOREGROUND_RED|FOREGROUND_INTENSITY ); #endif - oputz(zText); + sputz(stdout, zText); #if !SQLITE_OS_WINRT SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); #endif } #else static void printBold(const char *zText){ - oputf("\033[1m%s\033[0m", zText); + sputf(stdout, "\033[1m%s\033[0m", zText); } #endif @@ -12334,8 +12334,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ }else if( cli_strcmp(z,"-version")==0 ){ - oputf("%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(), - 8*(int)sizeof(char*)); + sputf(stdout, "%s %s (%d-bit)\n", + sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); return 0; }else if( cli_strcmp(z,"-interactive")==0 ){ /* already handled */ @@ -12467,13 +12467,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #else # define SHELL_CIO_CHAR_SET "" #endif - oputf("SQLite version %s %.19s%s\n" /*extra-version-info*/ + sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/ "Enter \".help\" for usage hints.\n", sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); if( warnInmemoryDb ){ - oputz("Connected to a "); + sputz(stdout, "Connected to a "); printBold("transient in-memory database"); - oputz(".\nUse \".open FILENAME\" to reopen on a" + sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" " persistent database.\n"); } zHistory = getenv("SQLITE_HISTORY"); From 88a61ff8ba35fedf06e6bf97db08c8638c3569d1 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 24 Nov 2023 18:33:40 +0000 Subject: [PATCH 244/347] Allow a pattern to filter test scripts to be appended to testrunner.tcl "mdevtest", "sdevtest" and "release" commands. e.g. "tclsh test/testrunner.tcl sdevtest fts5%". FossilOrigin-Name: f8ea0b58b37f4052ded448e595d6d2992988a33b8ecfe58d68f20532f8cb5a37 --- doc/testrunner.md | 22 +++++++++++++++++----- manifest | 16 ++++++++-------- manifest.uuid | 2 +- test/testrunner.tcl | 41 +++++++++++++++++++++++++---------------- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/doc/testrunner.md b/doc/testrunner.md index d828fd76d8..51aa1644ef 100644 --- a/doc/testrunner.md +++ b/doc/testrunner.md @@ -114,6 +114,12 @@ a specified pattern (e.g. all tests that start with "fts5"), either of: ./testfixture $TESTDIR/testrunner.tcl 'fts5*' ``` +Strictly speaking, for a test to be run the pattern must match the script +filename, not including the directory, using the rules of Tcl's +\[string match\] command. Except that before the matching is done, any "%" +characters specified as part of the pattern are transformed to "\*". + + To run "all" tests (full + permutations): ``` @@ -141,6 +147,7 @@ Or, if the failure occured as part of a permutation: TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT? + # 3. Source Code Tests The commands described in this section invoke the C compiler to build @@ -201,6 +208,16 @@ of the specific tests run. tclsh $TESTDIR/testrunner.tcl release ``` +As with source code tests, one or more patterns +may be appended to any of the above commands (mdevtest, sdevtest or release). +In that case only Tcl tests (no fuzz or other tests) that match the specified +pattern are run. For example, to run the just the Tcl rtree tests in all +builds and configurations supported by "release": + +``` + tclsh $TESTDIR/testrunner.tcl release rtree% +``` + ## Running ZipVFS Tests testrunner.tcl can build a zipvfs-enabled testfixture and use it to run @@ -277,8 +294,3 @@ testrunner.log and testrunner.db files: - - - - - diff --git a/manifest b/manifest index 74ef0bb128..f0b7fb1dfb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\sall\sCLI\sprint\scalls\swhich\swent\sto\sstdout\sin\s3.44.0\sto\scontinue\sgoing\sto\sstdout. -D 2023-11-24T15:58:14.760 +C Allow\sa\spattern\sto\sfilter\stest\sscripts\sto\sbe\sappended\sto\stestrunner.tcl\s"mdevtest",\s"sdevtest"\sand\s"release"\scommands.\se.g.\s"tclsh\stest/testrunner.tcl\ssdevtest\sfts5%". +D 2023-11-24T18:33:40.234 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -41,7 +41,7 @@ F doc/compile-for-windows.md 50b27d77be96195c66031a3181cb8684ed822327ea834e07f9c F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/lemon.html 44a53a1d2b42d7751f7b2f478efb23c978e258d794bfd172442307a755b9fa44 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 -F doc/testrunner.md 2434864be2219d4f0b6ffc99d0a2172d531c4ca4345340776f67ad4edd90dc90 +F doc/testrunner.md 727ef3877f5cea152a56f04566d0e095e0761918e9d771fc914db233a91bdd2e F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56 F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a @@ -1653,7 +1653,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 8a6721213bce1cfd3b33e1588cc6431143d96b98819206bf91f5a205fbb150d4 +F test/testrunner.tcl 3ce1f7d0541bcb60f23e843fcde6c66f76acd907bbfa0192620f40ad78a4c9c3 F test/testrunner_data.tcl e4d5017290a6d5c11785e36cc94c67d8bb950c8cdc2dbe4c1db2a3a583812560 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2143,8 +2143,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 4ff103d294b79cf7734e87e94e9d88c0e9f0b087cbb352e6da2f0a3a6b268f46 -R c9c02224e257d585a65f6ab0d6ca2ee5 -U larrybr -Z dc01e7a4eb28e73d82b5d93b184ed26c +P e9951ede184ce07cf725152723d795f299922460715ab76225cd3071bf0f18ee +R b6b078e60149e99b226063ed1104d74a +U dan +Z 8e4057a95f8c06424678eb8234c037ec # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1cf6dbe0cc..9b275cbf88 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e9951ede184ce07cf725152723d795f299922460715ab76225cd3071bf0f18ee \ No newline at end of file +f8ea0b58b37f4052ded448e595d6d2992988a33b8ecfe58d68f20532f8cb5a37 \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index 025f2ecdb4..e85aafadf2 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -752,6 +752,20 @@ proc add_zipvfs_jobs {} { set ::env(SQLITE_TEST_DIR) $::testdir } +# Used to add jobs for "mdevtest" and "sdevtest". +# +proc add_devtest_jobs {lBld patternlist} { + global TRG + + foreach b $lBld { + set bld [add_build_job $b $TRG(testfixture)] + add_tcl_jobs $bld veryquick $patternlist + if {$patternlist==""} { + add_fuzztest_jobs $b + } + } +} + proc add_jobs_from_cmdline {patternlist} { global TRG @@ -775,33 +789,28 @@ proc add_jobs_from_cmdline {patternlist} { } mdevtest { - foreach b [list All-O0 All-Debug] { - set bld [add_build_job $b $TRG(testfixture)] - add_tcl_jobs $bld veryquick "" - add_fuzztest_jobs $b - } + add_devtest_jobs {All-O0 All-Debug} [lrange $patternlist 1 end] } sdevtest { - foreach b [list All-Sanitize All-Debug] { - set bld [add_build_job $b $TRG(testfixture)] - add_tcl_jobs $bld veryquick "" - add_fuzztest_jobs $b - } + add_devtest_jobs {All-Sanitize All-Debug} [lrange $patternlist 1 end] } release { + set patternlist [lrange $patternlist 1 end] foreach b [trd_builds $TRG(platform)] { set bld [add_build_job $b $TRG(testfixture)] foreach c [trd_configs $TRG(platform) $b] { - add_tcl_jobs $bld $c "" + add_tcl_jobs $bld $c $patternlist } - foreach e [trd_extras $TRG(platform) $b] { - if {$e=="fuzztest"} { - add_fuzztest_jobs $b - } else { - add_make_job $bld $e + if {$patternlist==""} { + foreach e [trd_extras $TRG(platform) $b] { + if {$e=="fuzztest"} { + add_fuzztest_jobs $b + } else { + add_make_job $bld $e + } } } } From abbdbdfc1f1ea527233c4fdf711c97dc51d9a4ff Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 24 Nov 2023 18:44:00 +0000 Subject: [PATCH 245/347] Incremental progress toward getting json_each() and json_tree() to work directly off of a JSONB blob. FossilOrigin-Name: f8cab41b3bc65af6ff34b481db693d640ea025d09463d50b8e56d855e2abc913 --- manifest | 15 +- manifest.uuid | 2 +- src/json.c | 483 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 356 insertions(+), 144 deletions(-) diff --git a/manifest b/manifest index 90ba0620cd..49e3c13317 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sjsonParseReset()\sto\sproperly\sclear\sthe\sJsonParse.aBlob\selement. -D 2023-11-24T14:25:56.679 +C Incremental\sprogress\stoward\sgetting\sjson_each()\sand\sjson_tree()\sto\swork\ndirectly\soff\sof\sa\sJSONB\sblob. +D 2023-11-24T18:44:00.124 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c c6b23115b4561008363346a209b5c8a3538d967b35c63685b2e07dc7de1f1e31 +F src/json.c 1b9cc57728bc3a420bae5f4ae8233dcd2c3a337f00a2467346f7b788aedf1ba0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7dbc2f496d7a362460bb4c262ecafe5f30e35a8744861163d12c996365c2142f -R 1676f3ca8710b44ac390888e709fad02 +P ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 +R b8a3524be145a961ee4bfad02645fa25 +T *branch * jsonb-tree +T *sym-jsonb-tree * +T -sym-jsonb * U drh -Z 41b7b01f3bc28811bfbbf765da2aea03 +Z b88ae8be743d3b0432d41e7f9773a471 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8430a0984f..3afbf02fc8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 \ No newline at end of file +f8cab41b3bc65af6ff34b481db693d640ea025d09463d50b8e56d855e2abc913 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0eacca3089..fdd2a21d59 100644 --- a/src/json.c +++ b/src/json.c @@ -133,6 +133,14 @@ #define JSONB_ARRAY 11 /* An array */ #define JSONB_OBJECT 12 /* An object */ +/* Human-readalbe names for the JSONB values: +*/ +static const char * const jsonbType[] = { + "null", "true", "false", "integer", "integer", + "real", "real", "text", "text", "text", + "text", "array", "object" +}; + /* ** Growing our own isspace() routine this way is twice as fast as ** the library isspace() function, resulting in a 7% overall performance @@ -4070,7 +4078,8 @@ static void jsonReturnTextJsonFromBlob( static void jsonReturnFromBlob( JsonParse *pParse, /* Complete JSON parse tree */ u32 i, /* Index of the node */ - sqlite3_context *pCtx /* Return value for this function */ + sqlite3_context *pCtx, /* Return value for this function */ + int textOnly /* return text JSON. Disregard user-data */ ){ u32 n, sz; int rc; @@ -4217,7 +4226,7 @@ static void jsonReturnFromBlob( } case JSONB_ARRAY: case JSONB_OBJECT: { - int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); + int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); if( flags & JSON_BLOB ){ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); }else{ @@ -4280,7 +4289,7 @@ static void jsonExtractFromBlob( return; } if( idb = db; } return rc; } @@ -5628,12 +5655,14 @@ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ /* constructor for a JsonEachCursor object for json_each(). */ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + JsonEachConnection *pVtab = (JsonEachConnection*)p; JsonEachCursor *pCur; UNUSED_PARAMETER(p); pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); + pCur->db = pVtab->db; *ppCursor = &pCur->base; return SQLITE_OK; } @@ -5653,8 +5682,12 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ static void jsonEachCursorReset(JsonEachCursor *p){ sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); + sqlite3DbFree(p->db, p->aParent); p->iRowid = 0; p->i = 0; + p->aParent = 0; + p->nParent = 0; + p->nParentAlloc = 0; p->iEnd = 0; p->eType = 0; p->zJson = 0; @@ -5676,45 +5709,106 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){ return p->i >= p->iEnd; } +/* +** If the cursor is currently pointing at the label of a object entry, +** then return the index of the value. For all other cases, return the +** current pointer position, which is the value. +*/ +static int jsonSkipLabel(JsonEachCursor *p){ + if( p->eType==JSONB_OBJECT ){ + u32 sz = 0; + u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); + return p->i + n + sz; + }else{ + return p->i; + } +} + /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; - if( p->bRecursive ){ - if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; - p->i++; - p->iRowid++; - if( p->iiEnd ){ - u32 iUp = p->sParse.aUp[p->i]; - JsonNode *pUp = &p->sParse.aNode[iUp]; - p->eType = pUp->eType; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==0 || pUp->eU==3 ); - testcase( pUp->eU==3 ); - JSON_VVA( pUp->eU = 3 ); - if( iUp==p->i-1 ){ - pUp->u.iKey = 0; - }else{ - pUp->u.iKey++; + if( p->sParse.aNode ){ + /* LEGACY */ + if( p->bRecursive ){ + if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; + p->i++; + p->iRowid++; + if( p->iiEnd ){ + u32 iUp = p->sParse.aUp[p->i]; + JsonNode *pUp = &p->sParse.aNode[iUp]; + p->eType = pUp->eType; + if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==0 || pUp->eU==3 ); + testcase( pUp->eU==3 ); + JSON_VVA( pUp->eU = 3 ); + if( iUp==p->i-1 ){ + pUp->u.iKey = 0; + }else{ + pUp->u.iKey++; + } + } + } + }else{ + switch( p->eType ){ + case JSON_ARRAY: { + p->i += jsonNodeSize(&p->sParse.aNode[p->i]); + p->iRowid++; + break; + } + case JSON_OBJECT: { + p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); + p->iRowid++; + break; + } + default: { + p->i = p->iEnd; + break; } } } - }else{ - switch( p->eType ){ - case JSON_ARRAY: { - p->i += jsonNodeSize(&p->sParse.aNode[p->i]); - p->iRowid++; - break; + return SQLITE_OK; + }else if( p->bRecursive ){ + u8 x; + u8 levelChange = 0; + u32 n, sz = 0; + u32 i = jsonSkipLabel(p); + x = p->sParse.aBlob[i] & 0x0f; + n = jsonbPayloadSize(&p->sParse, p->i, &sz); + if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ + JsonParent *pParent; + if( p->nParent>=p->nParentAlloc ){ + JsonParent *pNew; + u64 nNew; + nNew = p->nParentAlloc*2 + 3; + pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew); + if( pNew==0 ) return SQLITE_NOMEM; + p->nParentAlloc = (u32)nNew; + p->aParent = pNew; + levelChange = 1; } - case JSON_OBJECT: { - p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); - p->iRowid++; - break; - } - default: { - p->i = p->iEnd; - break; + pParent = &p->aParent[p->nParent++]; + pParent->iHead = p->i; + pParent->iEnd = p->i + n + sz; + pParent->iKey = 0; + }else{ + p->i = i + n + sz; + } + if( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ + p->nParent--; + levelChange = 1; + } + if( levelChange ){ + if( p->nParent>0 ){ + p->eType = p->sParse.aBlob[p->aParent[p->nParent-1].iHead] & 0x0f; + }else{ + p->eType = 0; } } + }else{ + u32 n, sz = 0; + u32 i = jsonSkipLabel(p); + n = jsonbPayloadSize(&p->sParse, i, &sz); + p->i = i + n + sz; } return SQLITE_OK; } @@ -5722,7 +5816,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ /* Append an object label to the JSON Path being constructed ** in pStr. */ -static void jsonAppendObjectPathElement( +static void jsonAppendObjectPathElementOfNode( JsonString *pStr, JsonNode *pNode ){ @@ -5763,18 +5857,23 @@ static void jsonEachComputePath( jsonAppendChar(pStr, '$'); return; } - iUp = p->sParse.aUp[i]; - jsonEachComputePath(p, pStr, iUp); - pNode = &p->sParse.aNode[i]; - pUp = &p->sParse.aNode[iUp]; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); - testcase( pUp->eU==0 ); - jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); - }else{ - assert( pUp->eType==JSON_OBJECT ); - if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; - jsonAppendObjectPathElement(pStr, pNode); + if( p->sParse.aNode ){ + /* LEGACY */ + assert( p->sParse.aUp ); + iUp = p->sParse.aUp[i]; + jsonEachComputePath(p, pStr, iUp); + pNode = &p->sParse.aNode[i]; + pUp = &p->sParse.aNode[iUp]; + if( pUp->eType==JSON_ARRAY ){ + assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); + testcase( pUp->eU==0 ); + jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); + }else{ + assert( pUp->eType==JSON_OBJECT ); + if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; + jsonAppendObjectPathElementOfNode(pStr, pNode); + } + return; } } @@ -5782,102 +5881,200 @@ static void jsonEachComputePath( static int jsonEachColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ - int i /* Which column to return */ + int iColumn /* Which column to return */ ){ JsonEachCursor *p = (JsonEachCursor*)cur; - JsonNode *pThis = &p->sParse.aNode[p->i]; - switch( i ){ - case JEACH_KEY: { - if( p->i==0 ) break; - if( p->eType==JSON_OBJECT ){ + if( p->sParse.aNode!=0 ){ + /* LEGACY */ + JsonNode *pThis = &p->sParse.aNode[p->i]; + switch( iColumn ){ + case JEACH_KEY: { + if( p->i==0 ) break; + if( p->eType==JSON_OBJECT ){ + jsonReturnFromNode(&p->sParse, pThis, ctx, 0); + }else if( p->eType==JSON_ARRAY ){ + u32 iKey; + if( p->bRecursive ){ + if( p->iRowid==0 ) break; + assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); + iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; + }else{ + iKey = p->iRowid; + } + sqlite3_result_int64(ctx, (sqlite3_int64)iKey); + } + break; + } + case JEACH_VALUE: { + if( pThis->jnFlags & JNODE_LABEL ) pThis++; jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - }else if( p->eType==JSON_ARRAY ){ - u32 iKey; - if( p->bRecursive ){ - if( p->iRowid==0 ) break; - assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); - iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; - }else{ - iKey = p->iRowid; - } - sqlite3_result_int64(ctx, (sqlite3_int64)iKey); + break; } - break; - } - case JEACH_VALUE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - break; - } - case JEACH_TYPE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); - break; - } - case JEACH_ATOM: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - if( pThis->eType>=JSON_ARRAY ) break; - jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - break; - } - case JEACH_ID: { - sqlite3_result_int64(ctx, - (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); - break; - } - case JEACH_PARENT: { - if( p->i>p->iBegin && p->bRecursive ){ - sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); + case JEACH_TYPE: { + if( pThis->jnFlags & JNODE_LABEL ) pThis++; + sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); + break; } - break; - } - case JEACH_FULLKEY: { - JsonString x; - jsonStringInit(&x, ctx); - if( p->bRecursive ){ - jsonEachComputePath(p, &x, p->i); - }else{ - if( p->zRoot ){ - jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); - }else{ - jsonAppendChar(&x, '$'); - } - if( p->eType==JSON_ARRAY ){ - jsonPrintf(30, &x, "[%d]", p->iRowid); - }else if( p->eType==JSON_OBJECT ){ - jsonAppendObjectPathElement(&x, pThis); - } + case JEACH_ATOM: { + if( pThis->jnFlags & JNODE_LABEL ) pThis++; + if( pThis->eType>=JSON_ARRAY ) break; + jsonReturnFromNode(&p->sParse, pThis, ctx, 0); + break; } - jsonReturnString(&x); - break; - } - case JEACH_PATH: { - if( p->bRecursive ){ + case JEACH_ID: { + sqlite3_result_int64(ctx, + (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); + break; + } + case JEACH_PARENT: { + if( p->i>p->iBegin && p->bRecursive ){ + sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); + } + break; + } + case JEACH_FULLKEY: { JsonString x; jsonStringInit(&x, ctx); - jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); + if( p->bRecursive ){ + jsonEachComputePath(p, &x, p->i); + }else{ + if( p->zRoot ){ + jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); + }else{ + jsonAppendChar(&x, '$'); + } + if( p->eType==JSON_ARRAY ){ + jsonPrintf(30, &x, "[%d]", p->iRowid); + }else if( p->eType==JSON_OBJECT ){ + jsonAppendObjectPathElementOfNode(&x, pThis); + } + } jsonReturnString(&x); break; } - /* For json_each() path and root are the same so fall through - ** into the root case */ - /* no break */ deliberate_fall_through - } - default: { - const char *zRoot = p->zRoot; - if( zRoot==0 ) zRoot = "$"; - sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); - break; - } - case JEACH_JSON: { - if( p->sParse.isBinary ){ - sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, - SQLITE_STATIC); - }else{ - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + case JEACH_PATH: { + if( p->bRecursive ){ + JsonString x; + jsonStringInit(&x, ctx); + jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); + jsonReturnString(&x); + break; + } + /* For json_each() path and root are the same so fall through + ** into the root case */ + /* no break */ deliberate_fall_through + } + default: { + const char *zRoot = p->zRoot; + if( zRoot==0 ) zRoot = "$"; + sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); + break; + } + case JEACH_JSON: { + if( p->sParse.isBinary ){ + sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, + SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + } + break; } - break; } + }else{ + /* Blob scan */ + switch( iColumn ){ + case JEACH_KEY: { + if( p->nParent==0 ) break; + if( p->eType==JSONB_OBJECT ){ + jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); + }else{ + assert( p->eType==JSONB_ARRAY ); + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); + } + break; + } + case JEACH_VALUE: { + u32 i = jsonSkipLabel(p); + jsonReturnFromBlob(&p->sParse, i, ctx, 1); + break; + } + case JEACH_TYPE: { + u32 i = jsonSkipLabel(p); + u8 eType = eType = p->sParse.aBlob[i] & 0x0f; + sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); + break; + } + case JEACH_ATOM: { + u32 i; + if( p->eType>=JSON_ARRAY ) break; + i = jsonSkipLabel(p); + jsonReturnFromBlob(&p->sParse, i, ctx, 1); + break; + } + case JEACH_ID: { + sqlite3_result_int64(ctx, (sqlite3_int64)p->i); + break; + } + case JEACH_PARENT: { + if( p->nParent>0 ){ + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); + } + break; + } + case JEACH_FULLKEY: { +#if 0 + JsonString x; + jsonStringInit(&x, ctx); + if( p->bRecursive ){ + jsonEachComputePath(p, &x, p->i); + }else{ + if( p->zRoot ){ + jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); + }else{ + jsonAppendChar(&x, '$'); + } + if( p->eType==JSON_ARRAY ){ + jsonPrintf(30, &x, "[%d]", p->iRowid); + }else if( p->eType==JSON_OBJECT ){ + jsonAppendObjectPathElementOfNode(&x, pThis); + } + } + jsonReturnString(&x); +#endif + break; + } + case JEACH_PATH: { +#if 0 + if( p->bRecursive ){ + JsonString x; + jsonStringInit(&x, ctx); + jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); + jsonReturnString(&x); + break; + } + /* For json_each() path and root are the same so fall through + ** into the root case */ + /* no break */ deliberate_fall_through +#endif + break; + } + default: { + const char *zRoot = p->zRoot; + if( zRoot==0 ) zRoot = "$"; + sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); + break; + } + case JEACH_JSON: { + if( p->sParse.isBinary ){ + sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, + SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + } + break; + } + } + } return SQLITE_OK; } @@ -5975,16 +6172,28 @@ static int jsonEachFilter( UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; + memset(&p->sParse, 0, sizeof(p->sParse)); + p->sParse.nJPRef = 1; if( jsonFuncArgMightBeBinary(argv[0]) ){ - z = (const char*)sqlite3_value_blob(argv[0]); - isBinary = 1; + u32 i, n, sz; + p->sParse.nBlob = sqlite3_value_bytes(argv[0]); + p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); + if( p->sParse.aBlob==0 ){ + return SQLITE_NOMEM; + } + i = p->i = 0; + p->iEnd = 0; + p->eType = 0; + p->nParent = 0; + p->sParse.isBinary = 1; + n = jsonbPayloadSize(&p->sParse, i, &sz); + p->iEnd = i+n+sz; + return SQLITE_OK; }else{ z = (const char*)sqlite3_value_text(argv[0]); isBinary = 0; } if( z==0 ) return SQLITE_OK; - memset(&p->sParse, 0, sizeof(p->sParse)); - p->sParse.nJPRef = 1; if( sqlite3ValueIsOfClass(argv[0], sqlite3RCStrUnref) ){ p->sParse.zJson = sqlite3RCStrRef((char*)z); }else{ From 48cca2422eaff80968966c29705222be8b061d72 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 24 Nov 2023 20:14:18 +0000 Subject: [PATCH 246/347] Add the --buildonly and --dryrun options to testrunner.tcl. FossilOrigin-Name: a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e --- doc/testrunner.md | 56 +++++++++++++++++++++++++++++++++--- manifest | 14 ++++----- manifest.uuid | 2 +- test/testrunner.tcl | 69 +++++++++++++++++++++++++++++++++------------ 4 files changed, 111 insertions(+), 30 deletions(-) diff --git a/doc/testrunner.md b/doc/testrunner.md index 51aa1644ef..d420076c4f 100644 --- a/doc/testrunner.md +++ b/doc/testrunner.md @@ -2,6 +2,26 @@ # The testrunner.tcl Script +

    + + # 1. Overview testrunner.tcl is a Tcl script used to run multiple SQLite tests using @@ -44,6 +64,7 @@ Sometimes testrunner.tcl uses the [testfixture] binary that it is run with to run tests (see "Binary Tests" below). Sometimes it builds testfixture and other binaries in specific configurations to test (see "Source Tests"). + # 2. Binary Tests The commands described in this section all run various combinations of the Tcl @@ -61,6 +82,7 @@ these tests is therefore: The following sub-sections describe the various options that can be passed to testrunner.tcl to test binary testfixture builds. + ## 2.1. Organization of Tcl Tests Tcl tests are stored in files that match the pattern *\*.test*. They are @@ -91,6 +113,7 @@ Running **all** tests is to run all tests in the full test set, plus a dozen or so permutations. The specific permutations that are run as part of "all" are defined in file *testrunner_data.tcl*. + ## 2.2. Commands to Run Tests To run the "veryquick" test set, use either of the following: @@ -166,7 +189,8 @@ shell that supports SQLite 3.31.1 or newer via "package require sqlite3". TODO: ./configure + Makefile.msc build systems. -## Commands to Run SQLite Tests + +## 3.1. Commands to Run SQLite Tests The **mdevtest** command is equivalent to running the veryquick tests and the [make fuzztest] target once for each of two --enable-all builds - one @@ -218,7 +242,8 @@ builds and configurations supported by "release": tclsh $TESTDIR/testrunner.tcl release rtree% ``` -## Running ZipVFS Tests + +## 3.2. Running ZipVFS Tests testrunner.tcl can build a zipvfs-enabled testfixture and use it to run tests from the Zipvfs project with the following command: @@ -234,7 +259,8 @@ test both SQLite and Zipvfs with a single command: tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest ``` -## Investigating Source Code Test Failures + +## 3.3. Investigating Source Code Test Failures Investigating a test failure that occurs during source code testing is a two step process: @@ -261,9 +287,31 @@ target to build. This may be used either to run a [make] command test directly, or else to build a testfixture (or testfixture.exe) binary with which to run a Tcl test script, as described above. + +# 4. Extra testrunner.tcl Options +The testrunner.tcl script options in this section may be used with both source +code and binary tests. -# 4. Controlling CPU Core Utilization +The **--buildonly** option instructs testrunner.tcl just to build the binaries +required by a test, not to run any actual tests. For example: + +``` + # Build binaries required by release test. + tclsh $TESTDIR/testrunner.tcl --buildonly release" +``` + +The **--dryrun** option prevents testrunner.tcl from building any binaries +or running any tests. Instead, it just writes the shell commands that it +would normally execute into the testrunner.log file. Example: + +``` + # Log the shell commmands that make up the mdevtest test. + tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest" +``` + + +# 5. Controlling CPU Core Utilization When running either binary or source code tests, testrunner.tcl reports the number of jobs it intends to use to stdout. e.g. diff --git a/manifest b/manifest index f0b7fb1dfb..bc7b1ca18c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sa\spattern\sto\sfilter\stest\sscripts\sto\sbe\sappended\sto\stestrunner.tcl\s"mdevtest",\s"sdevtest"\sand\s"release"\scommands.\se.g.\s"tclsh\stest/testrunner.tcl\ssdevtest\sfts5%". -D 2023-11-24T18:33:40.234 +C Add\sthe\s--buildonly\sand\s--dryrun\soptions\sto\stestrunner.tcl. +D 2023-11-24T20:14:18.100 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -41,7 +41,7 @@ F doc/compile-for-windows.md 50b27d77be96195c66031a3181cb8684ed822327ea834e07f9c F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/lemon.html 44a53a1d2b42d7751f7b2f478efb23c978e258d794bfd172442307a755b9fa44 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 -F doc/testrunner.md 727ef3877f5cea152a56f04566d0e095e0761918e9d771fc914db233a91bdd2e +F doc/testrunner.md 8d36ec692cf4994bb66d84a4645b9afa1ce9d47dc12cbf8d437c5a5fb6ddeedb F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56 F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a @@ -1653,7 +1653,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 3ce1f7d0541bcb60f23e843fcde6c66f76acd907bbfa0192620f40ad78a4c9c3 +F test/testrunner.tcl e18d71f2e797da808ba6d31335e504ed6b2791581b89287a72b697a2f31b1ea1 F test/testrunner_data.tcl e4d5017290a6d5c11785e36cc94c67d8bb950c8cdc2dbe4c1db2a3a583812560 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2143,8 +2143,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 e9951ede184ce07cf725152723d795f299922460715ab76225cd3071bf0f18ee -R b6b078e60149e99b226063ed1104d74a +P f8ea0b58b37f4052ded448e595d6d2992988a33b8ecfe58d68f20532f8cb5a37 +R a09b448e8fa45e3193b13a1fe5b499b2 U dan -Z 8e4057a95f8c06424678eb8234c037ec +Z ce0098ca9d7841ef7ea900cfd937f5a5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9b275cbf88..5863b29096 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f8ea0b58b37f4052ded448e595d6d2992988a33b8ecfe58d68f20532f8cb5a37 \ No newline at end of file +a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index e85aafadf2..edefdcd156 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -148,6 +148,8 @@ set TRG(cmdline) $argv set TRG(reporttime) 2000 set TRG(fuzztest) 0 ;# is the fuzztest option present. set TRG(zipvfs) "" ;# -zipvfs option, if any +set TRG(buildonly) 0 ;# True if --buildonly option +set TRG(dryrun) 0 ;# True if --dryrun option switch -nocase -glob -- $tcl_platform(os) { *darwin* { @@ -427,6 +429,10 @@ for {set ii 0} {$ii < [llength $argv]} {incr ii} { incr ii set TRG(zipvfs) [file normalize [lindex $argv $ii]] if {$isLast} { usage } + } elseif {($n>2 && [string match "$a*" --buildonly]) || $a=="-b"} { + set TRG(buildonly) 1 + } elseif {($n>2 && [string match "$a*" --dryrun]) || $a=="-d"} { + set TRG(dryrun) 1 } else { usage } @@ -843,6 +849,17 @@ proc make_new_testset {} { } +proc mark_job_as_finished {jobid output state endtm} { + r_write_db { + trdb eval { + UPDATE jobs + SET output=$output, state=$state, endtime=$endtm + WHERE jobid=$jobid; + UPDATE jobs SET state='ready' WHERE depid=$jobid; + } + } +} + proc script_input_ready {fd iJob jobid} { global TRG global O @@ -877,15 +894,7 @@ proc script_input_ready {fd iJob jobid} { puts $TRG(log) "### $job(displayname) ${jobtm}ms ($state)" puts $TRG(log) [string trim $O($iJob)] - r_write_db { - set output $O($iJob) - trdb eval { - UPDATE jobs - SET output=$output, state=$state, endtime=$tm - WHERE jobid=$jobid; - UPDATE jobs SET state='ready' WHERE depid=$jobid; - } - } + mark_job_as_finished $jobid $O($iJob) $state $tm dirs_freeDir $iJob launch_some_jobs @@ -937,16 +946,28 @@ proc launch_another_job {iJob} { close $fd } - set pwd [pwd] - cd $dir - set fd [open $TRG(run) w] - puts $fd $job(cmd) - close $fd - set fd [open "|$TRG(runcmd) 2>@1" r] - cd $pwd + if { $TRG(dryrun) } { - fconfigure $fd -blocking false - fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)] + mark_job_as_finished $job(jobid) "" done 0 + dirs_freeDir $iJob + if {$job(build)!=""} { + puts $TRG(log) "(cd $dir ; $job(cmd) )" + } else { + puts $TRG(log) "$job(cmd)" + } + + } else { + set pwd [pwd] + cd $dir + set fd [open $TRG(run) w] + puts $fd $job(cmd) + close $fd + set fd [open "|$TRG(runcmd) 2>@1" r] + cd $pwd + + fconfigure $fd -blocking false + fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)] + } return 1 } @@ -1044,6 +1065,16 @@ proc run_testset {} { puts "Test log is $TRG(logname)" } +# Handle the --buildonly option, if it was specified. +# +proc handle_buildonly {} { + global TRG + if {$TRG(buildonly)} { + r_write_db { + trdb eval { DELETE FROM jobs WHERE displaytype!='bld' } + } + } +} sqlite3 trdb $TRG(dbname) trdb timeout $TRG(timeout) @@ -1052,6 +1083,8 @@ if {$TRG(nJob)>1} { puts "splitting work across $TRG(nJob) jobs" } puts "built testset in [expr $tm/1000]ms.." + +handle_buildonly run_testset trdb close #puts [pwd] From 5e6500c81c717b74d3910909283fa23dec6139ee Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 24 Nov 2023 21:57:38 +0000 Subject: [PATCH 247/347] Continuing work on json_tree() against a JSONB. FossilOrigin-Name: 3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 35 +++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 49e3c13317..55cf674438 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Incremental\sprogress\stoward\sgetting\sjson_each()\sand\sjson_tree()\sto\swork\ndirectly\soff\sof\sa\sJSONB\sblob. -D 2023-11-24T18:44:00.124 +C Continuing\swork\son\sjson_tree()\sagainst\sa\sJSONB. +D 2023-11-24T21:57:38.322 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 1b9cc57728bc3a420bae5f4ae8233dcd2c3a337f00a2467346f7b788aedf1ba0 +F src/json.c 699b3fa340c24a10874988b9cbf4a1635b4231569dfaef9e4fd8fb96195406aa F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,11 +2145,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 ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 -R b8a3524be145a961ee4bfad02645fa25 -T *branch * jsonb-tree -T *sym-jsonb-tree * -T -sym-jsonb * +P f8cab41b3bc65af6ff34b481db693d640ea025d09463d50b8e56d855e2abc913 +R 12dbc87edffde88122e858181ec1f996 U drh -Z b88ae8be743d3b0432d41e7f9773a471 +Z f88095c49473dd89bb79ab974b58607c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3afbf02fc8..a401927f74 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f8cab41b3bc65af6ff34b481db693d640ea025d09463d50b8e56d855e2abc913 \ No newline at end of file +3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad \ No newline at end of file diff --git a/src/json.c b/src/json.c index fdd2a21d59..5823eadc88 100644 --- a/src/json.c +++ b/src/json.c @@ -5576,6 +5576,7 @@ static void jsonObjectFinal(sqlite3_context *ctx){ typedef struct JsonParent JsonParent; struct JsonParent { u32 iHead; /* Start of object or array */ + u32 iValue; /* Start of the value */ u32 iEnd; /* First byte past the end */ i64 iKey; /* Key for JSONB_ARRAY */ }; @@ -5773,7 +5774,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ u32 n, sz = 0; u32 i = jsonSkipLabel(p); x = p->sParse.aBlob[i] & 0x0f; - n = jsonbPayloadSize(&p->sParse, p->i, &sz); + n = jsonbPayloadSize(&p->sParse, i, &sz); if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ JsonParent *pParent; if( p->nParent>=p->nParentAlloc ){ @@ -5784,32 +5785,41 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ if( pNew==0 ) return SQLITE_NOMEM; p->nParentAlloc = (u32)nNew; p->aParent = pNew; - levelChange = 1; } + levelChange = 1; pParent = &p->aParent[p->nParent++]; pParent->iHead = p->i; - pParent->iEnd = p->i + n + sz; - pParent->iKey = 0; + pParent->iValue = i; + pParent->iEnd = i + n + sz; + pParent->iKey = -1; + p->i = i + n; }else{ p->i = i + n + sz; } - if( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ + while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ p->nParent--; levelChange = 1; } if( levelChange ){ if( p->nParent>0 ){ - p->eType = p->sParse.aBlob[p->aParent[p->nParent-1].iHead] & 0x0f; + JsonParent *pParent = &p->aParent[p->nParent-1]; + u32 i = pParent->iValue; + p->eType = p->sParse.aBlob[i] & 0x0f; }else{ p->eType = 0; } } + if( p->eType==JSONB_ARRAY ){ + assert( p->nParent>0 ); + p->aParent[p->nParent-1].iKey++; + } }else{ u32 n, sz = 0; u32 i = jsonSkipLabel(p); n = jsonbPayloadSize(&p->sParse, i, &sz); p->i = i + n + sz; } + p->iRowid++; return SQLITE_OK; } @@ -6022,6 +6032,19 @@ static int jsonEachColumn( break; } case JEACH_FULLKEY: { +#if 0 + u32 i; + JsonString x; + jsonStringInit(&x, ctx); + for(i=0; inParent; i++){ + jsonPrintf(200,&x,"(%u,%u,%u,%lld)", + p->aParent[i].iHead, + p->aParent[i].iValue, + p->aParent[i].iEnd, + p->aParent[i].iKey); + } + jsonReturnString(&x); +#endif #if 0 JsonString x; jsonStringInit(&x, ctx); From b7d5cb711ae2cc0906e91de380d0eef8a5b3c363 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 13:40:19 +0000 Subject: [PATCH 248/347] Handle the path argument to json_tree() and json_each(). FossilOrigin-Name: fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 53 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 55cf674438..b7f38de853 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Continuing\swork\son\sjson_tree()\sagainst\sa\sJSONB. -D 2023-11-24T21:57:38.322 +C Handle\sthe\spath\sargument\sto\sjson_tree()\sand\sjson_each(). +D 2023-11-25T13:40:19.363 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 699b3fa340c24a10874988b9cbf4a1635b4231569dfaef9e4fd8fb96195406aa +F src/json.c ff48d52463493f5f40d6e1e14ac3dc9277c40290df140a52bd17e7a562f766ea F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 f8cab41b3bc65af6ff34b481db693d640ea025d09463d50b8e56d855e2abc913 -R 12dbc87edffde88122e858181ec1f996 +P 3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad +R 86973d4c807995bd645f2f5dbc7eac48 U drh -Z f88095c49473dd89bb79ab974b58607c +Z e2d0e1a86b5ddc3117954e8a28079ed7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a401927f74..555d0938d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad \ No newline at end of file +fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 5823eadc88..503fff9368 100644 --- a/src/json.c +++ b/src/json.c @@ -353,6 +353,7 @@ struct JsonParse { u8 eEdit; /* Edit operation to apply */ int delta; /* Size change due to the edit */ u32 nIns; /* Number of bytes to insert */ + u32 iLabel; /* Location of label if search landed on an object value */ u8 *aIns; /* Content to be inserted */ }; @@ -3904,6 +3905,7 @@ static u32 jsonLookupBlobStep( memcpy(&pParse->aBlob[iRoot], pParse->aIns, pParse->nIns); } } + pParse->iLabel = iLabel; return iRoot; } if( zPath[0]=='.' ){ @@ -6015,10 +6017,10 @@ static int jsonEachColumn( break; } case JEACH_ATOM: { - u32 i; - if( p->eType>=JSON_ARRAY ) break; - i = jsonSkipLabel(p); - jsonReturnFromBlob(&p->sParse, i, ctx, 1); + u32 i = jsonSkipLabel(p); + if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); + } break; } case JEACH_ID: { @@ -6204,13 +6206,50 @@ static int jsonEachFilter( if( p->sParse.aBlob==0 ){ return SQLITE_NOMEM; } - i = p->i = 0; - p->iEnd = 0; - p->eType = 0; + if( idxNum==3 ){ + zRoot = (const char*)sqlite3_value_text(argv[1]); + if( zRoot==0 ) return SQLITE_OK; + n = sqlite3_value_bytes(argv[1]); + p->zRoot = sqlite3_malloc64( n+1 ); + if( p->zRoot==0 ) return SQLITE_NOMEM; + memcpy(p->zRoot, zRoot, (size_t)n+1); + if( zRoot[0]!='$' ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + } + if( zRoot[1]==0 ){ + i = p->i = 0; + p->eType = 0; + }else{ + i = jsonLookupBlobStep(&p->sParse, 0, p->zRoot+1, 0); + if( JSON_BLOB_ISERROR(i) ){ + p->i = 0; + p->eType = 0; + p->iEnd = 0; + return SQLITE_OK; + } + if( p->sParse.iLabel ){ + p->i = p->sParse.iLabel; + p->eType = JSONB_OBJECT; + }else{ + p->i = i; + p->eType = JSONB_ARRAY; + } + } + }else{ + i = p->i = 0; + p->eType = 0; + } p->nParent = 0; p->sParse.isBinary = 1; n = jsonbPayloadSize(&p->sParse, i, &sz); p->iEnd = i+n+sz; + if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ + p->i += n; + p->eType = p->sParse.aBlob[i] & 0x0f; + } return SQLITE_OK; }else{ z = (const char*)sqlite3_value_text(argv[0]); From c2474105ca39070a40f53b3c36550c9b169c5469 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 18:11:11 +0000 Subject: [PATCH 249/347] Generate the fullkey and path columns of json_tree(). FossilOrigin-Name: ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 104 +++++++++++++++++++++++++------------------------- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/manifest b/manifest index b7f38de853..af86c512c2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Handle\sthe\spath\sargument\sto\sjson_tree()\sand\sjson_each(). -D 2023-11-25T13:40:19.363 +C Generate\sthe\sfullkey\sand\spath\scolumns\sof\sjson_tree(). +D 2023-11-25T18:11:11.468 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c ff48d52463493f5f40d6e1e14ac3dc9277c40290df140a52bd17e7a562f766ea +F src/json.c 9a5ff28f7b55e83b92eeab9285eb4b0132f11526620565e2d5b81fb84d4e8611 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 3df891cb11feee65e239ee2506eda34a9688341f05210d7c2e25a05338cb71ad -R 86973d4c807995bd645f2f5dbc7eac48 +P fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e +R 90c664426338898baffedf8dcba41bea U drh -Z e2d0e1a86b5ddc3117954e8a28079ed7 +Z e9b6e925dfee6d388855b55f2b458a4c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 555d0938d3..acd1f46af1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e \ No newline at end of file +ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 503fff9368..c8269436eb 100644 --- a/src/json.c +++ b/src/json.c @@ -796,7 +796,6 @@ static void jsonAppendSqlValue( } } - /* Make the text in p (which is probably a generated JSON text string) ** the result of the SQL function. ** @@ -5580,6 +5579,7 @@ struct JsonParent { u32 iHead; /* Start of object or array */ u32 iValue; /* Start of the value */ u32 iEnd; /* First byte past the end */ + u32 nPath; /* Length of path */ i64 iKey; /* Key for JSONB_ARRAY */ }; @@ -5598,6 +5598,7 @@ struct JsonEachCursor { char *zJson; /* Input JSON */ char *zRoot; /* Path by which to filter zJson */ sqlite3 *db; /* Database connection */ + JsonString path; /* Current path */ JsonParse sParse; /* Parse of the input JSON */ }; typedef struct JsonEachConnection JsonEachConnection; @@ -5666,6 +5667,7 @@ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->db = pVtab->db; + jsonStringZero(&pCur->path); *ppCursor = &pCur->base; return SQLITE_OK; } @@ -5685,6 +5687,7 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ static void jsonEachCursorReset(JsonEachCursor *p){ sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); + jsonStringReset(&p->path); sqlite3DbFree(p->db, p->aParent); p->iRowid = 0; p->i = 0; @@ -5701,6 +5704,7 @@ static void jsonEachCursorReset(JsonEachCursor *p){ static int jsonEachClose(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; jsonEachCursorReset(p); + sqlite3_free(cur); return SQLITE_OK; } @@ -5727,6 +5731,39 @@ static int jsonSkipLabel(JsonEachCursor *p){ } } +/* +** Append the path name for the current element. +*/ +static void jsonAppendPathName(JsonEachCursor *p){ + assert( p->nParent>0 ); + assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); + if( p->eType==JSONB_ARRAY ){ + jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); + }else{ + u32 n, sz = 0, k, i; + const char *z; + int needQuote = 0; + n = jsonbPayloadSize(&p->sParse, p->i, &sz); + k = p->i + n; + z = (const char*)&p->sParse.aBlob[k]; + if( sz==0 || !sqlite3Isalpha(z[0]) ){ + needQuote = 1; + }else{ + for(i=0; ipath,".\"%.*s\"", sz, z); + }else{ + jsonPrintf(sz+2,&p->path,".%.*s", sz, z); + } + } +} + /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; @@ -5789,17 +5826,21 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ p->aParent = pNew; } levelChange = 1; - pParent = &p->aParent[p->nParent++]; + pParent = &p->aParent[p->nParent]; pParent->iHead = p->i; pParent->iValue = i; pParent->iEnd = i + n + sz; pParent->iKey = -1; + pParent->nPath = (u32)p->path.nUsed; + if( p->eType && p->nParent ) jsonAppendPathName(p); + p->nParent++; p->i = i + n; }else{ p->i = i + n + sz; } while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ p->nParent--; + p->path.nUsed = p->aParent[p->nParent].nPath; levelChange = 1; } if( levelChange ){ @@ -6034,53 +6075,16 @@ static int jsonEachColumn( break; } case JEACH_FULLKEY: { -#if 0 - u32 i; - JsonString x; - jsonStringInit(&x, ctx); - for(i=0; inParent; i++){ - jsonPrintf(200,&x,"(%u,%u,%u,%lld)", - p->aParent[i].iHead, - p->aParent[i].iValue, - p->aParent[i].iEnd, - p->aParent[i].iKey); - } - jsonReturnString(&x); -#endif -#if 0 - JsonString x; - jsonStringInit(&x, ctx); - if( p->bRecursive ){ - jsonEachComputePath(p, &x, p->i); - }else{ - if( p->zRoot ){ - jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); - }else{ - jsonAppendChar(&x, '$'); - } - if( p->eType==JSON_ARRAY ){ - jsonPrintf(30, &x, "[%d]", p->iRowid); - }else if( p->eType==JSON_OBJECT ){ - jsonAppendObjectPathElementOfNode(&x, pThis); - } - } - jsonReturnString(&x); -#endif + u64 nBase = p->path.nUsed; + if( p->nParent ) jsonAppendPathName(p); + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + p->path.nUsed = nBase; break; } case JEACH_PATH: { -#if 0 - if( p->bRecursive ){ - JsonString x; - jsonStringInit(&x, ctx); - jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); - jsonReturnString(&x); - break; - } - /* For json_each() path and root are the same so fall through - ** into the root case */ - /* no break */ deliberate_fall_through -#endif + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); break; } default: { @@ -6209,10 +6213,6 @@ static int jsonEachFilter( if( idxNum==3 ){ zRoot = (const char*)sqlite3_value_text(argv[1]); if( zRoot==0 ) return SQLITE_OK; - n = sqlite3_value_bytes(argv[1]); - p->zRoot = sqlite3_malloc64( n+1 ); - if( p->zRoot==0 ) return SQLITE_NOMEM; - memcpy(p->zRoot, zRoot, (size_t)n+1); if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); @@ -6223,7 +6223,7 @@ static int jsonEachFilter( i = p->i = 0; p->eType = 0; }else{ - i = jsonLookupBlobStep(&p->sParse, 0, p->zRoot+1, 0); + i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); if( JSON_BLOB_ISERROR(i) ){ p->i = 0; p->eType = 0; @@ -6238,9 +6238,11 @@ static int jsonEachFilter( p->eType = JSONB_ARRAY; } } + jsonAppendRaw(&p->path, zRoot, sqlite3Strlen30(zRoot)); }else{ i = p->i = 0; p->eType = 0; + jsonAppendRaw(&p->path, "$", 1); } p->nParent = 0; p->sParse.isBinary = 1; From aea2d2312103e2c63c6433977e6e06a32e2244a4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 19:28:44 +0000 Subject: [PATCH 250/347] Almost working. Path is still not exactly right when Root is defined on json_tree(). FossilOrigin-Name: 92258246916a9c0d72785964513113848a850dec78bdade8b3f274e410df4e7e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 10 +++++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index af86c512c2..8851c060f1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Generate\sthe\sfullkey\sand\spath\scolumns\sof\sjson_tree(). -D 2023-11-25T18:11:11.468 +C Almost\sworking.\s\sPath\sis\sstill\snot\sexactly\sright\swhen\sRoot\sis\sdefined\son\njson_tree(). +D 2023-11-25T19:28:44.393 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9a5ff28f7b55e83b92eeab9285eb4b0132f11526620565e2d5b81fb84d4e8611 +F src/json.c 617eaedbe44c139f0144f04ecbfa240721462f45d76e715eaacc99e172bf43b0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 fded888469565b2a4687185a926bd23fccfbf167c8bebe6c10696fc7f972f41e -R 90c664426338898baffedf8dcba41bea +P ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 +R b82a578143acb9c5e1a084b85e2e99b7 U drh -Z e9b6e925dfee6d388855b55f2b458a4c +Z 1f6ed13fcc3b9ce0d1d8c376c125c482 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index acd1f46af1..78ef7d1413 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 \ No newline at end of file +92258246916a9c0d72785964513113848a850dec78bdade8b3f274e410df4e7e \ No newline at end of file diff --git a/src/json.c b/src/json.c index c8269436eb..2de4c61a12 100644 --- a/src/json.c +++ b/src/json.c @@ -6249,8 +6249,16 @@ static int jsonEachFilter( n = jsonbPayloadSize(&p->sParse, i, &sz); p->iEnd = i+n+sz; if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ - p->i += n; + p->i = i + n; p->eType = p->sParse.aBlob[i] & 0x0f; + p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); + if( p->aParent==0 ) return SQLITE_NOMEM; + p->nParent = 1; + p->nParentAlloc = 1; + p->aParent[0].iKey = 0; + p->aParent[0].iEnd = p->iEnd; + p->aParent[0].iHead = p->i; + p->aParent[0].iValue = i; } return SQLITE_OK; }else{ From 796abda53828a776e0d6057406656e4295446002 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 20:59:03 +0000 Subject: [PATCH 251/347] Remove the vestigal JsonNode logic from json_tree() and json_each(). FossilOrigin-Name: 66c2ab9ebbf90477742e6be0d30e061d827c409de038f2a5b73479ed9448c4a6 --- manifest | 12 +- manifest.uuid | 2 +- src/json.c | 552 +++++++++++--------------------------------------- 3 files changed, 124 insertions(+), 442 deletions(-) diff --git a/manifest b/manifest index 8851c060f1..1f18d63513 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Almost\sworking.\s\sPath\sis\sstill\snot\sexactly\sright\swhen\sRoot\sis\sdefined\son\njson_tree(). -D 2023-11-25T19:28:44.393 +C Remove\sthe\svestigal\sJsonNode\slogic\sfrom\sjson_tree()\sand\sjson_each(). +D 2023-11-25T20:59:03.008 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 617eaedbe44c139f0144f04ecbfa240721462f45d76e715eaacc99e172bf43b0 +F src/json.c 211c4e1b8fd886c97d3224105bfef9d4cc04db2e3f1daca1e95c52343df4e85c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 ffaa468ab8871906121df9ee5ef3dc00129a0086ed9c18831ecda69bf7f71455 -R b82a578143acb9c5e1a084b85e2e99b7 +P 92258246916a9c0d72785964513113848a850dec78bdade8b3f274e410df4e7e +R 43fba60a847876455ec8abde78205ef3 U drh -Z 1f6ed13fcc3b9ce0d1d8c376c125c482 +Z d0660c039b12ecd1fe4ffd828711a405 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 78ef7d1413..3b93842124 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -92258246916a9c0d72785964513113848a850dec78bdade8b3f274e410df4e7e \ No newline at end of file +66c2ab9ebbf90477742e6be0d30e061d827c409de038f2a5b73479ed9448c4a6 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 2de4c61a12..cc4eb83c5b 100644 --- a/src/json.c +++ b/src/json.c @@ -329,7 +329,6 @@ struct JsonParse { JsonNode *aNode; /* Array of nodes containing the parse */ char *zJson; /* Original JSON string (before edits) */ char *zAlt; /* Revised and/or mimified JSON */ - u32 *aUp; /* Index of parent of each node */ JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ @@ -860,10 +859,6 @@ static void jsonParseReset(JsonParse *pParse){ } pParse->nNode = 0; pParse->nAlloc = 0; - if( pParse->aUp ){ - sqlite3_free(pParse->aUp); - pParse->aUp = 0; - } if( pParse->bJsonIsRCStr ){ sqlite3RCStrUnref(pParse->zJson); pParse->zJson = 0; @@ -2013,33 +2008,6 @@ json_parse_restart: } /* End switch(z[i]) */ } -/* Mark node i of pParse as being a child of iParent. Call recursively -** to fill in all the descendants of node i. -*/ -static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ - JsonNode *pNode = &pParse->aNode[i]; - u32 j; - pParse->aUp[i] = iParent; - switch( pNode->eType ){ - case JSON_ARRAY: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ - jsonParseFillInParentage(pParse, i+j, i); - } - break; - } - case JSON_OBJECT: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ - pParse->aUp[i+j] = i; - jsonParseFillInParentage(pParse, i+j+1, i); - } - break; - } - default: { - break; - } - } -} - /* ** Parse JSON (either pure RFC-8259 JSON text, or JSON-5 text, or a JSONB ** blob) into the JsonNode representation. @@ -2090,21 +2058,6 @@ static int jsonParse( return 0; } -/* -** Compute the parentage of all nodes in a completed parse. -*/ -static int jsonParseFindParents(JsonParse *pParse){ - u32 *aUp; - assert( pParse->aUp==0 ); - aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); - if( aUp==0 ){ - pParse->oom = 1; - return SQLITE_NOMEM; - } - jsonParseFillInParentage(pParse, 0, 0); - return SQLITE_OK; -} - /* ** Magic number used for the JSON parse cache in sqlite3_get_auxdata() */ @@ -3257,7 +3210,7 @@ static int jsonConvertTextToBlob( } } if( i<=0 ){ - if( ALWAYS(pCtx!=0) ){ + if( pCtx!=0 ){ if( pParse->oom ){ sqlite3_result_error_nomem(pCtx); }else{ @@ -5767,47 +5720,7 @@ static void jsonAppendPathName(JsonEachCursor *p){ /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; - if( p->sParse.aNode ){ - /* LEGACY */ - if( p->bRecursive ){ - if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; - p->i++; - p->iRowid++; - if( p->iiEnd ){ - u32 iUp = p->sParse.aUp[p->i]; - JsonNode *pUp = &p->sParse.aNode[iUp]; - p->eType = pUp->eType; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==0 || pUp->eU==3 ); - testcase( pUp->eU==3 ); - JSON_VVA( pUp->eU = 3 ); - if( iUp==p->i-1 ){ - pUp->u.iKey = 0; - }else{ - pUp->u.iKey++; - } - } - } - }else{ - switch( p->eType ){ - case JSON_ARRAY: { - p->i += jsonNodeSize(&p->sParse.aNode[p->i]); - p->iRowid++; - break; - } - case JSON_OBJECT: { - p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); - p->iRowid++; - break; - } - default: { - p->i = p->iEnd; - break; - } - } - } - return SQLITE_OK; - }else if( p->bRecursive ){ + if( p->bRecursive ){ u8 x; u8 levelChange = 0; u32 n, sz = 0; @@ -5866,70 +5779,6 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ return SQLITE_OK; } -/* Append an object label to the JSON Path being constructed -** in pStr. -*/ -static void jsonAppendObjectPathElementOfNode( - JsonString *pStr, - JsonNode *pNode -){ - int nn; - const char *z; - int bNeedQuote = 0; - assert( pNode->eType==JSON_STRING ); - assert( pNode->jnFlags & JNODE_LABEL ); - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - nn = pNode->n; - if( pNode->jnFlags & JNODE_RAW ){ - /* no-op */ - }else if( nn==0 || !sqlite3Isalpha(z[0]) ){ - bNeedQuote = 1; - }else{ - int jj; - for(jj=1; jjsParse.aNode ){ - /* LEGACY */ - assert( p->sParse.aUp ); - iUp = p->sParse.aUp[i]; - jsonEachComputePath(p, pStr, iUp); - pNode = &p->sParse.aNode[i]; - pUp = &p->sParse.aNode[iUp]; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); - testcase( pUp->eU==0 ); - jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); - }else{ - assert( pUp->eType==JSON_OBJECT ); - if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; - jsonAppendObjectPathElementOfNode(pStr, pNode); - } - return; - } -} - /* Return the value of a column */ static int jsonEachColumn( sqlite3_vtab_cursor *cur, /* The cursor */ @@ -5937,173 +5786,73 @@ static int jsonEachColumn( int iColumn /* Which column to return */ ){ JsonEachCursor *p = (JsonEachCursor*)cur; - if( p->sParse.aNode!=0 ){ - /* LEGACY */ - JsonNode *pThis = &p->sParse.aNode[p->i]; - switch( iColumn ){ - case JEACH_KEY: { - if( p->i==0 ) break; - if( p->eType==JSON_OBJECT ){ - jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - }else if( p->eType==JSON_ARRAY ){ - u32 iKey; - if( p->bRecursive ){ - if( p->iRowid==0 ) break; - assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); - iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; - }else{ - iKey = p->iRowid; - } - sqlite3_result_int64(ctx, (sqlite3_int64)iKey); - } - break; - } - case JEACH_VALUE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - break; - } - case JEACH_TYPE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); - break; - } - case JEACH_ATOM: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - if( pThis->eType>=JSON_ARRAY ) break; - jsonReturnFromNode(&p->sParse, pThis, ctx, 0); - break; - } - case JEACH_ID: { - sqlite3_result_int64(ctx, - (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); - break; - } - case JEACH_PARENT: { - if( p->i>p->iBegin && p->bRecursive ){ - sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); - } - break; - } - case JEACH_FULLKEY: { - JsonString x; - jsonStringInit(&x, ctx); - if( p->bRecursive ){ - jsonEachComputePath(p, &x, p->i); - }else{ - if( p->zRoot ){ - jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); - }else{ - jsonAppendChar(&x, '$'); - } - if( p->eType==JSON_ARRAY ){ - jsonPrintf(30, &x, "[%d]", p->iRowid); - }else if( p->eType==JSON_OBJECT ){ - jsonAppendObjectPathElementOfNode(&x, pThis); - } - } - jsonReturnString(&x); - break; - } - case JEACH_PATH: { - if( p->bRecursive ){ - JsonString x; - jsonStringInit(&x, ctx); - jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); - jsonReturnString(&x); - break; - } - /* For json_each() path and root are the same so fall through - ** into the root case */ - /* no break */ deliberate_fall_through - } - default: { - const char *zRoot = p->zRoot; - if( zRoot==0 ) zRoot = "$"; - sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); - break; - } - case JEACH_JSON: { - if( p->sParse.isBinary ){ - sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, - SQLITE_STATIC); - }else{ - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); - } - break; + switch( iColumn ){ + case JEACH_KEY: { + if( p->nParent==0 ) break; + if( p->eType==JSONB_OBJECT ){ + jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); + }else{ + assert( p->eType==JSONB_ARRAY ); + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); } + break; } - }else{ - /* Blob scan */ - switch( iColumn ){ - case JEACH_KEY: { - if( p->nParent==0 ) break; - if( p->eType==JSONB_OBJECT ){ - jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); - }else{ - assert( p->eType==JSONB_ARRAY ); - sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); - } - break; - } - case JEACH_VALUE: { - u32 i = jsonSkipLabel(p); + case JEACH_VALUE: { + u32 i = jsonSkipLabel(p); + jsonReturnFromBlob(&p->sParse, i, ctx, 1); + break; + } + case JEACH_TYPE: { + u32 i = jsonSkipLabel(p); + u8 eType = eType = p->sParse.aBlob[i] & 0x0f; + sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); + break; + } + case JEACH_ATOM: { + u32 i = jsonSkipLabel(p); + if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); - break; - } - case JEACH_TYPE: { - u32 i = jsonSkipLabel(p); - u8 eType = eType = p->sParse.aBlob[i] & 0x0f; - sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); - break; - } - case JEACH_ATOM: { - u32 i = jsonSkipLabel(p); - if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); - } - break; - } - case JEACH_ID: { - sqlite3_result_int64(ctx, (sqlite3_int64)p->i); - break; - } - case JEACH_PARENT: { - if( p->nParent>0 ){ - sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); - } - break; - } - case JEACH_FULLKEY: { - u64 nBase = p->path.nUsed; - if( p->nParent ) jsonAppendPathName(p); - sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, - SQLITE_TRANSIENT, SQLITE_UTF8); - p->path.nUsed = nBase; - break; - } - case JEACH_PATH: { - sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, - SQLITE_TRANSIENT, SQLITE_UTF8); - break; - } - default: { - const char *zRoot = p->zRoot; - if( zRoot==0 ) zRoot = "$"; - sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); - break; - } - case JEACH_JSON: { - if( p->sParse.isBinary ){ - sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, - SQLITE_STATIC); - }else{ - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); - } - break; } + break; + } + case JEACH_ID: { + sqlite3_result_int64(ctx, (sqlite3_int64)p->i); + break; + } + case JEACH_PARENT: { + if( p->nParent>0 ){ + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); + } + break; + } + case JEACH_FULLKEY: { + u64 nBase = p->path.nUsed; + if( p->nParent ) jsonAppendPathName(p); + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + p->path.nUsed = nBase; + break; + } + case JEACH_PATH: { + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + break; + } + default: { + const char *zRoot = p->zRoot; + if( zRoot==0 ) zRoot = "$"; + sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); + break; + } + case JEACH_JSON: { + if( p->sParse.isBinary ){ + sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, + SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + } + break; } - } return SQLITE_OK; } @@ -6192,10 +5941,8 @@ static int jsonEachFilter( int argc, sqlite3_value **argv ){ JsonEachCursor *p = (JsonEachCursor*)cur; - const char *z; const char *zRoot = 0; - sqlite3_int64 n; - int isBinary; + u32 i, n, sz; UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(argc); @@ -6204,136 +5951,71 @@ static int jsonEachFilter( memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; if( jsonFuncArgMightBeBinary(argv[0]) ){ - u32 i, n, sz; p->sParse.nBlob = sqlite3_value_bytes(argv[0]); p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); if( p->sParse.aBlob==0 ){ return SQLITE_NOMEM; } - if( idxNum==3 ){ - zRoot = (const char*)sqlite3_value_text(argv[1]); - if( zRoot==0 ) return SQLITE_OK; - if( zRoot[0]!='$' ){ - sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); - jsonEachCursorReset(p); - return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + }else{ + p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); + p->sParse.nJson = sqlite3_value_bytes(argv[0]); + if( p->sParse.zJson==0 || jsonConvertTextToBlob(&p->sParse, 0) ){ + if( p->sParse.oom ){ + return SQLITE_NOMEM; } - if( zRoot[1]==0 ){ - i = p->i = 0; - p->eType = 0; - }else{ - i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); - if( JSON_BLOB_ISERROR(i) ){ - p->i = 0; - p->eType = 0; - p->iEnd = 0; - return SQLITE_OK; - } - if( p->sParse.iLabel ){ - p->i = p->sParse.iLabel; - p->eType = JSONB_OBJECT; - }else{ - p->i = i; - p->eType = JSONB_ARRAY; - } - } - jsonAppendRaw(&p->path, zRoot, sqlite3Strlen30(zRoot)); - }else{ + p->i = p->iEnd = 0; + return SQLITE_OK; + } + } + if( idxNum==3 ){ + zRoot = (const char*)sqlite3_value_text(argv[1]); + if( zRoot==0 ) return SQLITE_OK; + if( zRoot[0]!='$' ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + } + if( zRoot[1]==0 ){ i = p->i = 0; p->eType = 0; - jsonAppendRaw(&p->path, "$", 1); - } - p->nParent = 0; - p->sParse.isBinary = 1; - n = jsonbPayloadSize(&p->sParse, i, &sz); - p->iEnd = i+n+sz; - if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ - p->i = i + n; - p->eType = p->sParse.aBlob[i] & 0x0f; - p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); - if( p->aParent==0 ) return SQLITE_NOMEM; - p->nParent = 1; - p->nParentAlloc = 1; - p->aParent[0].iKey = 0; - p->aParent[0].iEnd = p->iEnd; - p->aParent[0].iHead = p->i; - p->aParent[0].iValue = i; - } - return SQLITE_OK; - }else{ - z = (const char*)sqlite3_value_text(argv[0]); - isBinary = 0; - } - if( z==0 ) return SQLITE_OK; - if( sqlite3ValueIsOfClass(argv[0], sqlite3RCStrUnref) ){ - p->sParse.zJson = sqlite3RCStrRef((char*)z); - }else{ - n = sqlite3_value_bytes(argv[0]); - p->sParse.zJson = sqlite3RCStrNew( n+1 ); - if( p->sParse.zJson==0 ) return SQLITE_NOMEM; - memcpy(p->sParse.zJson, z, (size_t)n+(isBinary==0)); - p->sParse.nJson = n; - } - p->sParse.bJsonIsRCStr = 1; - p->sParse.isBinary = isBinary; - p->zJson = p->sParse.zJson; - if( jsonParse(&p->sParse, 0) ){ - int rc = SQLITE_NOMEM; - if( p->sParse.oom==0 ){ - sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); - if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR; - } - jsonEachCursorReset(p); - return rc; - }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){ - jsonEachCursorReset(p); - return SQLITE_NOMEM; - }else{ - JsonNode *pNode = 0; - if( idxNum==3 ){ - const char *zErr = 0; - zRoot = (const char*)sqlite3_value_text(argv[1]); - if( zRoot==0 ) return SQLITE_OK; - n = sqlite3_value_bytes(argv[1]); - p->zRoot = sqlite3_malloc64( n+1 ); - if( p->zRoot==0 ) return SQLITE_NOMEM; - memcpy(p->zRoot, zRoot, (size_t)n+1); - if( zRoot[0]!='$' ){ - zErr = zRoot; - }else{ - pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr); - } - if( zErr ){ - sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr, 0); - jsonEachCursorReset(p); - return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; - }else if( pNode==0 ){ + }else{ + i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); + if( JSON_BLOB_ISERROR(i) ){ + p->i = 0; + p->eType = 0; + p->iEnd = 0; return SQLITE_OK; } - }else{ - pNode = p->sParse.aNode; - } - p->iBegin = p->i = (int)(pNode - p->sParse.aNode); - p->eType = pNode->eType; - if( p->eType>=JSON_ARRAY ){ - assert( pNode->eU==0 ); - JSON_VVA( pNode->eU = 3 ); - pNode->u.iKey = 0; - p->iEnd = p->i + pNode->n + 1; - if( p->bRecursive ){ - p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType; - if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){ - p->i--; - } + if( p->sParse.iLabel ){ + p->i = p->sParse.iLabel; + p->eType = JSONB_OBJECT; }else{ - p->i++; + p->i = i; + p->eType = JSONB_ARRAY; } - }else{ - p->iEnd = p->i+1; } + jsonAppendRaw(&p->path, zRoot, sqlite3Strlen30(zRoot)); + }else{ + i = p->i = 0; + p->eType = 0; + jsonAppendRaw(&p->path, "$", 1); + } + p->nParent = 0; + p->sParse.isBinary = 1; + n = jsonbPayloadSize(&p->sParse, i, &sz); + p->iEnd = i+n+sz; + if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ + p->i = i + n; + p->eType = p->sParse.aBlob[i] & 0x0f; + p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); + if( p->aParent==0 ) return SQLITE_NOMEM; + p->nParent = 1; + p->nParentAlloc = 1; + p->aParent[0].iKey = 0; + p->aParent[0].iEnd = p->iEnd; + p->aParent[0].iHead = p->i; + p->aParent[0].iValue = i; } return SQLITE_OK; } From e09a38c2e830b0311405cb6b442aa6f4d07583fc Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 25 Nov 2023 23:00:50 +0000 Subject: [PATCH 252/347] Remove unused elements from the json_tree() cursor. FossilOrigin-Name: 914a50117d477b2cd30d58388fb8d1b71ff7ff6842ba025f38efc6e9647d06d0 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 14 ++++---------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 1f18d63513..17f760ca91 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\svestigal\sJsonNode\slogic\sfrom\sjson_tree()\sand\sjson_each(). -D 2023-11-25T20:59:03.008 +C Remove\sunused\selements\sfrom\sthe\sjson_tree()\scursor. +D 2023-11-25T23:00:50.622 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 211c4e1b8fd886c97d3224105bfef9d4cc04db2e3f1daca1e95c52343df4e85c +F src/json.c 7bd0d3809b31e32ffa053ba68cf03f67121f6bdd0be7031e8d4c761bef9b6aee F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 92258246916a9c0d72785964513113848a850dec78bdade8b3f274e410df4e7e -R 43fba60a847876455ec8abde78205ef3 +P 66c2ab9ebbf90477742e6be0d30e061d827c409de038f2a5b73479ed9448c4a6 +R 79cb22ebedffb4f5b0a0989ee5c920ed U drh -Z d0660c039b12ecd1fe4ffd828711a405 +Z b95c9f50e6b9041cf41818958255b068 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3b93842124..a22ec07c45 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -66c2ab9ebbf90477742e6be0d30e061d827c409de038f2a5b73479ed9448c4a6 \ No newline at end of file +914a50117d477b2cd30d58388fb8d1b71ff7ff6842ba025f38efc6e9647d06d0 \ No newline at end of file diff --git a/src/json.c b/src/json.c index cc4eb83c5b..1a2a5bd9ef 100644 --- a/src/json.c +++ b/src/json.c @@ -5540,16 +5540,14 @@ typedef struct JsonEachCursor JsonEachCursor; struct JsonEachCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ u32 iRowid; /* The rowid */ - u32 iBegin; /* The first node of the scan */ u32 i; /* Index in sParse.aBlob[] of current row */ u32 iEnd; /* EOF when i equals or exceeds this value */ + u32 nRoot; /* Size of the root argument in bytes */ u8 eType; /* Type of the container for element i */ u8 bRecursive; /* True for json_tree(). False for json_each() */ u32 nParent; /* Current nesting depth */ u32 nParentAlloc; /* Space allocated for aParent[] */ JsonParent *aParent; /* Parent elements of i */ - char *zJson; /* Input JSON */ - char *zRoot; /* Path by which to filter zJson */ sqlite3 *db; /* Database connection */ JsonString path; /* Current path */ JsonParse sParse; /* Parse of the input JSON */ @@ -5638,7 +5636,6 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ /* Reset a JsonEachCursor back to its original state. Free any memory ** held. */ static void jsonEachCursorReset(JsonEachCursor *p){ - sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); jsonStringReset(&p->path); sqlite3DbFree(p->db, p->aParent); @@ -5649,8 +5646,6 @@ static void jsonEachCursorReset(JsonEachCursor *p){ p->nParentAlloc = 0; p->iEnd = 0; p->eType = 0; - p->zJson = 0; - p->zRoot = 0; } /* Destructor for a jsonEachCursor object */ @@ -5839,9 +5834,7 @@ static int jsonEachColumn( break; } default: { - const char *zRoot = p->zRoot; - if( zRoot==0 ) zRoot = "$"; - sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); + sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); break; } case JEACH_JSON: { @@ -5956,6 +5949,7 @@ static int jsonEachFilter( if( p->sParse.aBlob==0 ){ return SQLITE_NOMEM; } + p->sParse.isBinary = 1; }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); p->sParse.nJson = sqlite3_value_bytes(argv[0]); @@ -6001,8 +5995,8 @@ static int jsonEachFilter( p->eType = 0; jsonAppendRaw(&p->path, "$", 1); } + p->nRoot = p->path.nUsed; p->nParent = 0; - p->sParse.isBinary = 1; n = jsonbPayloadSize(&p->sParse, i, &sz); p->iEnd = i+n+sz; if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ From 50b37832b250acb3fc55b8f177935ab7af4eef48 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 26 Nov 2023 00:48:37 +0000 Subject: [PATCH 253/347] Same results as the legacy JsonNode implementation on a small set of test cases. FossilOrigin-Name: c3da4b079a1a15a4c0b1a6e71f876648b1d9eb32eddc67b9946c2475c7b6d085 --- manifest | 12 +++++----- manifest.uuid | 2 +- src/json.c | 62 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 17f760ca91..4ae8c18715 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sunused\selements\sfrom\sthe\sjson_tree()\scursor. -D 2023-11-25T23:00:50.622 +C Same\sresults\sas\sthe\slegacy\sJsonNode\simplementation\son\sa\ssmall\sset\sof\stest\scases. +D 2023-11-26T00:48:37.511 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7bd0d3809b31e32ffa053ba68cf03f67121f6bdd0be7031e8d4c761bef9b6aee +F src/json.c b93f282f032f904118121ae64552972f078ac13adcd7fc982d20f596f97a424a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 66c2ab9ebbf90477742e6be0d30e061d827c409de038f2a5b73479ed9448c4a6 -R 79cb22ebedffb4f5b0a0989ee5c920ed +P 914a50117d477b2cd30d58388fb8d1b71ff7ff6842ba025f38efc6e9647d06d0 +R d2ae12035201ed7d53848ee421edc8df U drh -Z b95c9f50e6b9041cf41818958255b068 +Z e4ce705fdf38aad5873f97f479b0490a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a22ec07c45..a5cbb721e5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -914a50117d477b2cd30d58388fb8d1b71ff7ff6842ba025f38efc6e9647d06d0 \ No newline at end of file +c3da4b079a1a15a4c0b1a6e71f876648b1d9eb32eddc67b9946c2475c7b6d085 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1a2a5bd9ef..14fe274146 100644 --- a/src/json.c +++ b/src/json.c @@ -5542,7 +5542,7 @@ struct JsonEachCursor { u32 iRowid; /* The rowid */ u32 i; /* Index in sParse.aBlob[] of current row */ u32 iEnd; /* EOF when i equals or exceeds this value */ - u32 nRoot; /* Size of the root argument in bytes */ + u32 nRoot; /* Size of the root path in bytes */ u8 eType; /* Type of the container for element i */ u8 bRecursive; /* True for json_tree(). False for json_each() */ u32 nParent; /* Current nesting depth */ @@ -5760,20 +5760,44 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ p->eType = 0; } } - if( p->eType==JSONB_ARRAY ){ - assert( p->nParent>0 ); - p->aParent[p->nParent-1].iKey++; - } }else{ u32 n, sz = 0; u32 i = jsonSkipLabel(p); n = jsonbPayloadSize(&p->sParse, i, &sz); p->i = i + n + sz; } + if( p->eType==JSONB_ARRAY ){ + assert( p->nParent>0 ); + p->aParent[p->nParent-1].iKey++; + } p->iRowid++; return SQLITE_OK; } +/* Length of the path for rowid==0 in bRecursive mode. +*/ +static int jsonEachPathLength(JsonEachCursor *p){ + u32 n = p->path.nUsed; + if( p->iRowid==0 && p->bRecursive && n>1 ){ + if( p->path.zBuf[n-1]==']' ){ + do{ + n--; + assert( n>0 ); + }while( p->path.zBuf[n]!='[' ); + }else{ + u32 sz = 0; + jsonbPayloadSize(&p->sParse, p->i, &sz); + if( p->path.zBuf[n-1]=='"' ) sz += 2; + n -= sz; + while( p->path.zBuf[n]!='.' ){ + n--; + assert( n>0 ); + } + } + } + return n; +} + /* Return the value of a column */ static int jsonEachColumn( sqlite3_vtab_cursor *cur, /* The cursor */ @@ -5783,7 +5807,23 @@ static int jsonEachColumn( JsonEachCursor *p = (JsonEachCursor*)cur; switch( iColumn ){ case JEACH_KEY: { - if( p->nParent==0 ) break; + if( p->nParent==0 ){ + u32 n, j; + assert( p->iRowid==0 && p->bRecursive ); + if( p->nRoot==1 ) break; + j = jsonEachPathLength(p); + n = p->nRoot - j; + if( p->path.zBuf[j]=='[' ){ + i64 x; + sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); + sqlite3_result_int64(ctx, x); + }else if( p->path.zBuf[j+1]=='"' ){ + sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT); + }else{ + sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT); + } + break; + } if( p->eType==JSONB_OBJECT ){ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); }else{ @@ -5815,7 +5855,7 @@ static int jsonEachColumn( break; } case JEACH_PARENT: { - if( p->nParent>0 ){ + if( p->nParent>0 && p->bRecursive ){ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); } break; @@ -5829,7 +5869,8 @@ static int jsonEachColumn( break; } case JEACH_PATH: { - sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + u32 n = jsonEachPathLength(p); + sqlite3_result_text64(ctx, p->path.zBuf, n, SQLITE_TRANSIENT, SQLITE_UTF8); break; } @@ -5970,6 +6011,7 @@ static int jsonEachFilter( jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } + p->nRoot = sqlite3_value_bytes(argv[1]); if( zRoot[1]==0 ){ i = p->i = 0; p->eType = 0; @@ -5989,13 +6031,13 @@ static int jsonEachFilter( p->eType = JSONB_ARRAY; } } - jsonAppendRaw(&p->path, zRoot, sqlite3Strlen30(zRoot)); + jsonAppendRaw(&p->path, zRoot, p->nRoot); }else{ i = p->i = 0; p->eType = 0; + p->nRoot = 1; jsonAppendRaw(&p->path, "$", 1); } - p->nRoot = p->path.nUsed; p->nParent = 0; n = jsonbPayloadSize(&p->sParse, i, &sz); p->iEnd = i+n+sz; From 15c0b03c5dcccd8a5c3282b7b961dbfc9fda7760 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 26 Nov 2023 00:56:40 +0000 Subject: [PATCH 254/347] Fix corner-case error conditions. FossilOrigin-Name: ec23d34ab75e1d7e9366e59c633e0d30def8759f6d4717583ebeb4c90aeccf0d --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 6 ++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 4ae8c18715..7736b5bcaf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Same\sresults\sas\sthe\slegacy\sJsonNode\simplementation\son\sa\ssmall\sset\sof\stest\scases. -D 2023-11-26T00:48:37.511 +C Fix\scorner-case\serror\sconditions. +D 2023-11-26T00:56:40.131 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c b93f282f032f904118121ae64552972f078ac13adcd7fc982d20f596f97a424a +F src/json.c 59dd8bf951f0c7b2e1004f406f8690d9743146f176d675da06427f99c75c78f7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 914a50117d477b2cd30d58388fb8d1b71ff7ff6842ba025f38efc6e9647d06d0 -R d2ae12035201ed7d53848ee421edc8df +P c3da4b079a1a15a4c0b1a6e71f876648b1d9eb32eddc67b9946c2475c7b6d085 +R 6a83cb0634a1b413d8dd9f72a2caf803 U drh -Z e4ce705fdf38aad5873f97f479b0490a +Z cf069d3ef952e41c66c8bcc7a7e83b20 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a5cbb721e5..90bef2caad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c3da4b079a1a15a4c0b1a6e71f876648b1d9eb32eddc67b9946c2475c7b6d085 \ No newline at end of file +ec23d34ab75e1d7e9366e59c633e0d30def8759f6d4717583ebeb4c90aeccf0d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 14fe274146..9317608267 100644 --- a/src/json.c +++ b/src/json.c @@ -416,7 +416,7 @@ static void jsonStringReset(JsonString *p){ */ static void jsonStringOom(JsonString *p){ p->eErr |= JSTRING_OOM; - sqlite3_result_error_nomem(p->pCtx); + if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx); jsonStringReset(p); } @@ -5766,8 +5766,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ n = jsonbPayloadSize(&p->sParse, i, &sz); p->i = i + n + sz; } - if( p->eType==JSONB_ARRAY ){ - assert( p->nParent>0 ); + if( p->eType==JSONB_ARRAY && p->nParent ){ p->aParent[p->nParent-1].iKey++; } p->iRowid++; @@ -5809,7 +5808,6 @@ static int jsonEachColumn( case JEACH_KEY: { if( p->nParent==0 ){ u32 n, j; - assert( p->iRowid==0 && p->bRecursive ); if( p->nRoot==1 ) break; j = jsonEachPathLength(p); n = p->nRoot - j; From b4e5bc6cdb3fb282e735706dbceb888ac500ac23 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 27 Nov 2023 12:30:55 +0000 Subject: [PATCH 255/347] All tests passing. FossilOrigin-Name: b5a5660ca22437640c9bf32c44d92c76a7293dafcbaf4fa6a4c171128d64871d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 38 ++++++++++++++++++++++++++++---------- test/json101.test | 8 ++++---- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/manifest b/manifest index 7736b5bcaf..8e410950f6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\scorner-case\serror\sconditions. -D 2023-11-26T00:56:40.131 +C All\stests\spassing. +D 2023-11-27T12:30:55.729 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 59dd8bf951f0c7b2e1004f406f8690d9743146f176d675da06427f99c75c78f7 +F src/json.c efd9ec1cb0f0cbee3e43ebaba69469b3d3f44ee623b53b1ce72361da42e05fbe F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1325,7 +1325,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 2420fbed1d2dddb9562f8b9d0ac540849b99deba74e869560f94f674ef24171c +F test/json101.test 19f9abc77c33151bd7fdb7850fb473cb0154f229b863b740590ad9de807a032d F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2145,8 +2145,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 c3da4b079a1a15a4c0b1a6e71f876648b1d9eb32eddc67b9946c2475c7b6d085 -R 6a83cb0634a1b413d8dd9f72a2caf803 +P ec23d34ab75e1d7e9366e59c633e0d30def8759f6d4717583ebeb4c90aeccf0d +R 6545b38a1b858ef8dae597ee252577f2 U drh -Z cf069d3ef952e41c66c8bcc7a7e83b20 +Z b98cdf654b65fc79957da28b6036dc53 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 90bef2caad..050c01db3c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ec23d34ab75e1d7e9366e59c633e0d30def8759f6d4717583ebeb4c90aeccf0d \ No newline at end of file +b5a5660ca22437640c9bf32c44d92c76a7293dafcbaf4fa6a4c171128d64871d \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9317608267..0e3f6fe575 100644 --- a/src/json.c +++ b/src/json.c @@ -5715,6 +5715,7 @@ static void jsonAppendPathName(JsonEachCursor *p){ /* Advance the cursor to the next element for json_tree() */ static int jsonEachNext(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; + int rc = SQLITE_OK; if( p->bRecursive ){ u8 x; u8 levelChange = 0; @@ -5740,7 +5741,10 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ pParent->iEnd = i + n + sz; pParent->iKey = -1; pParent->nPath = (u32)p->path.nUsed; - if( p->eType && p->nParent ) jsonAppendPathName(p); + if( p->eType && p->nParent ){ + jsonAppendPathName(p); + if( p->path.eErr ) rc = SQLITE_NOMEM; + } p->nParent++; p->i = i + n; }else{ @@ -5770,7 +5774,7 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ p->aParent[p->nParent-1].iKey++; } p->iRowid++; - return SQLITE_OK; + return rc; } /* Length of the path for rowid==0 in bRecursive mode. @@ -5811,7 +5815,9 @@ static int jsonEachColumn( if( p->nRoot==1 ) break; j = jsonEachPathLength(p); n = p->nRoot - j; - if( p->path.zBuf[j]=='[' ){ + if( n==0 ){ + break; + }else if( p->path.zBuf[j]=='[' ){ i64 x; sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); sqlite3_result_int64(ctx, x); @@ -5992,12 +5998,18 @@ static int jsonEachFilter( }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); p->sParse.nJson = sqlite3_value_bytes(argv[0]); - if( p->sParse.zJson==0 || jsonConvertTextToBlob(&p->sParse, 0) ){ + if( p->sParse.zJson==0 ){ + p->i = p->iEnd = 0; + return SQLITE_OK; + } + if( jsonConvertTextToBlob(&p->sParse, 0) ){ if( p->sParse.oom ){ return SQLITE_NOMEM; } - p->i = p->iEnd = 0; - return SQLITE_OK; + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } } if( idxNum==3 ){ @@ -6016,10 +6028,16 @@ static int jsonEachFilter( }else{ i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); if( JSON_BLOB_ISERROR(i) ){ - p->i = 0; - p->eType = 0; - p->iEnd = 0; - return SQLITE_OK; + if( i==JSON_BLOB_NOTFOUND ){ + p->i = 0; + p->eType = 0; + p->iEnd = 0; + return SQLITE_OK; + } + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } if( p->sParse.iLabel ){ p->i = p->sParse.iLabel; diff --git a/test/json101.test b/test/json101.test index 426746377c..280d467393 100644 --- a/test/json101.test +++ b/test/json101.test @@ -925,16 +925,16 @@ do_execsql_test json101-14.170 { # do_execsql_test json101-15.100 { SELECT * FROM JSON_EACH('{"a":1, "b":2}'); -} {a 1 integer 1 2 {} {$.a} {$} b 2 integer 2 4 {} {$.b} {$}} +} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.110 { SELECT xyz.* FROM JSON_EACH('{"a":1, "b":2}') AS xyz; -} {a 1 integer 1 2 {} {$.a} {$} b 2 integer 2 4 {} {$.b} {$}} +} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.120 { SELECT * FROM (JSON_EACH('{"a":1, "b":2}')); -} {a 1 integer 1 2 {} {$.a} {$} b 2 integer 2 4 {} {$.b} {$}} +} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} do_execsql_test json101-15.130 { SELECT xyz.* FROM (JSON_EACH('{"a":1, "b":2}')) AS xyz; -} {a 1 integer 1 2 {} {$.a} {$} b 2 integer 2 4 {} {$.b} {$}} +} {a 1 integer 1 1 {} {$.a} {$} b 2 integer 2 5 {} {$.b} {$}} # 2019-11-10 # Mailing list bug report on the handling of surrogate pairs From 821a4c9f8c681faaa45c131ae2c9be455dd3097e Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 27 Nov 2023 15:57:11 +0000 Subject: [PATCH 256/347] Give the json_valid() function an optional second argument that determines what is meant by "valid". FossilOrigin-Name: a4e19ad43dac81e7655ec03ff69bb99d1d02b0c227034c90fb41415fd4793fe3 --- manifest | 15 +++---- manifest.uuid | 2 +- src/json.c | 111 +++++++++++++++++++++++++++++++++++++--------- test/json101.test | 2 +- 4 files changed, 98 insertions(+), 32 deletions(-) diff --git a/manifest b/manifest index 17d296c97f..188b5f5277 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sthe\sjson_tree()\sand\sjson_each()\svirtual\stables\sover\sto\suse\sJSONB\shas\ntheir\sinternal\srepresentation. -D 2023-11-27T12:36:29.017 +C Give\sthe\sjson_valid()\sfunction\san\soptional\ssecond\sargument\sthat\sdetermines\nwhat\sis\smeant\sby\s"valid". +D 2023-11-27T15:57:11.976 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c efd9ec1cb0f0cbee3e43ebaba69469b3d3f44ee623b53b1ce72361da42e05fbe +F src/json.c 6c1de0d7802a15669a78008125b409b67aa23eefcacfc52436d713419d09be9e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1325,7 +1325,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 19f9abc77c33151bd7fdb7850fb473cb0154f229b863b740590ad9de807a032d +F test/json101.test 643936557daeb2eeeb839e9d87a682c7d7c6f6aee66045eedd8613cb8471d8d8 F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2145,9 +2145,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 ab2644aacf4757a51cf62e05cff6711a0a3605d60502a3dd310887df1b993545 b5a5660ca22437640c9bf32c44d92c76a7293dafcbaf4fa6a4c171128d64871d -R 6545b38a1b858ef8dae597ee252577f2 -T +closed b5a5660ca22437640c9bf32c44d92c76a7293dafcbaf4fa6a4c171128d64871d +P ec18caa3f7790b780dde66c1ccbb6eb09d2f1507629cc45955fc1b08380b4017 +R ae458da46f597856d9232f7ad8297973 U drh -Z 9c0c7326e55f087530f0f53e8712d89c +Z 6c8b34340e171672e2ebcad369756a23 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 189e2ccc7a..c418cd9780 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ec18caa3f7790b780dde66c1ccbb6eb09d2f1507629cc45955fc1b08380b4017 \ No newline at end of file +a4e19ad43dac81e7655ec03ff69bb99d1d02b0c227034c90fb41415fd4793fe3 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0e3f6fe575..1107a24f4c 100644 --- a/src/json.c +++ b/src/json.c @@ -5241,15 +5241,60 @@ static void jsonTypeFunc( /* ** json_valid(JSON) +** json_valid(JSON, FLAGS) ** -** Return 1 if the argument is one of: +** Check the JSON argument to see if it is well-formed. The FLAGS argument +** encodes the various constraints on what is meant by "well-formed": ** -** * A well-formed canonical JSON string according to RFC-8259 -** (without JSON5 enhancements), or +** 0x01 Canonical RFC-8259 JSON text +** 0x02 JSON text with optional JSON-5 extensions +** 0x04 Superficially appears to be JSONB +** 0x08 Strictly well-formed JSONB ** -** * A BLOB that plausibly could be a JSONB value. +** If the FLAGS argument is omitted, it defaults to 1. Useful values for +** FLAGS include: ** -** Return 0 otherwise. +** 1 Strict canonical JSON text +** 2 JSON text perhaps with JSON-5 extensions +** 4 Superficially appears to be JSONB +** 5 Canonical JSON text or superficial JSONB +** 6 JSON-5 text or superficial JSONB +** 8 Strict JSONB +** 9 Canonical JSON text or strict JSONB +** 10 JSON-5 text or strict JSONB +** +** Other flag combinations are redundant. For example, every canonical +** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3 +** are the same. Similarly, any input that passes a strict JSONB validation +** will also pass the superficial validation so 12 through 15 are the same +** as 8 through 11 respectively. +** +** This routine runs in linear time to validate text and when doing strict +** JSONB validation. Superficial JSONB validation is constant time, +** assuming the BLOB is already in memory. The performance advantage +** of superficial JSONB validation is why that option is provided. +** Application developers can choose to do fast superficial validation or +** slower strict validation, according to their specific needs. +** +** Only the lower four bits of the FLAGS argument are currently used. +** Higher bits are reserved for future expansion. To facilitate +** compatibility, the current implementation raises an error if any bit +** in FLAGS is set other than the lower four bits. +** +** The original circa 2015 implementation of the JSON routines in +** SQLite only supported canonical RFC-8259 JSON text and the json_valid() +** function only accepted one argument. That is why the default value +** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only +** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS +** argument was added when the JSON routines were extended to support +** JSON5-like extensions and binary JSONB stored in BLOBs. +** +** Return Values: +** +** * Raise an error if FLAGS is outside the range of 1 to 15. +** * Return NULL if the input is NULL +** * Return 1 if the input is well-formed. +** * Return 0 if the input is not well-formed. */ static void jsonValidFunc( sqlite3_context *ctx, @@ -5257,26 +5302,47 @@ static void jsonValidFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ + u8 flags = 1; + u8 res = 0; + if( argc==2 ){ + i64 f = sqlite3_value_int64(argv[1]); + if( f<1 || f>15 ){ + sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be" + " between 1 and 15", -1); + return; + } + flags = f & 0x0f; + } + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_NULL: { #ifdef SQLITE_LEGACY_JSON_VALID - /* Incorrect legacy behavior was to return FALSE for a NULL input */ - sqlite3_result_int(ctx, 0); + /* Incorrect legacy behavior was to return FALSE for a NULL input */ + sqlite3_result_int(ctx, 0); #endif - return; - } - if( jsonFuncArgMightBeBinary(argv[0]) ){ - sqlite3_result_int(ctx, 1); - return; - } - p = jsonParseCached(ctx, argv[0], 0, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else{ - sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod)); - if( p->nErr ) jsonParseFree(p); + return; + } + case SQLITE_BLOB: { + if( (flags & 0x0c)!=0 && jsonFuncArgMightBeBinary(argv[0]) ){ + /* TO-DO: strict checking if flags & 0x08 */ + res = 1; + } + break; + } + default: { + if( (flags & 0x3)==0 ) break; + p = jsonParseCached(ctx, argv[0], 0, 0); + if( p==0 || p->oom ){ + sqlite3_result_error_nomem(ctx); + sqlite3_free(p); + }else if( p->nErr ){ + jsonParseFree(p); + }else if( (flags & 0x02)!=0 || p->hasNonstd==0 || p->useMod ){ + res = 1; + } + break; + } } + sqlite3_result_int(ctx, res); } /* @@ -6171,6 +6237,7 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc), JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc), JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc), + JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), #if SQLITE_DEBUG JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), JFUNCTION(json_test1, 1,1,0, 1,0,0, jsonTest1Func), diff --git a/test/json101.test b/test/json101.test index 280d467393..ebaf0f3b34 100644 --- a/test/json101.test +++ b/test/json101.test @@ -324,7 +324,7 @@ do_execsql_test json101-5.2 { SELECT id, json_valid(json), json_type(json), '|' FROM j2 ORDER BY id; } {1 1 object | 2 1 object | 3 1 array |} do_execsql_test json101-5.2b { - SELECT id, json_valid(json), json_type(json), '|' FROM j2b ORDER BY id; + SELECT id, json_valid(json,5), json_type(json), '|' FROM j2b ORDER BY id; } {1 1 object | 2 1 object | 3 1 array |} ifcapable !vtab { From 8a3034add8dac022f58706bbcdbd076c624f8d11 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 27 Nov 2023 17:13:18 +0000 Subject: [PATCH 257/347] Enhance the (SQLITE_DEBUG-only) json_parse() routine so that it shows a decoding of JSONB when given a BLOB argument. FossilOrigin-Name: af267868562e0799ad691dccad05f17afbc34d609eede8c55f57d209290246ef --- manifest | 12 +++---- manifest.uuid | 2 +- src/json.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 188b5f5277..0989187da0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Give\sthe\sjson_valid()\sfunction\san\soptional\ssecond\sargument\sthat\sdetermines\nwhat\sis\smeant\sby\s"valid". -D 2023-11-27T15:57:11.976 +C Enhance\sthe\s(SQLITE_DEBUG-only)\sjson_parse()\sroutine\sso\sthat\sit\sshows\sa\s\ndecoding\sof\sJSONB\swhen\sgiven\sa\sBLOB\sargument. +D 2023-11-27T17:13:18.242 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6c1de0d7802a15669a78008125b409b67aa23eefcacfc52436d713419d09be9e +F src/json.c aefd3bf7c225ad03e25680febb2659779a99b227cd98222270e16719b1251bdb F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 ec18caa3f7790b780dde66c1ccbb6eb09d2f1507629cc45955fc1b08380b4017 -R ae458da46f597856d9232f7ad8297973 +P a4e19ad43dac81e7655ec03ff69bb99d1d02b0c227034c90fb41415fd4793fe3 +R 2b39f71294c83f1ae33c269e29617f78 U drh -Z 6c8b34340e171672e2ebcad369756a23 +Z ba17737c2ff9c3cb6da75654e948a46b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c418cd9780..5a42393c10 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a4e19ad43dac81e7655ec03ff69bb99d1d02b0c227034c90fb41415fd4793fe3 \ No newline at end of file +af267868562e0799ad691dccad05f17afbc34d609eede8c55f57d209290246ef \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1107a24f4c..e672921521 100644 --- a/src/json.c +++ b/src/json.c @@ -4490,6 +4490,84 @@ static void jsonDebugPrintNodeEntries( } #endif /* SQLITE_DEBUG */ +#if SQLITE_DEBUG +/* +** Decode JSONB bytes in aBlob[] starting at iStart through but not +** including iEnd. Indent the +** content by nIndent spaces. +*/ +static void jsonDebugPrintBlob( + JsonParse *pParse, /* JSON content */ + u32 iStart, /* Start rendering here */ + u32 iEnd, /* Do not render this byte or any byte after this one */ + int nIndent /* Indent by this many spaces */ +){ + while( iStartaBlob[iStart] & 0x0f; + printf("%5d:%*s", iStart, nIndent, ""); + nn = n = jsonbPayloadSize(pParse, iStart, &sz); + if( nn==0 ) nn = 1; + if( sz>0 && xaBlob[iStart+i]); + if( n==0 || iStart+n+sz>iEnd ){ + printf(" ERROR invalid node size\n"); + iStart = n==0 ? iStart+1 : iEnd; + continue; + } + printf(" <-- "); + switch( x ){ + case JSONB_NULL: printf("null"); break; + case JSONB_TRUE: printf("true"); break; + case JSONB_FALSE: printf("false"); break; + case JSONB_INT: printf("int"); break; + case JSONB_INT5: printf("int5"); break; + case JSONB_FLOAT: printf("float"); break; + case JSONB_FLOAT5: printf("float5"); break; + case JSONB_TEXT: printf("text"); break; + case JSONB_TEXTJ: printf("textj"); break; + case JSONB_TEXT5: printf("text5"); break; + case JSONB_TEXTRAW: printf("textraw"); break; + case JSONB_ARRAY: { + printf("array, %u bytes\n", sz); + jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2); + showContent = 0; + break; + } + case JSONB_OBJECT: { + printf("object, %u bytes\n", sz); + jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2); + showContent = 0; + break; + } + default: { + printf("ERROR: unknown node type\n"); + showContent = 0; + break; + } + } + if( showContent ){ + if( sz==0 && x<=JSON_FALSE ){ + printf("\n"); + }else{ + u32 i; + printf(": \""); + for(i=iStart+n; iaBlob[i]; + if( c<0x20 || c>=0x7f ) c = '.'; + putchar(c); + } + printf("\"\n"); + } + } + iStart += n + sz; + } +} +#endif /* SQLITE_DEBUG */ + #if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */ static void jsonDebugPrintParse(JsonParse *p){ @@ -4520,6 +4598,14 @@ static void jsonParseFunc( JsonParse *p; /* The parse */ assert( argc==1 ); + if( jsonFuncArgMightBeBinary(argv[0]) ){ + JsonParse x; + memset(&x, 0, sizeof(x)); + x.nBlob = sqlite3_value_bytes(argv[0]); + x.aBlob = (u8*)sqlite3_value_blob(argv[0]); + jsonDebugPrintBlob(&x, 0, x.nBlob, 0); + return; + } p = jsonParseCached(ctx, argv[0], ctx, 0); if( p==0 ) return; printf("nNode = %u\n", p->nNode); From e52854a9e6d3afacf60a690c1d31dc256da8cf1f Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 27 Nov 2023 19:22:50 +0000 Subject: [PATCH 258/347] In SQLITE_ENABLE_SETLK_TIMEOUT builds, use blocking locks in place of sleep() when opening a read-transaction. FossilOrigin-Name: a51ef39998e25e86bd0600e71d15011b12e05f4319608018293bdaecb09e8c97 --- manifest | 17 ++++++++++------- manifest.uuid | 2 +- src/os_unix.c | 13 +++++++++++++ src/wal.c | 53 ++++++++++++++++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index bc7b1ca18c..2ccefe87c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s--buildonly\sand\s--dryrun\soptions\sto\stestrunner.tcl. -D 2023-11-24T20:14:18.100 +C In\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds,\suse\sblocking\slocks\sin\splace\sof\ssleep()\swhen\sopening\sa\sread-transaction. +D 2023-11-27T19:22:50.016 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -712,7 +712,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c dc5404b56da7fb13cf272ddb94c3753cf9e82d32a65cba35dbb6aadcb849419c +F src/os_unix.c 97bdcd43315da7aaec9fea2da1ff7c9de458f93dd363e073f2742403a7f2e011 F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 987ab3a2cd9065d62e9955474470ff733445e2357432a67e3d0f5a8f9313e334 @@ -810,7 +810,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8 F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7 F src/vtab.c 154725ebecd3bc02f7fbd7ad3974334f73fff76e02a964e828e48a7c5fb7efff F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 69aa3ce60b2862a24cd86bb528e653e2137388ead258ef64db49ec9038807f5f +F src/wal.c f64bf6799dbbd346a9e2aae9b96e51dbbb222d24a7e7509db85fb0144e8d9f38 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 F src/where.c 1fdc69ce1333e9bd6d7d3df9fa5af1373a3f5bfdd52108d1dbc0ca85a55f777e @@ -2143,8 +2143,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f8ea0b58b37f4052ded448e595d6d2992988a33b8ecfe58d68f20532f8cb5a37 -R a09b448e8fa45e3193b13a1fe5b499b2 +P a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e +R 35e10752e16b85b0bde20be6e18f8d14 +T *branch * blocking-dms-lock +T *sym-blocking-dms-lock * +T -sym-trunk * U dan -Z ce0098ca9d7841ef7ea900cfd937f5a5 +Z fd8676cc431af47934a9227125e3da50 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5863b29096..9745d59d7d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e \ No newline at end of file +a51ef39998e25e86bd0600e71d15011b12e05f4319608018293bdaecb09e8c97 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index dab03c97fb..7362a13206 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4595,7 +4595,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ pShmNode->isUnlocked = 1; rc = SQLITE_READONLY_CANTINIT; }else{ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* Do not use a blocking lock here. If the lock cannot be obtained + ** immediately, it means some other connection is truncating the + ** *-shm file. And after it has done so, it will not release its + ** lock, but only downgrade it to a shared lock. So no point in + ** blocking here. The call below to obtain the shared DMS lock may + ** use a blocking lock. */ + int iSaveTimeout = pDbFd->iBusyTimeout; + pDbFd->iBusyTimeout = 0; +#endif rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + pDbFd->iBusyTimeout = iSaveTimeout; +#endif /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to diff --git a/src/wal.c b/src/wal.c index d83f361d64..c610c14d02 100644 --- a/src/wal.c +++ b/src/wal.c @@ -2004,6 +2004,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + + +/* +** Attempt to enable blocking locks that block for nMs ms. Return 1 if +** blocking locks are successfully enabled, or 0 otherwise. +*/ +static int walEnableBlockingMs(Wal *pWal, int nMs){ + int rc = sqlite3OsFileControl( + pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs + ); + return (rc==SQLITE_OK); +} + /* ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) ** they are supported by the VFS, and (b) the database handle is configured @@ -2015,11 +2028,7 @@ static int walEnableBlocking(Wal *pWal){ if( pWal->db ){ int tmout = pWal->db->busyTimeout; if( tmout ){ - int rc; - rc = sqlite3OsFileControl( - pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout - ); - res = (rc==SQLITE_OK); + res = walEnableBlockingMs(pWal, tmout); } } return res; @@ -2068,20 +2077,10 @@ void sqlite3WalDb(Wal *pWal, sqlite3 *db){ pWal->db = db; } -/* -** Take an exclusive WRITE lock. Blocking if so configured. -*/ -static int walLockWriter(Wal *pWal){ - int rc; - walEnableBlocking(pWal); - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - walDisableBlocking(pWal); - return rc; -} #else # define walEnableBlocking(x) 0 # define walDisableBlocking(x) -# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) +# define walEnableBlockingMs(pWal, ms) 0 # define sqlite3WalDb(pWal, db) #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ @@ -2682,7 +2681,9 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ } }else{ int bWriteLock = pWal->writeLock; - if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ + if( bWriteLock + || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) + ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); @@ -2690,7 +2691,8 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ /* If the wal-index header is still malformed even while holding ** a WRITE lock, it can only mean that the header is corrupted and ** needs to be reconstructed. So run recovery to do exactly that. - */ + ** Disable blocking locks first. */ + walDisableBlocking(pWal); rc = walIndexRecover(pWal); *pChanged = 1; } @@ -2987,6 +2989,17 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ return SQLITE_PROTOCOL; } if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor + ** to block for locks for approximately nDelay us. This affects two + ** locks (a) the WRITER lock taken in walIndexReadHdr() if the first + ** attempted read fails and (b) the shared lock taken on the DMS slot + ** in os_unix.c. Both of these locks are attempted from within the + ** call to walIndexReadHdr() below. */ + if( cnt>=10 && !useWal && walEnableBlockingMs(pWal, (nDelay+999)/1000) ){ + nDelay = 1; + } +#endif sqlite3OsSleep(pWal->pVfs, nDelay); } @@ -2995,6 +3008,10 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ if( pWal->bShmUnreliable==0 ){ rc = walIndexReadHdr(pWal, pChanged); } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + walDisableBlocking(pWal); + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; +#endif if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process ** then convert BUSY errors to WAL_RETRY. If recovery is known to From bae2d0f2ebfa7385890adae075e998e86252b59e Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 27 Nov 2023 20:37:03 +0000 Subject: [PATCH 259/347] Have SQLITE_ENABLE_SETLK_TIMEOUT builds block when locking a read-lock slot. FossilOrigin-Name: f797baf47cf7859cfd8ce248f4f3087af4551a7040af990333426e5a7c269504 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/wal.c | 8 +++++++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 2ccefe87c7..82af9ff683 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds,\suse\sblocking\slocks\sin\splace\sof\ssleep()\swhen\sopening\sa\sread-transaction. -D 2023-11-27T19:22:50.016 +C Have\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds\sblock\swhen\slocking\sa\sread-lock\sslot. +D 2023-11-27T20:37:03.543 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -810,7 +810,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8 F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7 F src/vtab.c 154725ebecd3bc02f7fbd7ad3974334f73fff76e02a964e828e48a7c5fb7efff F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c f64bf6799dbbd346a9e2aae9b96e51dbbb222d24a7e7509db85fb0144e8d9f38 +F src/wal.c 86eaddc56b124de2df14dfaff292ca7bfb862dbd68728105547ddd6a1e799227 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 F src/where.c 1fdc69ce1333e9bd6d7d3df9fa5af1373a3f5bfdd52108d1dbc0ca85a55f777e @@ -2143,11 +2143,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 a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e -R 35e10752e16b85b0bde20be6e18f8d14 -T *branch * blocking-dms-lock -T *sym-blocking-dms-lock * -T -sym-trunk * +P a51ef39998e25e86bd0600e71d15011b12e05f4319608018293bdaecb09e8c97 +R 50c3db5d407d996f614a56ca5cb31c30 U dan -Z fd8676cc431af47934a9227125e3da50 +Z 3f1449733c562fe4e5bdc1215442e17e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9745d59d7d..8a036325f7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a51ef39998e25e86bd0600e71d15011b12e05f4319608018293bdaecb09e8c97 \ No newline at end of file +f797baf47cf7859cfd8ce248f4f3087af4551a7040af990333426e5a7c269504 \ No newline at end of file diff --git a/src/wal.c b/src/wal.c index c610c14d02..a15114710b 100644 --- a/src/wal.c +++ b/src/wal.c @@ -2959,6 +2959,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ u32 mxFrame; /* Wal frame to lock to */ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + int nBlockTmout = 0; +#endif assert( pWal->readLock<0 ); /* Not currently locked */ @@ -2996,7 +2999,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** attempted read fails and (b) the shared lock taken on the DMS slot ** in os_unix.c. Both of these locks are attempted from within the ** call to walIndexReadHdr() below. */ - if( cnt>=10 && !useWal && walEnableBlockingMs(pWal, (nDelay+999)/1000) ){ + nBlockTmout = (nDelay+998) / 1000; + if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){ nDelay = 1; } #endif @@ -3126,7 +3130,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } + (void)walEnableBlockingMs(pWal, nBlockTmout); rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); + walDisableBlocking(pWal); if( rc ){ return rc==SQLITE_BUSY ? WAL_RETRY : rc; } From eb04a0bb7b7bb111adb0ab2554445998244b7002 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 27 Nov 2023 23:46:12 +0000 Subject: [PATCH 260/347] Add untested (#ifdefed-out) code for the MergePatch algorithm against JSONB. Add (and test) the jsonBlobEdit() routine that is needed by the new MergePatch. FossilOrigin-Name: 4d353387fc10e1038cfdd86e66007bf728c231a928e588897bbee0fbfe76f225 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 240 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 224 insertions(+), 30 deletions(-) diff --git a/manifest b/manifest index 0989187da0..6ae9cbf521 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\s(SQLITE_DEBUG-only)\sjson_parse()\sroutine\sso\sthat\sit\sshows\sa\s\ndecoding\sof\sJSONB\swhen\sgiven\sa\sBLOB\sargument. -D 2023-11-27T17:13:18.242 +C Add\suntested\s(#ifdefed-out)\scode\sfor\sthe\sMergePatch\salgorithm\sagainst\sJSONB.\nAdd\s(and\stest)\sthe\sjsonBlobEdit()\sroutine\sthat\sis\sneeded\sby\sthe\snew\sMergePatch. +D 2023-11-27T23:46:12.954 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c aefd3bf7c225ad03e25680febb2659779a99b227cd98222270e16719b1251bdb +F src/json.c 7d6387920736cd9b78a4211771265c4c574284613d8e63f43b3ce7bc160da498 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 a4e19ad43dac81e7655ec03ff69bb99d1d02b0c227034c90fb41415fd4793fe3 -R 2b39f71294c83f1ae33c269e29617f78 +P af267868562e0799ad691dccad05f17afbc34d609eede8c55f57d209290246ef +R 47ffcc493583e45ee7703e45cf1805e3 U drh -Z ba17737c2ff9c3cb6da75654e948a46b +Z 2cedbcb9affb9f675b8d832bfea283a4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5a42393c10..a4814c0752 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -af267868562e0799ad691dccad05f17afbc34d609eede8c55f57d209290246ef \ No newline at end of file +4d353387fc10e1038cfdd86e66007bf728c231a928e588897bbee0fbfe76f225 \ No newline at end of file diff --git a/src/json.c b/src/json.c index e672921521..99205decf1 100644 --- a/src/json.c +++ b/src/json.c @@ -3801,6 +3801,41 @@ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ jsonBlobChangePayloadSize(pParse, iRoot, sz); } +/* +** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of +** content beginning at iDel, and replacing them with nIns bytes of +** content given by aIns. +** +** nDel may be zero, in which case no bytes are removed. But iDel is +** still important as new bytes will be insert beginning at iDel. +** +** nIns may be zero, in which case no new bytes are inserted. aIns might +** be a NULL pointer in this case. +** +** Set pParse->oom if an OOM occurs. +*/ +static void jsonBlobEdit( + JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */ + u32 iDel, /* First byte to be removed */ + u32 nDel, /* Number of bytes to remove */ + const u8 *aIns, /* Content to insert */ + u32 nIns /* Bytes of content to insert */ +){ + i64 d = (i64)nIns - (i64)nDel; + if( d!=0 ){ + if( pParse->nBlob + d > pParse->nBlobAlloc ){ + jsonBlobExpand(pParse, pParse->nBlob+d); + if( pParse->oom ) return; + } + memmove(&pParse->aBlob[iDel+nIns], + &pParse->aBlob[iDel+nDel], + pParse->nBlob - (iDel+nDel)); + pParse->nBlob += d; + pParse->delta += d; + } + if( nIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); +} + /* ** Error returns from jsonLookupBlobStep() */ @@ -3834,27 +3869,12 @@ static u32 jsonLookupBlobStep( sz += iRoot - iLabel; iRoot = iLabel; } - memmove(&pParse->aBlob[iRoot], &pParse->aBlob[iRoot+sz], - pParse->nBlob - (iRoot+sz)); - pParse->delta = -(int)sz; - pParse->nBlob -= sz; + jsonBlobEdit(pParse, iRoot, sz, 0, 0); }else if( pParse->eEdit==JEDIT_INS ){ /* Already exists, so json_insert() is a no-op */ }else{ /* json_set() or json_replace() */ - int d = (int)pParse->nIns - (int)sz; - pParse->delta = d; - if( d!=0 ){ - if( pParse->nBlob + d > pParse->nBlobAlloc ){ - jsonBlobExpand(pParse, pParse->nBlob+d); - if( pParse->oom ) return iRoot; - } - memmove(&pParse->aBlob[iRoot+pParse->nIns], - &pParse->aBlob[iRoot+sz], - pParse->nBlob - iRoot - sz); - pParse->nBlob += d; - } - memcpy(&pParse->aBlob[iRoot], pParse->aIns, pParse->nIns); + jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); } } pParse->iLabel = iLabel; @@ -3920,6 +3940,8 @@ static u32 jsonLookupBlobStep( ix.nBlobAlloc = sizeof(aLabel); jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey) ){ + /* This is similar to jsonBlobEdit() except that the inserted + ** bytes come from two different places, ix.aBlob and pParse->aBlob. */ nIns = ix.nBlob + nKey + pParse->nIns; assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); memmove(&pParse->aBlob[j+nIns], &pParse->aBlob[j], @@ -3984,12 +4006,7 @@ static u32 jsonLookupBlobStep( if( pParse->eEdit>=JEDIT_INS && jsonBlobMakeEditable(pParse, 0) ){ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); - assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); - memmove(&pParse->aBlob[j+pParse->nIns], &pParse->aBlob[j], - pParse->nBlob - j); - memcpy(&pParse->aBlob[j], pParse->aIns, pParse->nIns); - pParse->delta = pParse->nIns; - pParse->nBlob += pParse->nIns; + jsonBlobEdit(pParse, j, 0, pParse->aIns, pParse->nIns); jsonAfterEditSizeAdjust(pParse, iRoot); return j; } @@ -4284,6 +4301,7 @@ static void jsonRemoveFromBlob( return; /* return NULL if $ is removed */ } px.eEdit = JEDIT_DEL; + px.delta = 0; rc = jsonLookupBlobStep(&px, 0, zPath+1, 0); if( rc==JSON_BLOB_NOTFOUND ) continue; if( JSON_BLOB_ISERROR(rc) ) goto jsonRemoveFromBlob_patherror; @@ -4420,6 +4438,7 @@ static void jsonInsertIntoBlob( px.eEdit = eEdit; px.nIns = ax.nBlob; px.aIns = ax.aBlob; + px.delta = 0; rc = jsonLookupBlobStep(&px, 0, zPath+1, 0); jsonParseReset(&ax); if( rc==JSON_BLOB_NOTFOUND ) continue; @@ -4979,6 +4998,181 @@ static JsonNode *jsonMergePatch( return pTarget; } + +#if 0 +/* +** Return codes for jsonMergePatchBlob() +*/ +#define JSON_MERGE_OK 0 /* Success */ +#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ +#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ +#define JSON_MERGE_OOM 3 /* Out-of-memory condition */ + +/* +** RFC-7396 MergePatch for two JSONB blobs. +** +** pTarget is the target. pPatch is the patch. The target is updated +** in place. The patch is read-only. +** +** The original RFC-7396 algorithm is this: +** +** define MergePatch(Target, Patch): +** if Patch is an Object: +** if Target is not an Object: +** Target = {} # Ignore the contents and set it to an empty Object +** for each Name/Value pair in Patch: +** if Value is null: +** if Name exists in Target: +** remove the Name/Value pair from Target +** else: +** Target[Name] = MergePatch(Target[Name], Value) +** return Target +** else: +** return Patch +** +** Here is the same algorithm restrictured to show the actual +** implementation: +** +** 01 define MergePatch(Target, Patch): +** 02 if Patch is not an Object: +** 03 return Patch +** 04 else: // if Patch is an Object: +** 05 if Target is not an Object: +** 06 Target = {} +** 07 for each Name/Value pair in Patch: +** 08 if Name exists in Target: +** 09 if Value is null: +** 10 remove the Name/Value pair from Target +** 11 else +** 12 Target[name] = MergePatch(Target[Name], Value) +** 13 else if Value is not NULL: +** 14 Target[name] = RemoveNullVAlues(Value) +** 15 return Target +** | +** ^---- Line numbers referenced in comments in the implementation +*/ +static int jsonMergePatchBlob( + JsonParse *pTarget, /* The JSON parser that contains the TARGET */ + u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ + const JsonParse *pPatch /* The PATCH */ + u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ +){ + u8 x; /* Type of a single node */ + u32 n, sz=0; /* Return values from jsonbPayloadSize() */ + u32 iTCursor; /* Cursor position while scanning the target object */ + u32 iTStart; /* First label in the target object */ + u32 iTEndBE; /* Original first byte past end of target, before edit */ + u32 iTEnd; /* Current first byte past end of target */ + u32 iTLabel; /* Index of the label */ + u32 nTLabel; /* Header size in bytes for the target label */ + u32 szTLabel; /* Size of the target label payload */ + u32 iTValue; /* Index of the target value */ + u32 nTValue; /* Header size of the target value */ + u32 szTValue; /* Payload size for the target value */ + + u32 iPCursor; /* Cursor position while scanning the patch */ + u32 iPEnd; /* First byte past the end of the patch */ + u32 iPLabel; /* Start of patch label */ + u32 nPLabel; /* Size of header on the patch label */ + u32 szPLabel; /* Payload size of the patch label */ + u32 iPValue; /* Start of patch value */ + u32 nPValue; /* Header size for the patch value */ + u32 szPValue; /* Payload size of the patch value */ + + assert( iTarget>=0 && iTargetnBlob ); + assert( iPatch>=0 && iPatchnBlob ); + x = pPatch->aBlob[iPatch] & 0x0f; + if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */ + u32 szPatch; /* Total size of the patch, header+payload */ + u32 szTarget; /* Total size of the target, header+payload */ + n = jsonbPayloadSize(pPatch, iPatch, &sz); + szPatch = n+sz; + sz = 0; + n = jsonbPayloadSize(pTarget, iTarget, &sz); + szTarget = n+sz; + jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch); + return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */ + } + x = pTarget->aBlob[iTarget] & 0x0f; + if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ + static const u8 emptyObject = { JSONB_OBJECT }; + n = jsonbPayloadSize(pTarget, iTarget, &sz); + jsonBlobEdit(pTarget, iTarget, szTarget, emptyObject, 1); /* Line 06 */ + } + n = jsonbPayloadSize(pPatch, iPatch, &sz); + if( n==0 ) return JSON_MERGE_BADPATCH; + iPCursor = iPatch+n; + iPEnd = iPCursor+sz; + n = jsonbPayloadSize(pTarget, iTarget, &sz); + if( n==0 ) return JSON_MERGE_BADTARGET; + iTStart = iTarget+n; + iTEndBE = iTStart+sz; + + while( iPCursoraBlob[iPCursor] & 0x0f; + if( xJSONB_TEXTRAW ) return JSON_MERGE_BADPATCH; + nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); + if( nPLabel==0 ) return JSON_MERGE_BADPATCH; + iPValue = iPCursor + nPLabel + szPLabel; + if( iPCursor>=iPEnd ) return JSON_MERGE_BADPATCH; + nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); + if( nPValue==0 ) return JSON_MERGE_BADPATCH; + iPCursor = iPValue + nPValue + szPValue; + if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH; + + iTCursor = iTStart; + iTEnd = iTEndBE + pTarget->delta; + while( iTCursoraBlob[iTCursor] & 0x0f; + if( xJSONB_TEXTRAW ) return JSON_MERGE_BADTARGET; + nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); + if( nTLabel==0 ) return JSON_MERGE_BADTARGET; + iTValue = iTLabel + nTLabel + szTLabel; + if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET; + nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); + if( nTValue==0 ) return JSON_MERGE_BADTARGET; + if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; + if( eTLabel==ePLabel ){ + if( szTLabel==szPLabel + && memcmp(&pTarget->aBlob[iTLabel+nTLabel], + &pPatch->aBlob[iPLabel+nPLabel], szTLabel)==0 + ){ + break; /* Labels match. */ + } + }else{ + if( jsonLabelEqual(pTarget, iTLabel, pPatch, iPLabel) ) break; + } + iTCursor = iTValue + nTValue + szTValue; + } + x = pPatch->aBlob[iPValue] & 0x0f; + if( iTCursoroom ) return JSON_MERGE_OOM; + }else{ + /* Algorithm line 12 */ + int rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, pPValue); + if( rc ) return rc; + } + }else if( x>0 ){ /* Algorithm line 13 */ + /* No match and patch value is not NULL */ + jsonBlobEdit(pTarget, iTEnd, 0, + pPatch->aBlob+iPValue, szPValue+nPValue); + if( pTarget->oom ) return JSON_MERGE_OOM; + jsonBlobRemoveNullsFromObject(pTarget, iTEnd); + } + } + jsonAfterEditSizeAdjust(pTarget, iTarget); + return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; +} +#endif + + /* ** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON ** object that is the result of running the RFC 7396 MergePatch() algorithm From f46f89df975100808dc096be69c577206a587123 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 00:27:58 +0000 Subject: [PATCH 261/347] More aggressive use of jsonBlobEdit(). Improvements to the MergePatch implementation sketch. FossilOrigin-Name: fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 41 ++++++++++++++++++++++++----------------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 6ae9cbf521..eb129dd02f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\suntested\s(#ifdefed-out)\scode\sfor\sthe\sMergePatch\salgorithm\sagainst\sJSONB.\nAdd\s(and\stest)\sthe\sjsonBlobEdit()\sroutine\sthat\sis\sneeded\sby\sthe\snew\sMergePatch. -D 2023-11-27T23:46:12.954 +C More\saggressive\suse\sof\sjsonBlobEdit().\s\sImprovements\sto\sthe\sMergePatch\nimplementation\ssketch. +D 2023-11-28T00:27:58.169 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 7d6387920736cd9b78a4211771265c4c574284613d8e63f43b3ce7bc160da498 +F src/json.c a91647a3c56298ff94bb2d5a989e8e329c4435e3a0ae9760b04b46c7b2083541 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 af267868562e0799ad691dccad05f17afbc34d609eede8c55f57d209290246ef -R 47ffcc493583e45ee7703e45cf1805e3 +P 4d353387fc10e1038cfdd86e66007bf728c231a928e588897bbee0fbfe76f225 +R 42f4ec3bb9c19f358b256294966169e5 U drh -Z 2cedbcb9affb9f675b8d832bfea283a4 +Z 551901455bde8067c499befcab52664b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a4814c0752..6460236f38 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4d353387fc10e1038cfdd86e66007bf728c231a928e588897bbee0fbfe76f225 \ No newline at end of file +fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 99205decf1..0138db5183 100644 --- a/src/json.c +++ b/src/json.c @@ -3809,8 +3809,8 @@ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ ** nDel may be zero, in which case no bytes are removed. But iDel is ** still important as new bytes will be insert beginning at iDel. ** -** nIns may be zero, in which case no new bytes are inserted. aIns might -** be a NULL pointer in this case. +** aIns may be zero, in which case space is created to hold nIns bytes +** beginning at iDel, but that space is uninitialized. ** ** Set pParse->oom if an OOM occurs. */ @@ -3833,7 +3833,7 @@ static void jsonBlobEdit( pParse->nBlob += d; pParse->delta += d; } - if( nIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); + if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); } /* @@ -3940,19 +3940,14 @@ static u32 jsonLookupBlobStep( ix.nBlobAlloc = sizeof(aLabel); jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey) ){ - /* This is similar to jsonBlobEdit() except that the inserted - ** bytes come from two different places, ix.aBlob and pParse->aBlob. */ nIns = ix.nBlob + nKey + pParse->nIns; + jsonBlobEdit(pParse, j, 0, 0, nIns); assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); - memmove(&pParse->aBlob[j+nIns], &pParse->aBlob[j], - pParse->nBlob - j); memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); k += nKey; memcpy(&pParse->aBlob[k], pParse->aIns, pParse->nIns); - pParse->delta = nIns; - pParse->nBlob += nIns; jsonAfterEditSizeAdjust(pParse, iRoot); } return j; @@ -5030,13 +5025,13 @@ static JsonNode *jsonMergePatch( ** else: ** return Patch ** -** Here is the same algorithm restrictured to show the actual +** Here is an equivalent algorithm restructured to show the actual ** implementation: ** ** 01 define MergePatch(Target, Patch): ** 02 if Patch is not an Object: ** 03 return Patch -** 04 else: // if Patch is an Object: +** 04 else: // if Patch is an Object ** 05 if Target is not an Object: ** 06 Target = {} ** 07 for each Name/Value pair in Patch: @@ -5046,8 +5041,11 @@ static JsonNode *jsonMergePatch( ** 11 else ** 12 Target[name] = MergePatch(Target[Name], Value) ** 13 else if Value is not NULL: -** 14 Target[name] = RemoveNullVAlues(Value) -** 15 return Target +** 14 if Value is not an Object: +** 15 Target[name] = Value +** 16 else: +** 17 Target[name] = MergePatch('{}',value) +** 18 return Target ** | ** ^---- Line numbers referenced in comments in the implementation */ @@ -5161,10 +5159,19 @@ static int jsonMergePatchBlob( } }else if( x>0 ){ /* Algorithm line 13 */ /* No match and patch value is not NULL */ - jsonBlobEdit(pTarget, iTEnd, 0, - pPatch->aBlob+iPValue, szPValue+nPValue); - if( pTarget->oom ) return JSON_MERGE_OOM; - jsonBlobRemoveNullsFromObject(pTarget, iTEnd); + if( pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ + jsonBlobEdit(pTarget, iTEnd, 0, + pPatch->aBlob+iPValue, szPValue+nPValue, 0, 0); + if( pTarget->oom ) return JSON_MERGE_OOM; + }else{ + u32 szNew = szPLabel+nPLabel; + jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); + if( pTarget->oom ) return JSON_MERGE_OOM; + memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); + pTarget->aBlob[iTEnd+szNew] = 0x00; + rc = jsonMergePatch(pTarget, iTEnd+szNew, pPatch, iPValue); + if( rc ) return rc; + } } } jsonAfterEditSizeAdjust(pTarget, iTarget); From 5026ddb83d04aeca4f4ec487c572e27b2c87d6b7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 12:28:28 +0000 Subject: [PATCH 262/347] The json_patch() code for JSONB compiles and works sometimes, but there are still issues. Incremental check-in. FossilOrigin-Name: e0099464a0045a04f4ccf29bc2b8325fc8c7f39ccf4847e74818f928c9153588 --- manifest | 15 ++++--- manifest.uuid | 2 +- src/json.c | 106 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/manifest b/manifest index eb129dd02f..3e1841dc0f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\saggressive\suse\sof\sjsonBlobEdit().\s\sImprovements\sto\sthe\sMergePatch\nimplementation\ssketch. -D 2023-11-28T00:27:58.169 +C The\sjson_patch()\scode\sfor\sJSONB\scompiles\sand\sworks\ssometimes,\sbut\sthere\sare\nstill\sissues.\s\sIncremental\scheck-in. +D 2023-11-28T12:28:28.510 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a91647a3c56298ff94bb2d5a989e8e329c4435e3a0ae9760b04b46c7b2083541 +F src/json.c 2dfbca7572f686feea28665d524a34cdde23cf3dcb15cfa79cabe77f8032524f F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4d353387fc10e1038cfdd86e66007bf728c231a928e588897bbee0fbfe76f225 -R 42f4ec3bb9c19f358b256294966169e5 +P fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 +R 74a90e7c3058a227c8ae29f97ecb7181 +T *branch * jsonb-patch +T *sym-jsonb-patch * +T -sym-jsonb * U drh -Z 551901455bde8067c499befcab52664b +Z 73c44f2da8d785efd9b8e3ab2061af48 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6460236f38..477eefc522 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 \ No newline at end of file +e0099464a0045a04f4ccf29bc2b8325fc8c7f39ccf4847e74818f928c9153588 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0138db5183..6f5021aafe 100644 --- a/src/json.c +++ b/src/json.c @@ -380,7 +380,7 @@ static int jsonParseAddNode(JsonParse*,u32,u32,const char*); static int jsonXlateBlobToNode(JsonParse *pParse, u32 i); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); -static u32 jsonXlateBlobToText(JsonParse*,u32,JsonString*); +static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); /************************************************************************** ** Utility routines for dealing with JsonString objects @@ -2558,13 +2558,12 @@ static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ u32 nSize; if( pParse->nBlobAlloc>0 ) return 1; aOld = pParse->aBlob; - nSize = pParse->nBlob + pParse->nIns + nExtra; - if( nSize>100 ) nSize -= 100; + nSize = pParse->nBlob + nExtra; pParse->aBlob = 0; if( jsonBlobExpand(pParse, nSize) ){ return 0; } - assert( pParse->nBlobAlloc >= pParse->nBlob + pParse->nIns ); + assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra ); memcpy(pParse->aBlob, aOld, pParse->nBlob); return 1; } @@ -3248,7 +3247,7 @@ static void jsonReturnStringAsBlob(JsonString *pStr){ ** payload size in to *pSz. It returns the offset from i to the ** beginning of the payload. Return 0 on error. */ -static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ +static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; @@ -3306,7 +3305,7 @@ static u32 jsonbPayloadSize(JsonParse *pParse, u32 i, u32 *pSz){ ** The pOut->eErr JSTRING_OOM flag is set on a OOM. */ static u32 jsonXlateBlobToText( - JsonParse *pParse, /* the complete parse of the JSON */ + const JsonParse *pParse, /* the complete parse of the JSON */ u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ ){ @@ -3861,7 +3860,7 @@ static u32 jsonLookupBlobStep( u8 x; if( zPath[0]==0 ){ - if( pParse->eEdit && jsonBlobMakeEditable(pParse, 0) ){ + if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ n = jsonbPayloadSize(pParse, iRoot, &sz); sz += n; if( pParse->eEdit==JEDIT_DEL ){ @@ -3939,7 +3938,7 @@ static u32 jsonLookupBlobStep( ix.aBlob = aLabel; ix.nBlobAlloc = sizeof(aLabel); jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); - if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey) ){ + if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+pParse->nIns) ){ nIns = ix.nBlob + nKey + pParse->nIns; jsonBlobEdit(pParse, j, 0, 0, nIns); assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); @@ -3998,7 +3997,9 @@ static u32 jsonLookupBlobStep( } if( j>iEnd ) return JSON_BLOB_ERROR; if( k>1 ) return JSON_BLOB_NOTFOUND; - if( pParse->eEdit>=JEDIT_INS && jsonBlobMakeEditable(pParse, 0) ){ + if( pParse->eEdit>=JEDIT_INS + && jsonBlobMakeEditable(pParse, pParse->nIns) + ){ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); jsonBlobEdit(pParse, j, 0, pParse->aIns, pParse->nIns); @@ -4994,7 +4995,6 @@ static JsonNode *jsonMergePatch( } -#if 0 /* ** Return codes for jsonMergePatchBlob() */ @@ -5052,7 +5052,7 @@ static JsonNode *jsonMergePatch( static int jsonMergePatchBlob( JsonParse *pTarget, /* The JSON parser that contains the TARGET */ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ - const JsonParse *pPatch /* The PATCH */ + const JsonParse *pPatch, /* The PATCH */ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ ){ u8 x; /* Type of a single node */ @@ -5061,6 +5061,7 @@ static int jsonMergePatchBlob( u32 iTStart; /* First label in the target object */ u32 iTEndBE; /* Original first byte past end of target, before edit */ u32 iTEnd; /* Current first byte past end of target */ + u8 eTLabel; /* Node type of the target label */ u32 iTLabel; /* Index of the label */ u32 nTLabel; /* Header size in bytes for the target label */ u32 szTLabel; /* Size of the target label payload */ @@ -5070,6 +5071,7 @@ static int jsonMergePatchBlob( u32 iPCursor; /* Cursor position while scanning the patch */ u32 iPEnd; /* First byte past the end of the patch */ + u8 ePLabel; /* Node type of the patch label */ u32 iPLabel; /* Start of patch label */ u32 nPLabel; /* Size of header on the patch label */ u32 szPLabel; /* Payload size of the patch label */ @@ -5093,9 +5095,9 @@ static int jsonMergePatchBlob( } x = pTarget->aBlob[iTarget] & 0x0f; if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ - static const u8 emptyObject = { JSONB_OBJECT }; + static const u8 emptyObject[] = { JSONB_OBJECT }; n = jsonbPayloadSize(pTarget, iTarget, &sz); - jsonBlobEdit(pTarget, iTarget, szTarget, emptyObject, 1); /* Line 06 */ + jsonBlobEdit(pTarget, iTarget, sz, emptyObject, 1); /* Line 06 */ } n = jsonbPayloadSize(pPatch, iPatch, &sz); if( n==0 ) return JSON_MERGE_BADPATCH; @@ -5108,8 +5110,10 @@ static int jsonMergePatchBlob( while( iPCursoraBlob[iPCursor] & 0x0f; - if( xJSONB_TEXTRAW ) return JSON_MERGE_BADPATCH; + ePLabel = pPatch->aBlob[iPCursor] & 0x0f; + if( ePLabelJSONB_TEXTRAW ){ + return JSON_MERGE_BADPATCH; + } nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); if( nPLabel==0 ) return JSON_MERGE_BADPATCH; iPValue = iPCursor + nPLabel + szPLabel; @@ -5123,8 +5127,10 @@ static int jsonMergePatchBlob( iTEnd = iTEndBE + pTarget->delta; while( iTCursoraBlob[iTCursor] & 0x0f; - if( xJSONB_TEXTRAW ) return JSON_MERGE_BADTARGET; + eTLabel = pTarget->aBlob[iTCursor] & 0x0f; + if( eTLabelJSONB_TEXTRAW ){ + return JSON_MERGE_BADTARGET; + } nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); if( nTLabel==0 ) return JSON_MERGE_BADTARGET; iTValue = iTLabel + nTLabel + szTLabel; @@ -5133,6 +5139,7 @@ static int jsonMergePatchBlob( if( nTValue==0 ) return JSON_MERGE_BADTARGET; if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; if( eTLabel==ePLabel ){ + /* Common case */ if( szTLabel==szPLabel && memcmp(&pTarget->aBlob[iTLabel+nTLabel], &pPatch->aBlob[iPLabel+nPLabel], szTLabel)==0 @@ -5140,7 +5147,23 @@ static int jsonMergePatchBlob( break; /* Labels match. */ } }else{ - if( jsonLabelEqual(pTarget, iTLabel, pPatch, iPLabel) ) break; + /* Should rarely happen */ + JsonString s1, s2; + int isEqual; + jsonStringInit(&s1, 0); + jsonXlateBlobToText(pTarget, iTLabel, &s1); + if( s1.eErr ) return JSON_MERGE_OOM; + jsonStringInit(&s2, 0); + jsonXlateBlobToText(pPatch, iPLabel, &s2); + if( s2.eErr ) return JSON_MERGE_OOM; + if( s1.nUsed==s2.nUsed && memcmp(s1.zBuf, s2.zBuf, s1.nUsed)==0 ){ + isEqual = 1; + }else{ + isEqual = 0; + } + jsonStringReset(&s1); + jsonStringReset(&s2); + if( isEqual ) break; } iTCursor = iTValue + nTValue + szTValue; } @@ -5154,22 +5177,23 @@ static int jsonMergePatchBlob( if( pTarget->oom ) return JSON_MERGE_OOM; }else{ /* Algorithm line 12 */ - int rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, pPValue); + int rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, iPValue); if( rc ) return rc; } }else if( x>0 ){ /* Algorithm line 13 */ /* No match and patch value is not NULL */ - if( pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ + if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ jsonBlobEdit(pTarget, iTEnd, 0, - pPatch->aBlob+iPValue, szPValue+nPValue, 0, 0); + pPatch->aBlob+iPValue, szPValue+nPValue); if( pTarget->oom ) return JSON_MERGE_OOM; }else{ + int rc; u32 szNew = szPLabel+nPLabel; jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); if( pTarget->oom ) return JSON_MERGE_OOM; memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); pTarget->aBlob[iTEnd+szNew] = 0x00; - rc = jsonMergePatch(pTarget, iTEnd+szNew, pPatch, iPValue); + rc = jsonMergePatchBlob(pTarget, iTEnd+szNew,pPatch,iPValue); if( rc ) return rc; } } @@ -5177,7 +5201,6 @@ static int jsonMergePatchBlob( jsonAfterEditSizeAdjust(pTarget, iTarget); return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; } -#endif /* @@ -5195,6 +5218,43 @@ static void jsonPatchFunc( JsonNode *pResult; /* The result of the merge */ UNUSED_PARAMETER(argc); + if( jsonFuncArgMightBeBinary(argv[0]) + && jsonFuncArgMightBeBinary(argv[1]) + ){ + JsonParse target, patch; + sqlite3 *db; + int rc; + memset(&target, 0, sizeof(target)); + memset(&patch, 0, sizeof(patch)); + target.aBlob = (u8*)sqlite3_value_blob(argv[0]); + target.nBlob = sqlite3_value_bytes(argv[0]); + patch.aBlob = (u8*)sqlite3_value_blob(argv[1]); + patch.nBlob = sqlite3_value_bytes(argv[1]); + db = sqlite3_context_db_handle(ctx); + if( db->mallocFailed ) return; + if( !jsonBlobMakeEditable(&target, patch.nBlob) ) return; + rc = jsonMergePatchBlob(&target, 0, &patch, 0); + if( rc ){ + if( rc==JSON_MERGE_OOM ){ + sqlite3_result_error_nomem(ctx); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + jsonParseReset(&target); + }else{ + int flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( flgs & JSON_BLOB ){ + sqlite3_result_blob(ctx, target.aBlob, target.nBlob, SQLITE_DYNAMIC); + }else{ + JsonString s; + jsonStringInit(&s, ctx); + jsonXlateBlobToText(&target, 0, &s); + jsonReturnString(&s); + jsonParseReset(&target); + } + } + return; + } pX = jsonParseCached(ctx, argv[0], ctx, 1); if( pX==0 ) return; assert( pX->hasMod==0 ); From ec1f59f0cdf988c2b4d66d0c8662832a64a60dfa Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 13:35:53 +0000 Subject: [PATCH 263/347] All legacy tests are passing. FossilOrigin-Name: 2c436806b8d5f57de99c00f6154b038454fb9ae427d00d7b4a46ab9c7c69bcb9 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/manifest b/manifest index 3e1841dc0f..0d87ba4a36 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sjson_patch()\scode\sfor\sJSONB\scompiles\sand\sworks\ssometimes,\sbut\sthere\sare\nstill\sissues.\s\sIncremental\scheck-in. -D 2023-11-28T12:28:28.510 +C All\slegacy\stests\sare\spassing. +D 2023-11-28T13:35:53.633 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 2dfbca7572f686feea28665d524a34cdde23cf3dcb15cfa79cabe77f8032524f +F src/json.c fa3a1b6f2432848dc03448d359a07040f7e4a44d868e9e16512c6066b65aeef9 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,11 +2145,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 fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 -R 74a90e7c3058a227c8ae29f97ecb7181 -T *branch * jsonb-patch -T *sym-jsonb-patch * -T -sym-jsonb * +P e0099464a0045a04f4ccf29bc2b8325fc8c7f39ccf4847e74818f928c9153588 +R e5b38e7d70999e4556fdfbbe91a82c08 U drh -Z 73c44f2da8d785efd9b8e3ab2061af48 +Z 686f56079312574c393270803643d299 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 477eefc522..7c28bb5a17 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e0099464a0045a04f4ccf29bc2b8325fc8c7f39ccf4847e74818f928c9153588 \ No newline at end of file +2c436806b8d5f57de99c00f6154b038454fb9ae427d00d7b4a46ab9c7c69bcb9 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6f5021aafe..bdc9087323 100644 --- a/src/json.c +++ b/src/json.c @@ -5177,24 +5177,32 @@ static int jsonMergePatchBlob( if( pTarget->oom ) return JSON_MERGE_OOM; }else{ /* Algorithm line 12 */ - int rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, iPValue); + int rc, savedDelta = pTarget->delta; + pTarget->delta = 0; + rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, iPValue); if( rc ) return rc; + pTarget->delta += savedDelta; } }else if( x>0 ){ /* Algorithm line 13 */ /* No match and patch value is not NULL */ + u32 szNew = szPLabel+nPLabel; if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ - jsonBlobEdit(pTarget, iTEnd, 0, - pPatch->aBlob+iPValue, szPValue+nPValue); + jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew); if( pTarget->oom ) return JSON_MERGE_OOM; + memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); + memcpy(&pTarget->aBlob[iTEnd+szNew], + &pPatch->aBlob[iPValue], szPValue+nPValue); }else{ - int rc; - u32 szNew = szPLabel+nPLabel; + int rc, savedDelta; jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); if( pTarget->oom ) return JSON_MERGE_OOM; memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); pTarget->aBlob[iTEnd+szNew] = 0x00; + savedDelta = pTarget->delta; + pTarget->delta = 0; rc = jsonMergePatchBlob(pTarget, iTEnd+szNew,pPatch,iPValue); if( rc ) return rc; + pTarget->delta += savedDelta; } } } From b8950e0f45656a96c09d5721e564fa0b8ef8e79a Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 28 Nov 2023 15:29:04 +0000 Subject: [PATCH 264/347] Handle an SQLITE_BUSY_TIMEOUT error if one occurs while attempting a shared lock on a read-lock slot. FossilOrigin-Name: 5fbf3906d272df3eb981f67455eb35f649ad2774cba9fc3f077b28d9bef3f0cb --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/wal.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 82af9ff683..c28f30eb6f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Have\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds\sblock\swhen\slocking\sa\sread-lock\sslot. -D 2023-11-27T20:37:03.543 +C Handle\san\sSQLITE_BUSY_TIMEOUT\serror\sif\sone\soccurs\swhile\sattempting\sa\sshared\slock\son\sa\sread-lock\sslot. +D 2023-11-28T15:29:04.012 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -810,7 +810,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8 F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7 F src/vtab.c 154725ebecd3bc02f7fbd7ad3974334f73fff76e02a964e828e48a7c5fb7efff F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 86eaddc56b124de2df14dfaff292ca7bfb862dbd68728105547ddd6a1e799227 +F src/wal.c fb52b05da32d500575a9b4ce0bb1c03b69876ea0f0873d4a8b0ca262da34e540 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 F src/where.c 1fdc69ce1333e9bd6d7d3df9fa5af1373a3f5bfdd52108d1dbc0ca85a55f777e @@ -2143,8 +2143,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 a51ef39998e25e86bd0600e71d15011b12e05f4319608018293bdaecb09e8c97 -R 50c3db5d407d996f614a56ca5cb31c30 +P f797baf47cf7859cfd8ce248f4f3087af4551a7040af990333426e5a7c269504 +R c6e742d8a411bdf1bc8b658a51b789de U dan -Z 3f1449733c562fe4e5bdc1215442e17e +Z 58f292c9c356eaa52cbcdd405bfb61bb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8a036325f7..360d2aad65 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f797baf47cf7859cfd8ce248f4f3087af4551a7040af990333426e5a7c269504 \ No newline at end of file +5fbf3906d272df3eb981f67455eb35f649ad2774cba9fc3f077b28d9bef3f0cb \ No newline at end of file diff --git a/src/wal.c b/src/wal.c index a15114710b..2bdaa1c3fb 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3134,7 +3134,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); walDisableBlocking(pWal); if( rc ){ - return rc==SQLITE_BUSY ? WAL_RETRY : rc; + assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); + return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index From ef97f8360a7063ea2191824cccd0414d86b607a1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 18:16:02 +0000 Subject: [PATCH 265/347] The json_remove() function now uses only JSONB, never JsonNodes, internally. FossilOrigin-Name: b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 --- manifest | 15 +- manifest.uuid | 2 +- src/json.c | 354 ++++++++++++++++++++++++---------------------- test/json501.test | 6 +- 4 files changed, 193 insertions(+), 184 deletions(-) diff --git a/manifest b/manifest index af38aa5b5a..510a2234e3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Activate\sthe\sability\sof\sjson_patch()\sto\swork\son\sJSONB. -D 2023-11-28T13:38:22.987 +C The\sjson_remove()\sfunction\snow\suses\sonly\sJSONB,\snever\sJsonNodes,\sinternally. +D 2023-11-28T18:16:02.028 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fa3a1b6f2432848dc03448d359a07040f7e4a44d868e9e16512c6066b65aeef9 +F src/json.c 442ff1ce9ebb82728aca5ddfcab21af46c3230e9f672307b91489f43cd6d6fdc F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1330,7 +1330,7 @@ F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2 F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d -F test/json501.test c419deb835b70c1a2c8532936927bcc1146730328edd2052276715bfd209724d +F test/json501.test ab168a12eb6eb14d479f8c1cdae3ac062fd5a4679f17f976e96f1af518408330 F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b F test/jsonb01.test cace70765b36a36aec9a85a41ea65667d3bbf647d4400ddc3ac76f8fe7d94f90 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff @@ -2145,9 +2145,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 fbca9570fd2e1465739e4d3a8d9bb40fad594fd78ab49b2cb34efa27ebdd8361 2c436806b8d5f57de99c00f6154b038454fb9ae427d00d7b4a46ab9c7c69bcb9 -R e5b38e7d70999e4556fdfbbe91a82c08 -T +closed 2c436806b8d5f57de99c00f6154b038454fb9ae427d00d7b4a46ab9c7c69bcb9 +P 11aba347ff7c639500eec904e212eabe889b077351b946cfeac2b74b9703672a +R 82f8a4e62603e32381a0117063382c7a U drh -Z 1523e6413d446a9df2c7c937b4cb4eb3 +Z 37d5559550d9d10448cc0117446ead8c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b1acbb8add..a64ec6f678 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -11aba347ff7c639500eec904e212eabe889b077351b946cfeac2b74b9703672a \ No newline at end of file +b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 \ No newline at end of file diff --git a/src/json.c b/src/json.c index bdc9087323..4baffa7fa3 100644 --- a/src/json.c +++ b/src/json.c @@ -885,11 +885,13 @@ static void jsonParseReset(JsonParse *pParse){ ** that this recursive destructor sequence terminates harmlessly. */ static void jsonParseFree(JsonParse *pParse){ - if( pParse->nJPRef>1 ){ - pParse->nJPRef--; - }else{ - jsonParseReset(pParse); - sqlite3_free(pParse); + if( pParse ){ + if( pParse->nJPRef>1 ){ + pParse->nJPRef--; + }else{ + jsonParseReset(pParse); + sqlite3_free(pParse); + } } } @@ -2636,7 +2638,7 @@ static void jsonBlobAppendNodeType( /* Change the payload size for the node at index i to be szPayload. */ -static void jsonBlobChangePayloadSize( +static int jsonBlobChangePayloadSize( JsonParse *pParse, u32 i, u32 szPayload @@ -2645,8 +2647,8 @@ static void jsonBlobChangePayloadSize( u8 szType; u8 nExtra; u8 nNeeded; - i8 delta; - if( pParse->oom ) return; + int delta; + if( pParse->oom ) return 0; a = &pParse->aBlob[i]; szType = a[0]>>4; if( szType<=11 ){ @@ -2672,7 +2674,7 @@ static void jsonBlobChangePayloadSize( u32 newSize = pParse->nBlob + delta; if( delta>0 ){ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ - return; /* OOM error. Error state recorded in pParse->oom. */ + return 0; /* OOM error. Error state recorded in pParse->oom. */ } a = &pParse->aBlob[i]; memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); @@ -2697,6 +2699,7 @@ static void jsonBlobChangePayloadSize( a[3] = (szPayload >> 8) & 0xff; a[4] = szPayload & 0xff; } + return delta; } /* @@ -3376,13 +3379,13 @@ static u32 jsonXlateBlobToText( } break; } - case JSONB_TEXT: case JSONB_TEXTJ: { jsonAppendChar(pOut, '"'); jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); jsonAppendChar(pOut, '"'); break; } + case JSONB_TEXT: case JSONB_TEXT5: { const char *zIn; u32 k; @@ -3390,7 +3393,7 @@ static u32 jsonXlateBlobToText( zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ - for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); if( k>=sz2 ){ @@ -3399,6 +3402,12 @@ static u32 jsonXlateBlobToText( zIn += k; sz2 -= k; } + if( zIn[0]=='"' ){ + jsonAppendRawNZ(pOut, "\\\"", 2); + zIn++; + sz2--; + continue; + } if( sz2<2 ){ if( sz2>0 ) pOut->eErr |= JSTRING_MALFORMED; if( sz2==0 ) break; @@ -3514,9 +3523,9 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ int nBlob; JsonParse s; if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; + aBlob = sqlite3_value_blob(pJson); nBlob = sqlite3_value_bytes(pJson); if( nBlob<1 ) return 0; - aBlob = sqlite3_value_blob(pJson); if( aBlob==0 || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; memset(&s, 0, sizeof(s)); s.aBlob = (u8*)aBlob; @@ -3797,7 +3806,7 @@ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ (void)jsonbPayloadSize(pParse, iRoot, &sz); pParse->nBlob = nBlob; sz += pParse->delta; - jsonBlobChangePayloadSize(pParse, iRoot, sz); + pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz); } /* @@ -4268,58 +4277,6 @@ static void jsonExtractFromBlob( sqlite3_free(zMsg); } } - -/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent -** arguments are JSON paths of elements to be removed. Do that removal -** and return the result. -*/ -static void jsonRemoveFromBlob( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - int i; - u32 rc; - const char *zPath = 0; - int flgs; - JsonParse px; - - memset(&px, 0, sizeof(px)); - px.nBlob = sqlite3_value_bytes(argv[0]); - px.aBlob = (u8*)sqlite3_value_blob(argv[0]); - if( px.aBlob==0 ) return; - for(i=1; i0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); - }else{ - JsonString s; - jsonStringInit(&s, ctx); - jsonXlateBlobToText(&px, 0, &s); - jsonReturnString(&s); - jsonParseReset(&px); - } - return; - -jsonRemoveFromBlob_patherror: - jsonParseReset(&px); - jsonPathSyntaxError(zPath, ctx); - return; -} /* ** pArg is a function argument that might be an SQL value or a JSON @@ -4463,48 +4420,119 @@ jsonInsertIntoBlob_patherror: return; } +/* +** Allowed values for the flgs argument to jsonParseFuncArg(); +*/ +#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ + +/* +** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, +** from the SQL function argument pArg. Return a pointer to the new +** JsonParse object. +** +** Ownership of the new JsonParse object is passed to the caller. The +** caller should invoke jsonParseFree() on the return value when it +** has finished using it. +** +** If any errors are detected, an appropriate error messages is set +** using sqlite3_result_error() or the equivalent and this routine +** returns NULL. This routine also returns NULL if the pArg argument +** is an SQL NULL value, but no error message is set in that case. This +** is so that SQL functions that are given NULL arguments will return +** a NULL value. +*/ +static JsonParse *jsonParseFuncArg( + sqlite3_context *ctx, + sqlite3_value *pArg, + u32 flgs +){ + int eType; /* Datatype of pArg */ + JsonParse *p = 0; /* Value to be returned */ + + assert( ctx!=0 ); + eType = sqlite3_value_type(pArg); + if( eType==SQLITE_NULL ){ + return 0; + } + p = sqlite3_malloc64( sizeof(*p) ); + if( p==0 ) goto json_pfa_oom; + memset(p, 0, sizeof(*p)); + if( eType==SQLITE_BLOB ){ + u32 n, sz = 0; + p->aBlob = (u8*)sqlite3_value_blob(pArg); + p->nBlob = (u32)sqlite3_value_bytes(pArg); + if( p->nBlob==0 ){ + goto json_pfa_malformed; + } + if( p->aBlob==0 ){ + goto json_pfa_oom; + } + if( (p->aBlob[0] & 0x0f)>JSONB_OBJECT ){ + goto json_pfa_malformed; + } + n = jsonbPayloadSize(p, 0, &sz); + if( n==0 + || sz+n!=p->nBlob + || ((p->aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0) + || sz+n!=p->nBlob + ){ + goto json_pfa_malformed; + } + if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ + goto json_pfa_oom; + } + return p; + } + /* TODO: Check in the cache */ + p->zJson = (char*)sqlite3_value_text(pArg); + p->nJson = sqlite3_value_bytes(pArg); + if( p->nJson==0 ) goto json_pfa_malformed; + if( p->zJson==0 ) goto json_pfa_oom; + if( jsonConvertTextToBlob(p, ctx) ){ + jsonParseFree(p); + return 0; + } + return p; + +json_pfa_malformed: + jsonParseFree(p); + sqlite3_result_error(ctx, "malformed JSON", -1); + return 0; + +json_pfa_oom: + jsonParseFree(p); + sqlite3_result_error_nomem(ctx); + return 0; +} + +/* +** Make the return value of a JSON function either the raw JSONB blob +** or make it JSON text, depending on whether the JSON_BLOB flag is +** set on the function. +*/ +static void jsonReturnParse( + sqlite3_context *ctx, + JsonParse *p +){ + int flgs; + flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( flgs & JSON_BLOB ){ + sqlite3_result_blob(ctx, p->aBlob, p->nBlob, + p->nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); + p->nBlobAlloc = 0; + }else{ + JsonString s; + jsonStringInit(&s, ctx); + jsonXlateBlobToText(p, 0, &s); + jsonReturnString(&s); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } +} + /**************************************************************************** ** SQL functions used for testing and debugging ****************************************************************************/ -#if SQLITE_DEBUG -/* -** Print N node entries. -*/ -static void jsonDebugPrintNodeEntries( - JsonNode *aNode, /* First node entry to print */ - int N /* Number of node entries to print */ -){ - int i; - for(i=0; inBlob); + } + jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0); +} #endif /* SQLITE_DEBUG */ - -#if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */ -static void jsonDebugPrintParse(JsonParse *p){ - jsonDebugPrintNodeEntries(p->aNode, p->nNode); -} -static void jsonDebugPrintNode(JsonNode *pNode){ - jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode)); -} -#else - /* The usual case */ -# define jsonDebugPrintNode(X) -# define jsonDebugPrintParse(X) -#endif - #ifdef SQLITE_DEBUG /* ** SQL function: json_parse(JSON) ** -** Parse JSON using jsonParseCached(). Then print a dump of that -** parse on standard output. Return the mimified JSON result, just -** like the json() function. +** Parse JSON using jsonParseFuncArg(). Then print a dump of that +** parse on standard output. */ static void jsonParseFunc( sqlite3_context *ctx, @@ -4613,30 +4635,9 @@ static void jsonParseFunc( JsonParse *p; /* The parse */ assert( argc==1 ); - if( jsonFuncArgMightBeBinary(argv[0]) ){ - JsonParse x; - memset(&x, 0, sizeof(x)); - x.nBlob = sqlite3_value_bytes(argv[0]); - x.aBlob = (u8*)sqlite3_value_blob(argv[0]); - jsonDebugPrintBlob(&x, 0, x.nBlob, 0); - return; - } - p = jsonParseCached(ctx, argv[0], ctx, 0); - if( p==0 ) return; - printf("nNode = %u\n", p->nNode); - printf("nAlloc = %u\n", p->nAlloc); - printf("nJson = %d\n", p->nJson); - printf("nAlt = %d\n", p->nAlt); - printf("nErr = %u\n", p->nErr); - printf("oom = %u\n", p->oom); - printf("hasNonstd = %u\n", p->hasNonstd); - printf("useMod = %u\n", p->useMod); - printf("hasMod = %u\n", p->hasMod); - printf("nJPRef = %u\n", p->nJPRef); - printf("iSubst = %u\n", p->iSubst); - printf("iHold = %u\n", p->iHold); - jsonDebugPrintNodeEntries(p->aNode, p->nNode); - jsonReturnNodeAsJson(p, p->aNode, ctx, 1, 0); + p = jsonParseFuncArg(ctx, argv[0], 0); + jsonShowParse(p); + jsonParseFree(p); } /* @@ -5278,8 +5279,6 @@ static void jsonPatchFunc( }else if( pX->nErr ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else if( pResult ){ - jsonDebugPrintParse(pX); - jsonDebugPrintNode(pResult); jsonReturnNodeAsJson(pX, pResult, ctx, 0, 0); } } @@ -5337,34 +5336,47 @@ static void jsonRemoveFunc( int argc, sqlite3_value **argv ){ - JsonParse *pParse; /* The parse */ - JsonNode *pNode; - const char *zPath; - u32 i; + JsonParse *p; /* The parse */ + const char *zPath = 0; /* Path of element to be removed */ + u32 i; /* Loop counter */ + int rc; /* Subroutine return code */ if( argc<1 ) return; - if( jsonFuncArgMightBeBinary(argv[0]) ){ - jsonRemoveFromBlob(ctx, argc, argv); - return; - } - pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); - if( pParse==0 ) return; - for(i=1; i<(u32)argc; i++){ - zPath = (const char*)sqlite3_value_text(argv[i]); - if( zPath==0 ) goto remove_done; - pNode = jsonLookup(pParse, zPath, 0, ctx); - if( pParse->nErr ) goto remove_done; - if( pNode ){ - pNode->jnFlags |= JNODE_REMOVE; - pParse->hasMod = 1; - pParse->useMod = 1; + p = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); + if( p==0 ) return; + for(i=1; ieEdit = JEDIT_DEL; + p->delta = 0; + rc = jsonLookupBlobStep(p, 0, zPath+1, 0); + if( rc==JSON_BLOB_NOTFOUND ) continue; + if( JSON_BLOB_ISERROR(rc) ) goto json_remove_patherror; } - if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0); - } -remove_done: - jsonDebugPrintParse(p); + jsonReturnParse(ctx, p); + jsonParseFree(p); + return; + +json_remove_patherror: + jsonParseFree(p); + jsonPathSyntaxError(zPath, ctx); + return; + +json_remove_return_null: + jsonParseFree(p); + return; } /* @@ -5503,7 +5515,6 @@ static void jsonReplaceFunc( } jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0); replace_err: - jsonDebugPrintParse(pParse); jsonParseFree(pParse); } @@ -5559,7 +5570,6 @@ static void jsonSetFunc( jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); } } - jsonDebugPrintParse(pParse); jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0); jsonSetDone: jsonParseFree(pParse); diff --git a/test/json501.test b/test/json501.test index 3318eea7f2..40b3b563db 100644 --- a/test/json501.test +++ b/test/json501.test @@ -252,13 +252,13 @@ do_execsql_test 8.11 { do_execsql_test 9.1 { WITH c(x) AS (VALUES('{x: +Infinity}')) SELECT x->>'x', json(x) FROM c; -} {Inf {{"x":9.0e999}}} +} {Inf {{"x":9e999}}} do_execsql_test 9.2 { WITH c(x) AS (VALUES('{x: -Infinity}')) SELECT x->>'x', json(x) FROM c; -} {-Inf {{"x":-9.0e999}}} +} {-Inf {{"x":-9e999}}} do_execsql_test 9.3 { WITH c(x) AS (VALUES('{x: Infinity}')) SELECT x->>'x', json(x) FROM c; -} {Inf {{"x":9.0e999}}} +} {Inf {{"x":9e999}}} do_execsql_test 9.4 { WITH c(x) AS (VALUES('{x: NaN}')) SELECT x->>'x', json(x) FROM c; } {{} {{"x":null}}} From 1cab41e2906130ec64af0381d5a31f017133b248 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 20:25:23 +0000 Subject: [PATCH 266/347] Attempt to get json_extract() working with pure JSONB only, and without the use of JsonNode. Mostly working, but there are some differences from legacy in corner cases. FossilOrigin-Name: 8c324af1eca27e86adc45622af4f3b06a67a3f968596ac58aa7434b1f6f05f3c --- manifest | 19 +-- manifest.uuid | 2 +- src/json.c | 336 +++++++++++++--------------------------------- test/json101.test | 2 +- test/json105.test | 10 +- 5 files changed, 112 insertions(+), 257 deletions(-) diff --git a/manifest b/manifest index 510a2234e3..8d932ccf28 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sjson_remove()\sfunction\snow\suses\sonly\sJSONB,\snever\sJsonNodes,\sinternally. -D 2023-11-28T18:16:02.028 +C Attempt\sto\sget\sjson_extract()\sworking\swith\spure\sJSONB\sonly,\sand\swithout\nthe\suse\sof\sJsonNode.\s\sMostly\sworking,\sbut\sthere\sare\ssome\sdifferences\sfrom\nlegacy\sin\scorner\scases. +D 2023-11-28T20:25:23.336 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 442ff1ce9ebb82728aca5ddfcab21af46c3230e9f672307b91489f43cd6d6fdc +F src/json.c bbe9cb34174bfbc0e067a480e431ffc8f0d6cc2cfcad8a1001472bbd00ca620a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1325,11 +1325,11 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 643936557daeb2eeeb839e9d87a682c7d7c6f6aee66045eedd8613cb8471d8d8 +F test/json101.test 8f5d1a3350c36945c8b58f538e1c92cfd87fd50ab6f5e3d5f4cf3cdb03b9546d F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 -F test/json105.test 11670a4387f4308ae0318cadcbd6a918ea7edcd19fbafde020720a073952675d +F test/json105.test e64a8d73677fbae67886642cd5076e2ef3efe89f8483b87595cf9c030216c9bd F test/json501.test ab168a12eb6eb14d479f8c1cdae3ac062fd5a4679f17f976e96f1af518408330 F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b F test/jsonb01.test cace70765b36a36aec9a85a41ea65667d3bbf647d4400ddc3ac76f8fe7d94f90 @@ -2145,8 +2145,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 11aba347ff7c639500eec904e212eabe889b077351b946cfeac2b74b9703672a -R 82f8a4e62603e32381a0117063382c7a +P b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 +R ccd1a96290081a160a58be4f68f67cce +T *branch * jsonb-extract +T *sym-jsonb-extract * +T -sym-jsonb * U drh -Z 37d5559550d9d10448cc0117446ead8c +Z fece822412df4bcd481f5f301c9018f9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a64ec6f678..2cee28775a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 \ No newline at end of file +8c324af1eca27e86adc45622af4f3b06a67a3f968596ac58aa7434b1f6f05f3c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 4baffa7fa3..c259ae1d87 100644 --- a/src/json.c +++ b/src/json.c @@ -1110,180 +1110,6 @@ static u32 jsonHexToInt4(const char *z){ return v; } -/* -** Make the return value from an SQL function be the SQL value of -** JsonNode pNode. -** -** If pNode is an atom (not an array or object) then the value returned -** is a pure SQL value - an SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT, or -** SQLITE_NULL. However, if pNode is a JSON array or object, then the -** value returned is either RFC-8259 JSON text or a BLOB in the JSONB -** format, depending on the JSON_BLOB flag of the function user-data. -*/ -static void jsonReturnFromNode( - JsonParse *pParse, /* Complete JSON parse tree */ - JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx, /* Return value for this function */ - int omitSubtype /* Do not call sqlite3_result_subtype() */ -){ - switch( pNode->eType ){ - default: { - assert( pNode->eType==JSON_NULL ); - sqlite3_result_null(pCtx); - break; - } - case JSON_TRUE: { - sqlite3_result_int(pCtx, 1); - break; - } - case JSON_FALSE: { - sqlite3_result_int(pCtx, 0); - break; - } - case JSON_INT: { - sqlite3_int64 i = 0; - int rc; - int bNeg = 0; - const char *z; - char *zz; - sqlite3 *db = sqlite3_context_db_handle(pCtx); - - assert( pNode->eU==1 ); - zz = sqlite3DbStrNDup(db, pNode->u.zJContent, pNode->n); - if( zz==0 ){ - sqlite3_result_error_nomem(pCtx); - return; - } - z = zz; - if( z[0]=='-' ){ z++; bNeg = 1; } - else if( z[0]=='+' ){ z++; } - rc = sqlite3DecOrHexToI64(z, &i); - sqlite3DbFree(db, zz); - if( rc<=1 ){ - sqlite3_result_int64(pCtx, bNeg ? -i : i); - }else if( rc==3 && bNeg ){ - sqlite3_result_int64(pCtx, SMALLEST_INT64); - }else{ - goto to_double; - } - break; - } - case JSON_REAL: { - double r; - const char *z; - assert( pNode->eU==1 ); - to_double: - z = pNode->u.zJContent; - sqlite3AtoF(z, &r, pNode->n, SQLITE_UTF8); - sqlite3_result_double(pCtx, r); - break; - } - case JSON_STRING: { - if( pNode->jnFlags & JNODE_RAW ){ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, - SQLITE_TRANSIENT); - }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ - /* JSON formatted without any backslash-escapes */ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, - SQLITE_TRANSIENT); - }else{ - /* Translate JSON formatted string into raw text */ - u32 i; - u32 n = pNode->n; - const char *z; - char *zOut; - u32 j; - u32 nOut = n; - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - zOut = sqlite3_malloc( nOut+1 ); - if( zOut==0 ){ - sqlite3_result_error_nomem(pCtx); - break; - } - for(i=0, j=0; i>6)); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - u32 vlo; - if( (v&0xfc00)==0xd800 - && i>18); - zOut[j++] = 0x80 | ((v>>12)&0x3f); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - zOut[j++] = 0xe0 | (v>>12); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - } - } - continue; - }else if( c=='b' ){ - c = '\b'; - }else if( c=='f' ){ - c = '\f'; - }else if( c=='n' ){ - c = '\n'; - }else if( c=='r' ){ - c = '\r'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='v' ){ - c = '\v'; - }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ - /* pass through unchanged */ - }else if( c=='0' ){ - c = 0; - }else if( c=='x' ){ - c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); - i += 2; - }else if( c=='\r' && z[i+1]=='\n' ){ - i++; - continue; - }else if( 0xe2==(u8)c ){ - assert( 0x80==(u8)z[i+1] ); - assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); - i += 2; - continue; - }else{ - continue; - } - } /* end if( c=='\\' ) */ - zOut[j++] = c; - } /* end for() */ - zOut[j] = 0; - sqlite3_result_text(pCtx, zOut, j, sqlite3_free); - } - break; - } - case JSON_ARRAY: - case JSON_OBJECT: { - jsonReturnNodeAsJson(pParse, pNode, pCtx, 0, omitSubtype); - break; - } - } -} - /* ** A macro to hint to the compiler that a function should not be ** inlined. @@ -4217,6 +4043,7 @@ static void jsonReturnFromBlob( } } +#if 0 /* Do a JSON_EXTRACT(JSON, PATH) on a when JSON is a BLOB. */ static void jsonExtractFromBlob( @@ -4277,6 +4104,7 @@ static void jsonExtractFromBlob( sqlite3_free(zMsg); } } +#endif /* ** pArg is a function argument that might be an SQL value or a JSON @@ -4819,6 +4647,19 @@ static void jsonArrayLengthFunc( sqlite3_result_int64(ctx, n); } +/* +** Generate a bad path error for json_extract() +*/ +static void jsonExtractBadPathError( + sqlite3_context *ctx, /* The function call containing the error */ + const char *zPath /* The path with the problem */ +){ + sqlite3 *db = sqlite3_context_db_handle(ctx); + char *zMsg = sqlite3MPrintf(db, "bad JSON path: %Q", zPath); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3DbFree(db, zMsg); +} + /* ** json_extract(JSON, PATH, ...) ** "->"(JSON,PATH) @@ -4844,83 +4685,94 @@ static void jsonExtractFunc( int argc, sqlite3_value **argv ){ - JsonParse *p; /* The parse */ - JsonNode *pNode; - const char *zPath; - int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - JsonString jx; + JsonParse *p = 0; /* The parse */ + int flags; /* Flags associated with the function */ + int i; /* Loop counter */ + JsonString jx; /* String for array result */ if( argc<2 ) return; - if( jsonFuncArgMightBeBinary(argv[0]) && argc==2 ){ - jsonExtractFromBlob(ctx, argv[0], argv[1], flags); - return; - } - p = jsonParseCached(ctx, argv[0], ctx, 0); + p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; - if( argc==2 ){ - /* With a single PATH argument */ - zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 ) return; - if( flags & JSON_ABPATH ){ - if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){ - /* The -> and ->> operators accept abbreviated PATH arguments. This - ** is mostly for compatibility with PostgreSQL, but also for - ** convenience. - ** - ** NUMBER ==> $[NUMBER] // PG compatible - ** LABEL ==> $.LABEL // PG compatible - ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience - */ - jsonStringInit(&jx, ctx); - if( sqlite3Isdigit(zPath[0]) ){ - jsonAppendRawNZ(&jx, "$[", 2); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendRawNZ(&jx, "]", 2); - }else{ - jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='[')); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendChar(&jx, 0); - } - pNode = jx.eErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); - jsonStringReset(&jx); - }else{ - pNode = jsonLookup(p, zPath, 0, ctx); - } - if( pNode ){ - if( flags & JSON_JSON ){ - jsonReturnNodeAsJson(p, pNode, ctx, 0, 0); - }else{ - jsonReturnFromNode(p, pNode, ctx, 1); - } - } - }else{ - pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturnFromNode(p, pNode, ctx, 0); - } - }else{ - /* Two or more PATH arguments results in a JSON array with each - ** element of the array being the value selected by one of the PATHs */ - int i; - jsonStringInit(&jx, ctx); + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + jsonStringInit(&jx, ctx); + if( argc>2 ){ jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - jsonAppendSeparator(&jx); - if( pNode ){ - jsonXlateNodeToText(p, pNode, &jx); + } + for(i=1; i and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for + ** convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + */ + jsonStringInit(&jx, ctx); + if( sqlite3Isdigit(zPath[0]) ){ + jsonAppendRawNZ(&jx, "[", 1); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendRawNZ(&jx, "]", 2); + }else if( zPath[0]!='[' ){ + jsonAppendRawNZ(&jx, ".", 1); + jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); + jsonAppendChar(&jx, 0); + } + jsonStringTerminate(&jx); + j = jsonLookupBlobStep(p, 0, jx.zBuf, 0); + jsonStringReset(&jx); + }else{ + jsonExtractBadPathError(ctx, zPath); + goto json_extract_error; + } + if( jnBlob ){ + if( argc==2 ){ + if( flags & JSON_JSON ){ + jsonStringInit(&jx, ctx); + jsonXlateBlobToText(p, j, &jx); + jsonReturnString(&jx); + jsonStringReset(&jx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + }else{ + jsonReturnFromBlob(p, j, ctx, 0); + if( (flags & JSON_SQL)==0 && (p->aBlob[j]&0x0f)>=JSONB_ARRAY ){ + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + } }else{ + jsonAppendSeparator(&jx); + jsonXlateBlobToText(p, j, &jx); + } + }else if( j==JSON_BLOB_NOTFOUND ){ + if( argc==2 ){ + goto json_extract_error; /* Return NULL if not found */ + }else{ + jsonAppendSeparator(&jx); jsonAppendRawNZ(&jx, "null", 4); } + }else if( j==JSON_BLOB_ERROR ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + goto json_extract_error; + }else{ + jsonExtractBadPathError(ctx, zPath); + goto json_extract_error; } - if( i==argc ){ - jsonAppendChar(&jx, ']'); - jsonReturnString(&jx); - sqlite3_result_subtype(ctx, JSON_SUBTYPE); - } - jsonStringReset(&jx); } + if( argc>2 ){ + jsonAppendChar(&jx, ']'); + jsonReturnString(&jx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } +json_extract_error: + jsonStringReset(&jx); + jsonParseFree(p); + return; } /* This is the RFC 7396 MergePatch algorithm. diff --git a/test/json101.test b/test/json101.test index ebaf0f3b34..3c3b1a5ec0 100644 --- a/test/json101.test +++ b/test/json101.test @@ -974,7 +974,7 @@ do_execsql_test json101-18.4 { } {6} do_catchsql_test json101-18.5 { SELECT json_extract('{"":8}', '$.'); -} {1 {JSON path error near ''}} +} {1 {bad JSON path: '$.'}} # 2022-08-29 https://sqlite.org/forum/forumpost/9b9e4716c0d7bbd1 # This is not a problem specifically with JSON functions. It is diff --git a/test/json105.test b/test/json105.test index 83face188e..5592191c22 100644 --- a/test/json105.test +++ b/test/json105.test @@ -96,18 +96,18 @@ json_replace_test 80 {'$.b[#-1]','AAA','$.b[#-1]','BBB'} \ do_catchsql_test json105-6.10 { SELECT json_extract(j, '$.b[#-]') FROM t1; -} {1 {JSON path error near '[#-]'}} +} {1 {bad JSON path: '$.b[#-]'}} do_catchsql_test json105-6.20 { SELECT json_extract(j, '$.b[#9]') FROM t1; -} {1 {JSON path error near '[#9]'}} +} {1 {bad JSON path: '$.b[#9]'}} do_catchsql_test json105-6.30 { SELECT json_extract(j, '$.b[#+2]') FROM t1; -} {1 {JSON path error near '[#+2]'}} +} {1 {bad JSON path: '$.b[#+2]'}} do_catchsql_test json105-6.40 { SELECT json_extract(j, '$.b[#-1') FROM t1; -} {1 {JSON path error near '[#-1'}} +} {1 {bad JSON path: '$.b[#-1'}} do_catchsql_test json105-6.50 { SELECT json_extract(j, '$.b[#-1x]') FROM t1; -} {1 {JSON path error near '[#-1x]'}} +} {1 {bad JSON path: '$.b[#-1x]'}} finish_test From 1ef232c0e1e10d17f58bfea678c0c2774e7f29ab Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 28 Nov 2023 20:33:20 +0000 Subject: [PATCH 267/347] Preserve flexibility in the format of the RHS of -> and ->> operators found in legacy. FossilOrigin-Name: 6231ec43adb7436195eb1497de39a6c13c6b4f1c5032e6ea52515d214e61fdbc --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 7 +++++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 8d932ccf28..aa8750da24 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Attempt\sto\sget\sjson_extract()\sworking\swith\spure\sJSONB\sonly,\sand\swithout\nthe\suse\sof\sJsonNode.\s\sMostly\sworking,\sbut\sthere\sare\ssome\sdifferences\sfrom\nlegacy\sin\scorner\scases. -D 2023-11-28T20:25:23.336 +C Preserve\sflexibility\sin\sthe\sformat\sof\sthe\sRHS\sof\s->\sand\s->>\soperators\sfound\nin\slegacy. +D 2023-11-28T20:33:20.843 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c bbe9cb34174bfbc0e067a480e431ffc8f0d6cc2cfcad8a1001472bbd00ca620a +F src/json.c 4dfa914bc57559ba1ee7e67211b7146d6ea522630fa2b51ddbc9244097d50b2b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,11 +2145,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 b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 -R ccd1a96290081a160a58be4f68f67cce -T *branch * jsonb-extract -T *sym-jsonb-extract * -T -sym-jsonb * +P 8c324af1eca27e86adc45622af4f3b06a67a3f968596ac58aa7434b1f6f05f3c +R 8969d2e433c13563f53a82b455df93bc U drh -Z fece822412df4bcd481f5f301c9018f9 +Z 4af78bee74fbed5f3138fbbbd5c5ff0e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2cee28775a..8d4b8f7819 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8c324af1eca27e86adc45622af4f3b06a67a3f968596ac58aa7434b1f6f05f3c \ No newline at end of file +6231ec43adb7436195eb1497de39a6c13c6b4f1c5032e6ea52515d214e61fdbc \ No newline at end of file diff --git a/src/json.c b/src/json.c index c259ae1d87..6d1cf0e40b 100644 --- a/src/json.c +++ b/src/json.c @@ -4701,6 +4701,7 @@ static void jsonExtractFunc( for(i=1; i Date: Tue, 28 Nov 2023 23:18:04 +0000 Subject: [PATCH 268/347] Do not set the J subtype when the output is JSONB. FossilOrigin-Name: 4f106b64fe8988435872806bd0a6c223b61f53af0dd1c47c847bb4eec4e03e27 --- manifest | 12 ++++----- manifest.uuid | 2 +- src/json.c | 72 +++++---------------------------------------------- 3 files changed, 14 insertions(+), 72 deletions(-) diff --git a/manifest b/manifest index aa8750da24..9f81009143 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Preserve\sflexibility\sin\sthe\sformat\sof\sthe\sRHS\sof\s->\sand\s->>\soperators\sfound\nin\slegacy. -D 2023-11-28T20:33:20.843 +C Do\snot\sset\sthe\sJ\ssubtype\swhen\sthe\soutput\sis\sJSONB. +D 2023-11-28T23:18:04.442 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4dfa914bc57559ba1ee7e67211b7146d6ea522630fa2b51ddbc9244097d50b2b +F src/json.c dd5c3f52877448cc9644887e83a2ff33ab0be87e158de7d5a86db12f90b28045 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 8c324af1eca27e86adc45622af4f3b06a67a3f968596ac58aa7434b1f6f05f3c -R 8969d2e433c13563f53a82b455df93bc +P 6231ec43adb7436195eb1497de39a6c13c6b4f1c5032e6ea52515d214e61fdbc +R e440543ab70160e0cdb3ca23863ac507 U drh -Z 4af78bee74fbed5f3138fbbbd5c5ff0e +Z d79a3adcec115b27b232bb847ce59659 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8d4b8f7819..2e041f86bf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6231ec43adb7436195eb1497de39a6c13c6b4f1c5032e6ea52515d214e61fdbc \ No newline at end of file +4f106b64fe8988435872806bd0a6c223b61f53af0dd1c47c847bb4eec4e03e27 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6d1cf0e40b..1a2af6f8f1 100644 --- a/src/json.c +++ b/src/json.c @@ -4043,69 +4043,6 @@ static void jsonReturnFromBlob( } } -#if 0 -/* Do a JSON_EXTRACT(JSON, PATH) on a when JSON is a BLOB. -*/ -static void jsonExtractFromBlob( - sqlite3_context *ctx, - sqlite3_value *pJson, - sqlite3_value *pPath, - int flags -){ - const char *zPath = (const char*)sqlite3_value_text(pPath); - u32 i = 0; - JsonParse px; - if( zPath==0 ) return; - memset(&px, 0, sizeof(px)); - px.nBlob = sqlite3_value_bytes(pJson); - px.aBlob = (u8*)sqlite3_value_blob(pJson); - if( px.aBlob==0 ) return; - if( zPath[0]=='$' ){ - zPath++; - i = jsonLookupBlobStep(&px, 0, zPath, 0); - }else if( (flags & JSON_ABPATH) ){ - /* The -> and ->> operators accept abbreviated PATH arguments. This - ** is mostly for compatibility with PostgreSQL, but also for - ** convenience. - ** - ** NUMBER ==> $[NUMBER] // PG compatible - ** LABEL ==> $.LABEL // PG compatible - ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience - */ - JsonString jx; - jsonStringInit(&jx, ctx); - if( sqlite3Isdigit(zPath[0]) ){ - jsonAppendRawNZ(&jx, "[", 1); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendRawNZ(&jx, "]", 2); - zPath = jx.zBuf; - }else if( zPath[0]!='[' ){ - jsonAppendRawNZ(&jx, ".", 1); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendChar(&jx, 0); - zPath = jx.zBuf; - } - i = jsonLookupBlobStep(&px, 0, zPath, 0); - jsonStringReset(&jx); - }else{ - sqlite3_result_error(ctx, "bad path", -1); - return; - } - if( iaBlob[j]&0x0f)>=JSONB_ARRAY ){ + if( (flags & (JSON_SQL|JSON_BLOB))==0 + && (p->aBlob[j]&0x0f)>=JSONB_ARRAY + ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } } @@ -4770,7 +4710,9 @@ static void jsonExtractFunc( if( argc>2 ){ jsonAppendChar(&jx, ']'); jsonReturnString(&jx); - sqlite3_result_subtype(ctx, JSON_SUBTYPE); + if( (flags & JSON_BLOB)==0 ){ + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } } json_extract_error: jsonStringReset(&jx); From 4b54d6cdafacfab6f84f4fd2055739d69e34a954 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 29 Nov 2023 01:38:15 +0000 Subject: [PATCH 269/347] Convert the json_array_length() function to use JSONB instead of JsonNodes. FossilOrigin-Name: 5ab790736d943e08f097efcee5cfbf0d83c65b0a53f273060330ba719affa5e5 --- manifest | 13 ++++---- manifest.uuid | 2 +- src/json.c | 89 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/manifest b/manifest index 8499e1f61f..0134cc2e4a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sall\sknown\sproblems\swith\sJSONB\sjson_extract(). -D 2023-11-28T23:26:55.773 +C Convert\sthe\sjson_array_length()\sfunction\sto\suse\sJSONB\sinstead\sof\sJsonNodes. +D 2023-11-29T01:38:15.043 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c dd5c3f52877448cc9644887e83a2ff33ab0be87e158de7d5a86db12f90b28045 +F src/json.c aebeb6c1beea6a143223139e9b168b4187aff02c60a49daa6bcafadafcb6ee15 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,9 +2145,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 b69786e746ae2b927b64d9871fd120b7f8f06cc53739fd46a4da51aa16cf8576 4f106b64fe8988435872806bd0a6c223b61f53af0dd1c47c847bb4eec4e03e27 -R e440543ab70160e0cdb3ca23863ac507 -T +closed 4f106b64fe8988435872806bd0a6c223b61f53af0dd1c47c847bb4eec4e03e27 +P d5f48c57e975ac468cf29a43a5d0b56ef6d06cf35a8b0bddf87ec1c0fc7ae028 +R 4f247130b18247c9fbdce9034c7ad3a3 U drh -Z c2c6abd51345cdb21ffc83ec27ff2d5d +Z 81615ca9c91f916f71f930ae045acac7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3a99a8156d..539a333b70 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d5f48c57e975ac468cf29a43a5d0b56ef6d06cf35a8b0bddf87ec1c0fc7ae028 \ No newline at end of file +5ab790736d943e08f097efcee5cfbf0d83c65b0a53f273060330ba719affa5e5 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1a2af6f8f1..d210e5e986 100644 --- a/src/json.c +++ b/src/json.c @@ -4538,6 +4538,18 @@ static void jsonArrayFunc( sqlite3_result_subtype(ctx, JSON_SUBTYPE); } +/* +** Generate a bad path error for json_extract() +*/ +static void jsonBadPathError( + sqlite3_context *ctx, /* The function call containing the error */ + const char *zPath /* The path with the problem */ +){ + sqlite3 *db = sqlite3_context_db_handle(ctx); + char *zMsg = sqlite3MPrintf(db, "bad JSON path: %Q", zPath); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3DbFree(db, zMsg); +} /* ** json_array_length(JSON) @@ -4552,49 +4564,52 @@ static void jsonArrayLengthFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - sqlite3_int64 n = 0; + sqlite3_int64 cnt = 0; u32 i; - JsonNode *pNode; + u8 eErr = 0; - p = jsonParseCached(ctx, argv[0], ctx, 0); + p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; - assert( p->nNode ); if( argc==2 ){ const char *zPath = (const char*)sqlite3_value_text(argv[1]); - pNode = jsonLookup(p, zPath, 0, ctx); - }else{ - pNode = p->aNode; - } - if( pNode==0 ){ - return; - } - if( pNode->eType==JSON_ARRAY ){ - while( 1 /*exit-by-break*/ ){ - i = 1; - while( i<=pNode->n ){ - if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++; - i += jsonNodeSize(&pNode[i]); + if( zPath==0 ){ + jsonParseFree(p); + return; + } + i = jsonLookupBlobStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); + if( JSON_BLOB_ISERROR(i) ){ + if( i==JSON_BLOB_NOTFOUND ){ + /* no-op */ + }else if( i==JSON_BLOB_PATHERROR ){ + jsonBadPathError(ctx, zPath); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( p->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &p->aNode[pNode->u.iAppend]; + eErr = 1; + i = 0; + } + }else{ + i = 0; + } + if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ + u32 n, sz = 0, iEnd; + n = jsonbPayloadSize(p, i, &sz); + if( n==0 ) eErr = 2; + iEnd = i+n+sz; + i += n; + while( eErr==0 && inBlob ){ @@ -4703,7 +4718,7 @@ static void jsonExtractFunc( sqlite3_result_error(ctx, "malformed JSON", -1); goto json_extract_error; }else{ - jsonExtractBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath); goto json_extract_error; } } From 2ba5534101c28d1edcc1f1c6e70a2bfb193b818b Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 29 Nov 2023 02:45:09 +0000 Subject: [PATCH 270/347] The assertion change at check-in [7946c79567b0ccd3] is insufficient to fix the problem of a Table object being deleted out from under the OP_VCheck opcode. We need to reference count the Table, which is accomplished here. FossilOrigin-Name: cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db --- manifest | 21 ++++++++++----------- manifest.uuid | 2 +- src/pragma.c | 3 ++- src/vdbe.c | 5 ++--- src/vdbe.h | 1 + src/vdbeaux.c | 4 ++++ 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 6390c0d3c4..d8076c2105 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sSQLITE_ENABLE_SETLK_TIMEOUT\sbuilds,\suse\sblocking\slocks\sin\splace\sof\ssleep()\scalls\swhen\sopening\sa\sread-transaction. -D 2023-11-28T17:12:42.879 +C The\sassertion\schange\sat\scheck-in\s[7946c79567b0ccd3]\sis\sinsufficient\sto\sfix\nthe\sproblem\sof\sa\sTable\sobject\sbeing\sdeleted\sout\sfrom\sunder\sthe\sOP_VCheck\nopcode.\s\sWe\sneed\sto\sreference\scount\sthe\sTable,\swhich\sis\saccomplished\shere. +D 2023-11-29T02:45:09.518 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -721,7 +721,7 @@ F src/parse.y 020d80386eb216ec9520549106353c517d2bbc89be28752ffdca649a9eaf56ec F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 -F src/pragma.c b3b4ad9c0298d63098a067acca613c21a5f56b4d176d5842922bcd0b07b7164e +F src/pragma.c b5b4cff830575e6188cd56a295a57448d2b9dbc53f0dae58e22b97354cda3781 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 371f6115cb69286ebc12c6f2d7511279c2e47d9f54f475d46a554d687a3b312c F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 @@ -798,11 +798,11 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c b22cc9f203a8c0b9ee5338a67f8860347d14845864c10248bebe84518a781677 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 -F src/vdbe.c 319af2cf092d20e233e8ad4267ae49bfe33c50ac4db4ee7e47af898f824c2368 -F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 +F src/vdbe.c c7a5a1b27c6567faeeb5f41c72a4f7f81ee34f8ec0449c3dd9ae25b8a28bdccd +F src/vdbe.h 88e19a982df9027ec1c177c793d1a5d34dc23d8f06e3b2d997f43688b05ee0eb F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c F src/vdbeapi.c b07df805110dc6e81f2a3f9cd4e83f56ea523277a59bcec489a12b740c1079e7 -F src/vdbeaux.c b34dfbc09403ccb676608da16ff0780d23d466470563d24fdf6350b8d2271d5e +F src/vdbeaux.c c5a471b34e9c4cfc0295a3e10734fd197670ffaebcb742f284c8e17e8026ceea F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 0012d5f01cc866833847c2f3ae4c318ac53a1cb3d28acad9c35e688039464cf0 F src/vdbesort.c 237840ca1947511fa59bd4e18b9eeae93f2af2468c34d2427b059f896230a547 @@ -2143,9 +2143,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 a0c87ae9d3db914d18e2c8811db0d0ae3ad7b15c63de84fa975efce28bace27e 5fbf3906d272df3eb981f67455eb35f649ad2774cba9fc3f077b28d9bef3f0cb -R a3e95520ff5d52f362b8ffc8bb5b50fd -T +closed 5fbf3906d272df3eb981f67455eb35f649ad2774cba9fc3f077b28d9bef3f0cb -U dan -Z fe1d300d9f65afce84bb98eff88fc681 +P 4c055b7a6e4533e1e571773456226ca7038ce372df3eedbbbcd9a81e8652a6cf +R b802cc74f401db4801f35603d41bed2d +U drh +Z fcd4003bc0cfee07fff53afb131d1b17 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a36bb8e2e7..47522a290b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4c055b7a6e4533e1e571773456226ca7038ce372df3eedbbbcd9a81e8652a6cf \ No newline at end of file +cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 90076b0c26..4c90574182 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1779,7 +1779,8 @@ void sqlite3Pragma( if( pVTab->pModule->iVersion<4 ) continue; if( pVTab->pModule->xIntegrity==0 ) continue; sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); - sqlite3VdbeAppendP4(v, pTab, P4_TABLE); + pTab->nTabRef++; + sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, a1); diff --git a/src/vdbe.c b/src/vdbe.c index 38155a170c..0387f21972 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -8180,9 +8180,10 @@ case OP_VCheck: { /* out2 */ pOut = &aMem[pOp->p2]; sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ - assert( pOp->p4type==P4_TABLE ); + assert( pOp->p4type==P4_TABLEREF ); pTab = pOp->p4.pTab; assert( pTab!=0 ); + assert( pTab->nTabRef>0 ); assert( IsVirtual(pTab) ); if( pTab->u.vtab.p==0 ) break; pVtab = pTab->u.vtab.p->pVtab; @@ -8191,13 +8192,11 @@ case OP_VCheck: { /* out2 */ assert( pModule!=0 ); assert( pModule->iVersion>=4 ); assert( pModule->xIntegrity!=0 ); - pTab->nTabRef++; sqlite3VtabLock(pTab->u.vtab.p); assert( pOp->p1>=0 && pOp->p1nDb ); rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName, pOp->p3, &zErr); sqlite3VtabUnlock(pTab->u.vtab.p); - sqlite3DeleteTable(db, pTab); if( rc ){ sqlite3_free(zErr); goto abort_due_to_error; diff --git a/src/vdbe.h b/src/vdbe.h index f44f24f93e..25bda6be7a 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -126,6 +126,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ +#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 114a759c9a..420365e930 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1400,6 +1400,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); break; } + case P4_TABLEREF: { + if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); + break; + } } } From ed088f5f8d09d98f0df704e1e56c05d4d42e78b2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 29 Nov 2023 13:47:46 +0000 Subject: [PATCH 271/347] In the recovery extension, if a payload size is unreasonably large, it is probably corrupt, so truncate it. FossilOrigin-Name: 988c3179e978a3a6d42541e9c7a2ab98150383671810926503376ed808f150ff --- ext/recover/dbdata.c | 1 + manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/recover/dbdata.c b/ext/recover/dbdata.c index 25a6e9fd6a..b6cb26ecd3 100644 --- a/ext/recover/dbdata.c +++ b/ext/recover/dbdata.c @@ -582,6 +582,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ bNextPage = 1; }else{ iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); + if( nPayload>0x7fffff00 ) nPayload &= 0x3fff; } /* If this is a leaf intkey cell, load the rowid */ diff --git a/manifest b/manifest index d8076c2105..0c8757df1c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sassertion\schange\sat\scheck-in\s[7946c79567b0ccd3]\sis\sinsufficient\sto\sfix\nthe\sproblem\sof\sa\sTable\sobject\sbeing\sdeleted\sout\sfrom\sunder\sthe\sOP_VCheck\nopcode.\s\sWe\sneed\sto\sreference\scount\sthe\sTable,\swhich\sis\saccomplished\shere. -D 2023-11-29T02:45:09.518 +C In\sthe\srecovery\sextension,\sif\sa\spayload\ssize\sis\sunreasonably\slarge,\sit\sis\nprobably\scorrupt,\sso\struncate\sit. +D 2023-11-29T13:47:46.693 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -460,7 +460,7 @@ F ext/rbu/rbuvacuum4.test ffccd22f67e2d0b380d2889685742159dfe0d19a3880ca3d2d1d69 F ext/rbu/sqlite3rbu.c d4ddf8f0e93772556e452a6c2814063cf47efb760a0834391a9d0cd9859fa4b9 F ext/rbu/sqlite3rbu.h 9d923eb135c5d04aa6afd7c39ca47b0d1d0707c100e02f19fdde6a494e414304 F ext/rbu/test_rbu.c ee6ede75147bc081fe9bc3931e6b206277418d14d3fbceea6fdc6216d9b47055 -F ext/recover/dbdata.c fc7147a68422cbbbaa481ee92ae1752cc25f5a24302bece1c70dcb76345bd736 +F ext/recover/dbdata.c b7746c2ec801b453840831311be4b31f8c8f9dd97791060a69bbf12392c78949 F ext/recover/recover1.test c484d01502239f11b61f23c1cee9f5dd19fa17617f8974e42e74d64639c524cf F ext/recover/recover_common.tcl a61306c1eb45c0c3fc45652c35b2d4ec19729e340bdf65a272ce4c229cefd85a F ext/recover/recoverbuild.test c74170e0f7b02456af41838afeb5353fdb985a48cc2331d661bbabbca7c6b8e3 @@ -2143,8 +2143,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 4c055b7a6e4533e1e571773456226ca7038ce372df3eedbbbcd9a81e8652a6cf -R b802cc74f401db4801f35603d41bed2d +P cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db +R 4a3a6ee7f774b8b1af828ef09f5272a7 U drh -Z fcd4003bc0cfee07fff53afb131d1b17 +Z 25e836823c87bebe85ade3cc7e139b02 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 47522a290b..6a5cca1b23 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db \ No newline at end of file +988c3179e978a3a6d42541e9c7a2ab98150383671810926503376ed808f150ff \ No newline at end of file From 89fcfbb424b1c36b97acd151267adb7c574f8240 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 29 Nov 2023 16:22:39 +0000 Subject: [PATCH 272/347] Fix signed integer overflow in fts5. FossilOrigin-Name: 60e46c7ec68fd8caaed960ca06d98fb06855b2d0bb860dd2fb7b5e89a5e9c7b4 --- ext/fts5/fts5_index.c | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 993069f490..4b7c8d3335 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -4276,7 +4276,7 @@ static void fts5WriteDlidxAppend( } if( pDlidx->bPrevValid ){ - iVal = iRowid - pDlidx->iPrev; + iVal = (u64)iRowid - (u64)pDlidx->iPrev; }else{ i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); assert( pDlidx->buf.n==0 ); diff --git a/manifest b/manifest index e69bda2b13..13a7da6a20 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\supdates\sinto\sthis\sbranch. -D 2023-11-28T19:43:08.965 +C Fix\ssigned\sinteger\soverflow\sin\sfts5. +D 2023-11-29T16:22:39.960 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c 5d557c7ebefaeac5a5111cc47d4fee8a2fc6bc15245d5c99eebf53dd04bf794e F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c de7bc351d9b7e0c8891db93b211fd9e137edef2cec832dcedfb32d4a2929c0d2 +F ext/fts5/fts5_index.c bafdef8be40a20bb86a131af4f09b56919f416fa13d1a86af1bf92bad9a2870d F ext/fts5/fts5_main.c 55b53085dbd1693b5735463198a8d124dfbc27f08311c839637b44b8254ef7cb F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab F ext/jni/src/org/sqlite/jni/capi/CommitHookCallback.java 482f53dfec9e3ac2a9070d3fceebd56250932aaaf7c4f5bc8de29fc011416e0c F ext/jni/src/org/sqlite/jni/capi/ConfigLogCallback.java b995ca412f59b631803b93aa5b3684fce62e335d1e123207084c054abfd488d4 -F ext/jni/src/org/sqlite/jni/capi/ConfigSqlLogCallback.java e5723900b6458bc6288f52187090a78ebe0a20f403ac7c887ec9061dfe51aba7 w ext/jni/src/org/sqlite/jni/capi/ConfigSqllogCallback.java +F ext/jni/src/org/sqlite/jni/capi/ConfigSqlLogCallback.java e5723900b6458bc6288f52187090a78ebe0a20f403ac7c887ec9061dfe51aba7 F ext/jni/src/org/sqlite/jni/capi/NativePointerHolder.java b7036dcb1ef1b39f1f36ac605dde0ff1a24a9a01ade6aa1a605039443e089a61 F ext/jni/src/org/sqlite/jni/capi/OutputPointer.java 246b0e66c4603f41c567105a21189d138aaf8c58203ecd4928802333da553e7c F ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java 97352091abd7556167f4799076396279a51749fdae2b72a6ba61cd39b3df0359 @@ -2146,8 +2146,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 9be8969edd49e3da96fb8ac2279aff6fe2e215d6ac55162b4734aca1b6316580 4c055b7a6e4533e1e571773456226ca7038ce372df3eedbbbcd9a81e8652a6cf -R ec1a7fd48afc240687b4ab2330f993e9 +P 554fc13f2ca5f2ebd9ad0206034c25b556ff40db3106051c5e539f2e142e88ea +R 5c7c1632cd09d2b131adef895eab90a8 U dan -Z 65a112da054f34cd19f6d265591002ed +Z 009b023eba91b6acc35beb7d051a677a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4ca4cbb4d0..df2c2789ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -554fc13f2ca5f2ebd9ad0206034c25b556ff40db3106051c5e539f2e142e88ea \ No newline at end of file +60e46c7ec68fd8caaed960ca06d98fb06855b2d0bb860dd2fb7b5e89a5e9c7b4 \ No newline at end of file From da2578391d5ae7cc8ba97f00994d14cdf6ab4a1f Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 29 Nov 2023 17:36:54 +0000 Subject: [PATCH 273/347] The json_patch() function now operates exclusively on JSONB. This patch also includes improvements to JSONB debug printing routines. FossilOrigin-Name: fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 205 +++++++++++--------------------------------------- 3 files changed, 51 insertions(+), 168 deletions(-) diff --git a/manifest b/manifest index 275eb80263..a779c2b472 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\sjsonb\sbranch. -D 2023-11-29T12:18:02.265 +C The\sjson_patch()\sfunction\snow\soperates\sexclusively\son\sJSONB.\s\sThis\spatch\nalso\sincludes\simprovements\sto\sJSONB\sdebug\sprinting\sroutines. +D 2023-11-29T17:36:54.076 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c aebeb6c1beea6a143223139e9b168b4187aff02c60a49daa6bcafadafcb6ee15 +F src/json.c 00b55c9e54047e1412fb7303e954e996c84bb8b06d3e376b12f49178c4fd7b69 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 5ab790736d943e08f097efcee5cfbf0d83c65b0a53f273060330ba719affa5e5 cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db -R e74b99bb550123c4a0166b07666ffd15 +P 1a59fcab2179cc3b52ecd3de7d2018db96ac149aaff521959773a517b8d9ac3e +R 883e1938b9afc8e6538b46e875ce6c0e U drh -Z 2ea38ce368a7d99793b6d4f302fe78c3 +Z 19b3c18296db473321d95b26a8cafb5e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2b29025123..f95e15f1a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1a59fcab2179cc3b52ecd3de7d2018db96ac149aaff521959773a517b8d9ac3e \ No newline at end of file +fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6 \ No newline at end of file diff --git a/src/json.c b/src/json.c index d210e5e986..c0fca583a4 100644 --- a/src/json.c +++ b/src/json.c @@ -2033,15 +2033,6 @@ static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; } -static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ - if( p1->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p2, p1->u.zJContent, p1->n); - }else if( p2->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p1, p2->u.zJContent, p2->n); - }else{ - return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; - } -} /* ** Search along zPath to find the node specified. Return a pointer @@ -2327,25 +2318,6 @@ static void jsonWrongNumArgs( sqlite3_free(zMsg); } -/* -** Mark all NULL entries in the Object passed in as JNODE_REMOVE. -*/ -static void jsonRemoveAllNulls(JsonNode *pNode){ - int i, n; - assert( pNode->eType==JSON_OBJECT ); - n = pNode->n; - for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ - switch( pNode[i].eType ){ - case JSON_NULL: - pNode[i].jnFlags |= JNODE_REMOVE; - break; - case JSON_OBJECT: - jsonRemoveAllNulls(&pNode[i]); - break; - } - } -} - /**************************************************************************** ** Utility routines for dealing with the binary BLOB representation of JSON ****************************************************************************/ @@ -3111,7 +3083,9 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; n = 5; } - if( i+sz+n>pParse->nBlob ){ + if( i+sz+n > pParse->nBlob + && i+sz+n > pParse->nBlob-pParse->delta + ){ sz = 0; n = 0; } @@ -4314,18 +4288,33 @@ static void jsonDebugPrintBlob( u32 i, n, nn, sz = 0; int showContent = 1; u8 x = pParse->aBlob[iStart] & 0x0f; + u32 savedNBlob = pParse->nBlob; printf("%5d:%*s", iStart, nIndent, ""); + if( pParse->nBlobAlloc>pParse->nBlob ){ + pParse->nBlob = pParse->nBlobAlloc; + } nn = n = jsonbPayloadSize(pParse, iStart, &sz); if( nn==0 ) nn = 1; if( sz>0 && xaBlob[iStart+i]); - if( n==0 || iStart+n+sz>iEnd ){ + if( n==0 ){ printf(" ERROR invalid node size\n"); iStart = n==0 ? iStart+1 : iEnd; continue; } + pParse->nBlob = savedNBlob; + if( iStart+n+sz>iEnd ){ + iEnd = iStart+n+sz; + if( iEnd>pParse->nBlob ){ + if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){ + iEnd = pParse->nBlobAlloc; + }else{ + iEnd = pParse->nBlob; + } + } + } printf(" <-- "); switch( x ){ case JSONB_NULL: printf("null"); break; @@ -4379,7 +4368,11 @@ static void jsonShowParse(JsonParse *pParse){ printf("NULL pointer\n"); return; }else{ - printf("%u byte JSONB:\n", pParse->nBlob); + printf("nBlobAlloc = %u\n", pParse->nBlobAlloc); + printf("nBlob = %u\n", pParse->nBlob); + printf("delta = %d\n", pParse->delta); + if( pParse->nBlob==0 ) return; + printf("content (bytes 0..%u):\n", pParse->nBlob-1); } jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0); } @@ -4735,79 +4728,6 @@ json_extract_error: return; } -/* This is the RFC 7396 MergePatch algorithm. -*/ -static JsonNode *jsonMergePatch( - JsonParse *pParse, /* The JSON parser that contains the TARGET */ - u32 iTarget, /* Node of the TARGET in pParse */ - JsonNode *pPatch /* The PATCH */ -){ - u32 i, j; - u32 iRoot; - JsonNode *pTarget; - if( pPatch->eType!=JSON_OBJECT ){ - return pPatch; - } - assert( iTargetnNode ); - pTarget = &pParse->aNode[iTarget]; - assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); - if( pTarget->eType!=JSON_OBJECT ){ - jsonRemoveAllNulls(pPatch); - return pPatch; - } - iRoot = iTarget; - for(i=1; in; i += jsonNodeSize(&pPatch[i+1])+1){ - u32 nKey; - const char *zKey; - if( pPatch[i].eType!=JSON_STRING ){ - pParse->nErr = 1; - return 0; - } - assert( pPatch[i].eU==1 ); - nKey = pPatch[i].n; - zKey = pPatch[i].u.zJContent; - for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ - assert( pTarget[j].eType==JSON_STRING ); - assert( pTarget[j].jnFlags & JNODE_LABEL ); - if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ - if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break; - if( pPatch[i+1].eType==JSON_NULL ){ - pTarget[j+1].jnFlags |= JNODE_REMOVE; - }else{ - JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); - if( pNew==0 ) return 0; - if( pNew!=&pParse->aNode[iTarget+j+1] ){ - jsonParseAddSubstNode(pParse, iTarget+j+1); - jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew)); - } - pTarget = &pParse->aNode[iTarget]; - } - break; - } - } - if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ - int iStart; - JsonNode *pApnd; - u32 nApnd; - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - pApnd = &pPatch[i+1]; - if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd); - nApnd = jsonNodeSize(pApnd); - jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd)); - if( pParse->oom ) return 0; - pParse->aNode[iStart].n = 1+nApnd; - pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; - pParse->aNode[iRoot].u.iAppend = iStart; - JSON_VVA( pParse->aNode[iRoot].eU = 2 ); - iRoot = iStart; - pTarget = &pParse->aNode[iTarget]; - } - } - return pTarget; -} - - /* ** Return codes for jsonMergePatchBlob() */ @@ -4908,9 +4828,10 @@ static int jsonMergePatchBlob( } x = pTarget->aBlob[iTarget] & 0x0f; if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ - static const u8 emptyObject[] = { JSONB_OBJECT }; n = jsonbPayloadSize(pTarget, iTarget, &sz); - jsonBlobEdit(pTarget, iTarget, sz, emptyObject, 1); /* Line 06 */ + jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0); + x = pTarget->aBlob[iTarget]; + pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; } n = jsonbPayloadSize(pPatch, iPatch, &sz); if( n==0 ) return JSON_MERGE_BADPATCH; @@ -5019,7 +4940,7 @@ static int jsonMergePatchBlob( } } } - jsonAfterEditSizeAdjust(pTarget, iTarget); + if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget); return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; } @@ -5034,65 +4955,27 @@ static void jsonPatchFunc( int argc, sqlite3_value **argv ){ - JsonParse *pX; /* The JSON that is being patched */ - JsonParse *pY; /* The patch */ - JsonNode *pResult; /* The result of the merge */ + JsonParse *pTarget; /* The TARGET */ + JsonParse *pPatch; /* The PATCH */ + int rc; /* Result code */ UNUSED_PARAMETER(argc); - if( jsonFuncArgMightBeBinary(argv[0]) - && jsonFuncArgMightBeBinary(argv[1]) - ){ - JsonParse target, patch; - sqlite3 *db; - int rc; - memset(&target, 0, sizeof(target)); - memset(&patch, 0, sizeof(patch)); - target.aBlob = (u8*)sqlite3_value_blob(argv[0]); - target.nBlob = sqlite3_value_bytes(argv[0]); - patch.aBlob = (u8*)sqlite3_value_blob(argv[1]); - patch.nBlob = sqlite3_value_bytes(argv[1]); - db = sqlite3_context_db_handle(ctx); - if( db->mallocFailed ) return; - if( !jsonBlobMakeEditable(&target, patch.nBlob) ) return; - rc = jsonMergePatchBlob(&target, 0, &patch, 0); - if( rc ){ - if( rc==JSON_MERGE_OOM ){ - sqlite3_result_error_nomem(ctx); - }else{ - sqlite3_result_error(ctx, "malformed JSON", -1); - } - jsonParseReset(&target); + assert( argc==2 ); + pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); + if( pTarget==0 ) return; + pPatch = jsonParseFuncArg(ctx, argv[1], 0); + if( pPatch ){ + rc = jsonMergePatchBlob(pTarget, 0, pPatch, 0); + if( rc==JSON_MERGE_OK ){ + jsonReturnParse(ctx, pTarget); + }else if( rc==JSON_MERGE_OOM ){ + sqlite3_result_error_nomem(ctx); }else{ - int flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - if( flgs & JSON_BLOB ){ - sqlite3_result_blob(ctx, target.aBlob, target.nBlob, SQLITE_DYNAMIC); - }else{ - JsonString s; - jsonStringInit(&s, ctx); - jsonXlateBlobToText(&target, 0, &s); - jsonReturnString(&s); - jsonParseReset(&target); - } + sqlite3_result_error(ctx, "malformed JSON", -1); } - return; - } - pX = jsonParseCached(ctx, argv[0], ctx, 1); - if( pX==0 ) return; - assert( pX->hasMod==0 ); - pX->hasMod = 1; - pY = jsonParseCached(ctx, argv[1], ctx, 1); - if( pY==0 ) return; - pX->useMod = 1; - pY->useMod = 1; - pResult = jsonMergePatch(pX, 0, pY->aNode); - assert( pResult!=0 || pX->oom || pX->nErr ); - if( pX->oom ){ - sqlite3_result_error_nomem(ctx); - }else if( pX->nErr ){ - sqlite3_result_error(ctx, "malformed JSON", -1); - }else if( pResult ){ - jsonReturnNodeAsJson(pX, pResult, ctx, 0, 0); + jsonParseFree(pPatch); } + jsonParseFree(pTarget); } From 694beecf2bef064f08426a23fd7c8097285c83c5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 29 Nov 2023 20:06:49 +0000 Subject: [PATCH 274/347] Convert the json_error_position() routine to use only JSONB internally. FossilOrigin-Name: e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad --- manifest | 12 +++++------ manifest.uuid | 2 +- src/json.c | 55 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index a779c2b472..393b7c556d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sjson_patch()\sfunction\snow\soperates\sexclusively\son\sJSONB.\s\sThis\spatch\nalso\sincludes\simprovements\sto\sJSONB\sdebug\sprinting\sroutines. -D 2023-11-29T17:36:54.076 +C Convert\sthe\sjson_error_position()\sroutine\sto\suse\sonly\sJSONB\sinternally. +D 2023-11-29T20:06:49.393 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 00b55c9e54047e1412fb7303e954e996c84bb8b06d3e376b12f49178c4fd7b69 +F src/json.c d93f5e7d0e0a50c5842657439c3e820802f862da0d2aaf28df08cb3528acc0aa F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 1a59fcab2179cc3b52ecd3de7d2018db96ac149aaff521959773a517b8d9ac3e -R 883e1938b9afc8e6538b46e875ce6c0e +P fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6 +R de34ae25adb13e4614fb6787c64e8886 U drh -Z 19b3c18296db473321d95b26a8cafb5e +Z beaa9ff647aa958b069175a31fff1f35 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f95e15f1a1..0114d98f13 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6 \ No newline at end of file +e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad \ No newline at end of file diff --git a/src/json.c b/src/json.c index c0fca583a4..ba15e12e17 100644 --- a/src/json.c +++ b/src/json.c @@ -5438,25 +5438,48 @@ static void jsonErrorFunc( int argc, sqlite3_value **argv ){ - JsonParse *p; /* The parse */ + i64 iErrPos = 0; /* Error position to be returned */ + JsonParse s; + + assert( argc==1 ); UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv[0], 0, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else if( p->nErr==0 ){ - sqlite3_result_int(ctx, 0); - }else{ - int n = 1; - u32 i; - const char *z = (const char*)sqlite3_value_text(argv[0]); - for(i=0; iiErr && ALWAYS(z[i]); i++){ - if( (z[i]&0xc0)!=0x80 ) n++; + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_NULL: { + return; + } + case SQLITE_BLOB: { + if( !jsonFuncArgMightBeBinary(argv[0]) ) iErrPos = 1; + break; + } + default: { + memset(&s, 0, sizeof(s)); + s.zJson = (char*)sqlite3_value_text(argv[0]); + s.nJson = sqlite3_value_bytes(argv[0]); + if( s.nJson==0 ){ + iErrPos = 1; + break; + } + if( s.zJson==0 ){ + sqlite3_result_error_nomem(ctx); + return; + } + if( jsonConvertTextToBlob(&s,0) ){ + u32 k; + if( s.oom ){ + sqlite3_result_error_nomem(ctx); + jsonParseReset(&s); + return; + } + /* Convert byte-offset s.iErr into a character offset */ + for(k=0; k Date: Thu, 30 Nov 2023 00:52:33 +0000 Subject: [PATCH 275/347] Convert json_insert(), json_replace(), json_set() to use JSONB internally. Mostly working, but some corner cases are still not quite right. FossilOrigin-Name: 99c8f6bd5c9a31b6d00f92e383bec8a8235ed553916ad59adbb1b7663f6ebff1 --- manifest | 15 +- manifest.uuid | 2 +- src/json.c | 784 ++++---------------------------------------------- 3 files changed, 72 insertions(+), 729 deletions(-) diff --git a/manifest b/manifest index 393b7c556d..d50bd996f4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sthe\sjson_error_position()\sroutine\sto\suse\sonly\sJSONB\sinternally. -D 2023-11-29T20:06:49.393 +C Convert\sjson_insert(),\sjson_replace(),\sjson_set()\sto\suse\sJSONB\sinternally.\nMostly\sworking,\sbut\ssome\scorner\scases\sare\sstill\snot\squite\sright. +D 2023-11-30T00:52:33.320 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d93f5e7d0e0a50c5842657439c3e820802f862da0d2aaf28df08cb3528acc0aa +F src/json.c 8b509dead923cd44f31e4866d1024f1b602bdf01c9d41a08cc36aa5f1203fdf8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fee19d0098242110d2c44ec7b9620c1210ef3f87913305f66ec85d277dd96ab6 -R de34ae25adb13e4614fb6787c64e8886 +P e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad +R 4c74c3d174d84fc0b9e05e4cabac42e8 +T *branch * jsonb-insert +T *sym-jsonb-insert * +T -sym-jsonb * U drh -Z beaa9ff647aa958b069175a31fff1f35 +Z 396365e2a8dc6b109097922e3ec543f6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0114d98f13..bed72fa4ce 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad \ No newline at end of file +99c8f6bd5c9a31b6d00f92e383bec8a8235ed553916ad59adbb1b7663f6ebff1 \ No newline at end of file diff --git a/src/json.c b/src/json.c index ba15e12e17..b3b4f993e6 100644 --- a/src/json.c +++ b/src/json.c @@ -371,16 +371,22 @@ struct JsonParse { */ #define JSON_MAX_DEPTH 1000 +/* +** Allowed values for the flgs argument to jsonParseFuncArg(); +*/ +#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ + /************************************************************************** ** Forward references **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); -static void jsonXlateNodeToBlob(JsonParse*,JsonNode*,JsonParse*); static int jsonParseAddNode(JsonParse*,u32,u32,const char*); static int jsonXlateBlobToNode(JsonParse *pParse, u32 i); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); +static void jsonReturnParse(sqlite3_context*,JsonParse*); +static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); /************************************************************************** ** Utility routines for dealing with JsonString objects @@ -592,159 +598,6 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ assert( p->nUsednAlloc ); } -/* -** The zIn[0..N] string is a JSON5 string literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. -*/ -static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ - u32 i; - jsonAppendChar(p, '"'); - while( N>0 ){ - for(i=0; i0 ){ - jsonAppendRawNZ(p, zIn, i); - if( i>=N ) break; - zIn += i; - N -= i; - } - if( N<2 ){ - p->eErr |= JSTRING_MALFORMED; - break; - } - if( zIn[0]=='"' ){ - jsonAppendRawNZ(p, "\\\"", 2); - zIn++; - N--; - continue; - } - assert( zIn[0]=='\\' ); - switch( (u8)zIn[1] ){ - case '\'': - jsonAppendChar(p, '\''); - break; - case 'v': - jsonAppendRawNZ(p, "\\u0009", 6); - break; - case 'x': - if( N<4 ){ - N = 2; - p->eErr |= JSTRING_MALFORMED; - break; - } - jsonAppendRawNZ(p, "\\u00", 4); - jsonAppendRawNZ(p, &zIn[2], 2); - zIn += 2; - N -= 2; - break; - case '0': - jsonAppendRawNZ(p, "\\u0000", 6); - break; - case '\r': - if( N>2 && zIn[2]=='\n' ){ - zIn++; - N--; - } - break; - case '\n': - break; - case 0xe2: /* \ followed by U+2028 or U+2029 line terminator ignored */ - if( N<4 - || 0x80!=(u8)zIn[2] - || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) - ){ - N = 2; - p->eErr |= JSTRING_MALFORMED; - break; - } - assert( N>=4 ); - assert( 0x80==(u8)zIn[2] ); - assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); - zIn += 2; - N -= 2; - break; - default: - jsonAppendRawNZ(p, zIn, 2); - break; - } - assert( N>=2 ); - zIn += 2; - N -= 2; - } - jsonAppendChar(p, '"'); -} - -/* -** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. -*/ -static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ - char *zBuf = sqlite3_malloc64( N+1 ); - if( zBuf==0 ){ - p->eErr |= JSTRING_OOM; - return; - } - memcpy(zBuf, zIn, N); - zBuf[N] = 0; - zIn = zBuf; - if( zIn[0]=='+' ){ - zIn++; - N--; - }else if( zIn[0]=='-' ){ - jsonAppendChar(p, '-'); - zIn++; - N--; - } - if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ - sqlite3_int64 i = 0; - int rc = sqlite3DecOrHexToI64(zIn, &i); - if( rc<=1 ){ - jsonPrintf(100,p,"%lld",i); - }else{ - assert( rc==2 ); - jsonAppendRawNZ(p, "9.0e999", 7); - } - }else{ - assert( N>0 ); - jsonAppendRawNZ(p, zIn, N); - } - sqlite3_free(zBuf); -} - -/* -** The zIn[0..N] string is a JSON5 real literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. -*/ -static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ - u32 i; - if( zIn[0]=='+' ){ - zIn++; - N--; - }else if( zIn[0]=='-' ){ - jsonAppendChar(p, '-'); - zIn++; - N--; - } - if( zIn[0]=='.' ){ - jsonAppendChar(p, '0'); - } - for(i=0; i0 ){ - jsonAppendRawNZ(p, zIn, N); - } -} - /* ** Append an sqlite3_value (such as a function parameter) to the JSON ** string under construction in p. @@ -895,194 +748,6 @@ static void jsonParseFree(JsonParse *pParse){ } } -/* -** Add a cleanup task to the JsonParse object. -** -** If an OOM occurs, the cleanup operation happens immediately -** and this function returns SQLITE_NOMEM. -*/ -static int jsonParseAddCleanup( - JsonParse *pParse, /* Add the cleanup task to this parser */ - void(*xOp)(void*), /* The cleanup task */ - void *pArg /* Argument to the cleanup */ -){ - JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) ); - if( pTask==0 ){ - pParse->oom = 1; - xOp(pArg); - return SQLITE_ERROR; - } - pTask->pJCNext = pParse->pClup; - pParse->pClup = pTask; - pTask->xOp = xOp; - pTask->pArg = pArg; - return SQLITE_OK; -} - -/* -** Translate the JsonNode pNode into a pure JSON string and -** append that string on pOut. Subsubstructure is also included. -*/ -static void jsonXlateNodeToText( - JsonParse *pParse, /* the complete parse of the JSON */ - JsonNode *pNode, /* The node to render */ - JsonString *pOut /* Write JSON here */ -){ - assert( pNode!=0 ); - while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ - u32 idx = (u32)(pNode - pParse->aNode); - u32 i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pNode = &pParse->aNode[i+1]; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - switch( pNode->eType ){ - default: { - assert( pNode->eType==JSON_NULL ); - jsonAppendRawNZ(pOut, "null", 4); - break; - } - case JSON_TRUE: { - jsonAppendRawNZ(pOut, "true", 4); - break; - } - case JSON_FALSE: { - jsonAppendRawNZ(pOut, "false", 5); - break; - } - case JSON_STRING: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - }else if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); - }else{ - jsonAppendChar(pOut, '"'); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - jsonAppendChar(pOut, '"'); - } - break; - } - case JSON_REAL: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_INT: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_ARRAY: { - u32 j = 1; - jsonAppendChar(pOut, '['); - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonXlateNodeToText(pParse, &pNode[j], pOut); - } - j += jsonNodeSize(&pNode[j]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, ']'); - break; - } - case JSON_OBJECT: { - u32 j = 1; - jsonAppendChar(pOut, '{'); - for(;;){ - while( jn ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonXlateNodeToText(pParse, &pNode[j], pOut); - jsonAppendChar(pOut, ':'); - jsonXlateNodeToText(pParse, &pNode[j+1], pOut); - } - j += 1 + jsonNodeSize(&pNode[j+1]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, '}'); - break; - } - } -} - -/* -** Make the return value of an SQL function be the JSON encoded by pNode. -** -** By default, the node is rendered as RFC-8259 JSON text (canonical -** JSON text without any JSON-5 enhancements). However if the -** JSON_BLOB flag is set in the user-data for the function, then the -** node is rendered into the JSONB format and returned as a BLOB. -*/ -static void jsonReturnNodeAsJson( - JsonParse *pParse, /* The complete JSON */ - JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx, /* Return value for this function */ - int bGenerateAlt, /* Also store the rendered text in zAlt */ - int omitSubtype /* Do not call sqlite3_result_subtype() */ -){ - int flags; - JsonString s; - if( pParse->oom ){ - sqlite3_result_error_nomem(pCtx); - return; - } - if( pParse->nErr ){ - return; - } - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); - if( flags & JSON_BLOB ){ - JsonParse x; - memset(&x, 0, sizeof(x)); - jsonXlateNodeToBlob(pParse, pNode, &x); - if( x.oom ){ - sqlite3_result_error_nomem(pCtx); - sqlite3_free(x.aBlob); - }else{ - sqlite3_result_blob(pCtx, x.aBlob, x.nBlob, sqlite3_free); - } - }else{ - jsonStringInit(&s, pCtx); - jsonXlateNodeToText(pParse, pNode, &s); - if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ - pParse->zAlt = sqlite3RCStrRef(s.zBuf); - pParse->nAlt = s.nUsed; - } - jsonReturnString(&s); - if( !omitSubtype ) sqlite3_result_subtype(pCtx, JSON_SUBTYPE); - } -} - /* ** Translate a single byte of Hex into an integer. ** This routine only works if h really is a valid hexadecimal @@ -1178,52 +843,6 @@ static int jsonParseAddNode( return pParse->nNode++; } -/* -** Add an array of new nodes to the current pParse->aNode array. -** Return the index of the first node added. -** -** If an OOM error occurs, set pParse->oom. -*/ -static void jsonParseAddNodeArray( - JsonParse *pParse, /* Append the node to this object */ - JsonNode *aNode, /* Array of nodes to add */ - u32 nNode /* Number of elements in aNew */ -){ - assert( aNode!=0 ); - assert( nNode>=1 ); - if( pParse->nNode + nNode > pParse->nAlloc ){ - u32 nNew = pParse->nNode + nNode; - JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode)); - if( aNew==0 ){ - pParse->oom = 1; - return; - } - pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode); - pParse->aNode = aNew; - } - memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode)); - pParse->nNode += nNode; -} - -/* -** Add a new JSON_SUBST node. The node immediately following -** this new node will be the substitute content for iNode. -*/ -static int jsonParseAddSubstNode( - JsonParse *pParse, /* Add the JSON_SUBST here */ - u32 iNode /* References this node */ -){ - int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0); - if( pParse->oom ) return -1; - pParse->aNode[iNode].jnFlags |= JNODE_REPLACE; - pParse->aNode[idx].eU = 4; - pParse->aNode[idx].u.iPrev = pParse->iSubst; - pParse->iSubst = idx; - pParse->hasMod = 1; - pParse->useMod = 1; - return idx; -} - /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ @@ -3446,138 +3065,6 @@ static int jsonXlateBlobToNode(JsonParse *pParse, u32 i){ return i+x+sz; } -/* -** Translate pNode (which is always a node found in pParse->aNode[]) into -** the JSONB representation and append the translation onto the end of the -** pOut->aBlob[] array. -*/ -static void jsonXlateNodeToBlob( - JsonParse *pParse, /* the complete parse of the JSON */ - JsonNode *pNode, /* The node to render */ - JsonParse *pOut /* Write the BLOB rendering of JSON here */ -){ - assert( pNode!=0 ); - while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ - u32 idx = (u32)(pNode - pParse->aNode); - u32 i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pNode = &pParse->aNode[i+1]; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - switch( pNode->eType ){ - default: { - assert( pNode->eType==JSON_NULL ); - jsonBlobAppendNodeType(pOut, JSONB_NULL, 0); - break; - } - case JSON_TRUE: { - jsonBlobAppendNodeType(pOut, JSONB_TRUE, 0); - break; - } - case JSON_FALSE: { - jsonBlobAppendNodeType(pOut, JSONB_FALSE, 0); - break; - } - case JSON_STRING: { - int op; - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( memchr(pNode->u.zJContent, '"', pNode->n)==0 - && memchr(pNode->u.zJContent, '\\', pNode->n)==0 - ){ - op = JSONB_TEXT; - }else{ - op = JSONB_TEXTRAW; - } - }else if( pNode->jnFlags & JNODE_JSON5 ){ - op = JSONB_TEXT5; - }else{ - op = JSONB_TEXTJ; - } - jsonBlobAppendNodeType(pOut, op, pNode->n); - jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); - break; - } - case JSON_REAL: { - int op; - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - op = JSONB_FLOAT5; - }else{ - assert( pNode->n>0 ); - op = JSONB_FLOAT; - } - jsonBlobAppendNodeType(pOut, op, pNode->n); - jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); - break; - } - case JSON_INT: { - int op; - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - op = JSONB_INT5; - }else{ - assert( pNode->n>0 ); - op = JSONB_INT; - } - jsonBlobAppendNodeType(pOut, op, pNode->n); - jsonBlobAppendNBytes(pOut, (const u8*)pNode->u.zJContent, pNode->n); - break; - } - case JSON_ARRAY: { - u32 j = 1; - u32 iStart, iThis = pOut->nBlob; - jsonBlobAppendNodeType(pOut, JSONB_ARRAY, pParse->nJson*2); - iStart = pOut->nBlob; - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonXlateNodeToBlob(pParse, &pNode[j], pOut); - } - j += jsonNodeSize(&pNode[j]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); - break; - } - case JSON_OBJECT: { - u32 j = 1; - u32 iStart, iThis = pOut->nBlob; - jsonBlobAppendNodeType(pOut, JSONB_OBJECT, pParse->nJson*2); - iStart = pOut->nBlob; - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonXlateNodeToBlob(pParse, &pNode[j], pOut); - jsonXlateNodeToBlob(pParse, &pNode[j+1], pOut); - } - j += 1 + jsonNodeSize(&pNode[j+1]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonBlobChangePayloadSize(pOut, iThis, pOut->nBlob - iStart); - break; - } - } -} - /* ** Given that a JSONB_ARRAY object starts at offset i, return ** the number of entries in that array. @@ -3750,13 +3237,12 @@ static u32 jsonLookupBlobStep( if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+pParse->nIns) ){ nIns = ix.nBlob + nKey + pParse->nIns; jsonBlobEdit(pParse, j, 0, 0, nIns); - assert( pParse->nBlob + pParse->nIns <= pParse->nBlobAlloc ); memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); k += nKey; memcpy(&pParse->aBlob[k], pParse->aIns, pParse->nIns); - jsonAfterEditSizeAdjust(pParse, iRoot); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); } return j; } @@ -3805,14 +3291,14 @@ static u32 jsonLookupBlobStep( j += n+sz; } if( j>iEnd ) return JSON_BLOB_ERROR; - if( k>1 ) return JSON_BLOB_NOTFOUND; + if( k>0 ) return JSON_BLOB_NOTFOUND; if( pParse->eEdit>=JEDIT_INS && jsonBlobMakeEditable(pParse, pParse->nIns) ){ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); jsonBlobEdit(pParse, j, 0, pParse->aIns, pParse->nIns); - jsonAfterEditSizeAdjust(pParse, iRoot); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return j; } }else{ @@ -4088,9 +3574,27 @@ static int jsonFunctionArgToBlob( break; } } - return 0; + if( pParse->oom ){ + sqlite3_result_error_nomem(ctx); + return 1; + }else{ + return 0; + } } - + +/* +** Generate a bad path error for json_extract() +*/ +static void jsonBadPathError( + sqlite3_context *ctx, /* The function call containing the error */ + const char *zPath /* The path with the problem */ +){ + sqlite3 *db = sqlite3_context_db_handle(ctx); + char *zMsg = sqlite3MPrintf(db, "bad JSON path: %Q", zPath); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3DbFree(db, zMsg); +} + /* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent ** arguments come in parse where each pair contains a JSON path and ** content to insert or set at that patch. Do the updates @@ -4109,61 +3613,57 @@ static void jsonInsertIntoBlob( u32 rc = 0; const char *zPath = 0; int flgs; - JsonParse px, ax; + JsonParse *p; + JsonParse ax; assert( (argc&1)==1 ); - memset(&px, 0, sizeof(px)); - px.nBlob = sqlite3_value_bytes(argv[0]); - px.aBlob = (u8*)sqlite3_value_blob(argv[0]); - if( px.aBlob==0 ) return; + flgs = argc==1 ? 0 : JSON_EDITABLE; + p = jsonParseFuncArg(ctx, argv[0], flgs); + if( p==0 ) return; for(i=1; inBlob, ax.aBlob, ax.nBlob); + } + rc = 0; + }else{ + p->eEdit = eEdit; + p->nIns = ax.nBlob; + p->aIns = ax.aBlob; + p->delta = 0; + rc = jsonLookupBlobStep(p, 0, zPath+1, 0); } - px.eEdit = eEdit; - px.nIns = ax.nBlob; - px.aIns = ax.aBlob; - px.delta = 0; - rc = jsonLookupBlobStep(&px, 0, zPath+1, 0); jsonParseReset(&ax); if( rc==JSON_BLOB_NOTFOUND ) continue; if( JSON_BLOB_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; } - flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - if( flgs & JSON_BLOB ){ - sqlite3_result_blob(ctx, px.aBlob, px.nBlob, - px.nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); - }else{ - JsonString s; - jsonStringInit(&s, ctx); - jsonXlateBlobToText(&px, 0, &s); - jsonReturnString(&s); - jsonParseReset(&px); - } + jsonReturnParse(ctx, p); + jsonParseFree(p); return; jsonInsertIntoBlob_patherror: - jsonParseReset(&px); + jsonParseFree(p); if( rc==JSON_BLOB_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ - jsonPathSyntaxError(zPath, ctx); + jsonBadPathError(ctx, zPath); } return; } -/* -** Allowed values for the flgs argument to jsonParseFuncArg(); -*/ -#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ - /* ** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ** from the SQL function argument pArg. Return a pointer to the new @@ -4531,19 +4031,6 @@ static void jsonArrayFunc( sqlite3_result_subtype(ctx, JSON_SUBTYPE); } -/* -** Generate a bad path error for json_extract() -*/ -static void jsonBadPathError( - sqlite3_context *ctx, /* The function call containing the error */ - const char *zPath /* The path with the problem */ -){ - sqlite3 *db = sqlite3_context_db_handle(ctx); - char *zMsg = sqlite3MPrintf(db, "bad JSON path: %Q", zPath); - sqlite3_result_error(ctx, zMsg, -1); - sqlite3DbFree(db, zMsg); -} - /* ** json_array_length(JSON) ** json_array_length(JSON, PATH) @@ -5074,103 +4561,6 @@ json_remove_return_null: return; } -/* -** Substitute the value at iNode with the pValue parameter. -*/ -static void jsonReplaceNode( - sqlite3_context *pCtx, - JsonParse *p, - int iNode, - sqlite3_value *pValue -){ - int idx = jsonParseAddSubstNode(p, iNode); - if( idx<=0 ){ - assert( p->oom ); - return; - } - switch( sqlite3_value_type(pValue) ){ - case SQLITE_NULL: { - jsonParseAddNode(p, JSON_NULL, 0, 0); - break; - } - case SQLITE_FLOAT: { - char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue)); - int n; - if( z==0 ){ - p->oom = 1; - break; - } - n = sqlite3Strlen30(z); - jsonParseAddNode(p, JSON_REAL, n, z); - jsonParseAddCleanup(p, sqlite3_free, z); - break; - } - case SQLITE_INTEGER: { - char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue)); - int n; - if( z==0 ){ - p->oom = 1; - break; - } - n = sqlite3Strlen30(z); - jsonParseAddNode(p, JSON_INT, n, z); - jsonParseAddCleanup(p, sqlite3_free, z); - - break; - } - case SQLITE_TEXT: { - const char *z = (const char*)sqlite3_value_text(pValue); - u32 n = (u32)sqlite3_value_bytes(pValue); - if( z==0 ){ - p->oom = 1; - break; - } - if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){ - char *zCopy = sqlite3_malloc64( n+1 ); - int k; - if( zCopy ){ - memcpy(zCopy, z, n); - zCopy[n] = 0; - jsonParseAddCleanup(p, sqlite3_free, zCopy); - }else{ - p->oom = 1; - sqlite3_result_error_nomem(pCtx); - } - k = jsonParseAddNode(p, JSON_STRING, n, zCopy); - assert( k>0 || p->oom ); - if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW; - break; - } - replace_with_json: - { - JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1); - if( pPatch==0 ){ - p->oom = 1; - break; - } - jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode); - /* The nodes copied out of pPatch and into p likely contain - ** u.zJContent pointers into pPatch->zJson. So preserve the - ** content of pPatch until p is destroyed. */ - assert( pPatch->nJPRef>=1 ); - pPatch->nJPRef++; - jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch); - } - break; - } - default: { - if( jsonFuncArgMightBeBinary(pValue) ){ - goto replace_with_json; - }else{ - jsonParseAddNode(p, JSON_NULL, 0, 0); - sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1); - p->nErr++; - } - break; - } - } -} - /* ** json_replace(JSON, PATH, VALUE, ...) ** @@ -5182,35 +4572,12 @@ static void jsonReplaceFunc( int argc, sqlite3_value **argv ){ - JsonParse *pParse; /* The parse */ - JsonNode *pNode; - const char *zPath; - u32 i; - if( argc<1 ) return; if( (argc&1)==0 ) { jsonWrongNumArgs(ctx, "replace"); return; } - if( jsonFuncArgMightBeBinary(argv[0]) && argc>=3 ){ - jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); - return; - } - pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); - if( pParse==0 ) return; - pParse->nJPRef++; - for(i=1; i<(u32)argc; i+=2){ - zPath = (const char*)sqlite3_value_text(argv[i]); - pParse->useMod = 1; - pNode = jsonLookup(pParse, zPath, 0, ctx); - if( pParse->nErr ) goto replace_err; - if( pNode ){ - jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); - } - } - jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0); -replace_err: - jsonParseFree(pParse); + jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); } @@ -5231,11 +4598,7 @@ static void jsonSetFunc( int argc, sqlite3_value **argv ){ - JsonParse *pParse; /* The parse */ - JsonNode *pNode; - const char *zPath; - u32 i; - int bApnd; + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); int bIsSet = (flags&JSON_ISSET)!=0; @@ -5244,30 +4607,7 @@ static void jsonSetFunc( jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } - if( jsonFuncArgMightBeBinary(argv[0]) && argc>=3 ){ - jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); - return; - } - pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); - if( pParse==0 ) return; - pParse->nJPRef++; - for(i=1; i<(u32)argc; i+=2){ - zPath = (const char*)sqlite3_value_text(argv[i]); - bApnd = 0; - pParse->useMod = 1; - pNode = jsonLookup(pParse, zPath, &bApnd, ctx); - if( pParse->oom ){ - sqlite3_result_error_nomem(ctx); - goto jsonSetDone; - }else if( pParse->nErr ){ - goto jsonSetDone; - }else if( pNode && (bApnd || bIsSet) ){ - jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); - } - } - jsonReturnNodeAsJson(pParse, pParse->aNode, ctx, 1, 0); -jsonSetDone: - jsonParseFree(pParse); + jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); } /* From bac2fe9f44aa3540cb4589e006ff17ed8bc49860 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 30 Nov 2023 10:00:25 +0000 Subject: [PATCH 276/347] Update some OPFS-related help text in WASM tests. Minor cleanups in speedtest1-worker.js. FossilOrigin-Name: 263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a --- ext/wasm/index-dist.html | 9 +++------ ext/wasm/index.html | 9 +++------ ext/wasm/speedtest1-worker.js | 23 +++++------------------ manifest | 18 +++++++++--------- manifest.uuid | 2 +- 5 files changed, 21 insertions(+), 40 deletions(-) diff --git a/ext/wasm/index-dist.html b/ext/wasm/index-dist.html index 36e21117e3..f5bcdc1cb2 100644 --- a/ext/wasm/index-dist.html +++ b/ext/wasm/index-dist.html @@ -46,6 +46,9 @@ file:// URLs.

    W{q^&u`@O5?fo`B!kOx{Es* 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 z

  • Any OPFS-related pages or tests require:
      +
    • An OPFS-capable browser released after February + 2023. Some tests will work with Chromium-based browsers + going back to around v102.
    • That the web server emit the so-called COOP and @@ -53,12 +56,6 @@ headers. althttpd requires the -enable-sab flag for that.
    • -
    • A very recent version of a Chromium-based browser - (v102 at least, possibly newer). OPFS support in the - other major browsers is pending. Development and testing - is currently done against a dev-channel release of - Chrome (v111 as of 2023-02-10). -
  • diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 70ce0441e0..ebbfd6763d 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -31,6 +31,9 @@ file:// URLs.
  • Any OPFS-related pages or tests require:
      +
    • An OPFS-capable browser released after February + 2023. Some tests will work with Chromium-based browsers + going back to around v102.
    • That the web server emit the so-called COOP and @@ -38,12 +41,6 @@ headers. althttpd requires the -enable-sab flag for that.
    • -
    • A very recent version of a - Chromium-based browser (v102 at least, possibly newer). OPFS - support in the other major browsers is pending. Development - and testing is currently done against a dev-channel release - of Chrome (v111 as of 2023-02-10). -
  • diff --git a/ext/wasm/speedtest1-worker.js b/ext/wasm/speedtest1-worker.js index dedc320292..3abc589b59 100644 --- a/ext/wasm/speedtest1-worker.js +++ b/ext/wasm/speedtest1-worker.js @@ -7,9 +7,9 @@ } importScripts(speedtestJs); /** - If this environment contains OPFS, this function initializes it and - returns the name of the dir on which OPFS is mounted, else it returns - an empty string. + If this build includes WASMFS, this function initializes it and + returns the name of the dir on which OPFS is mounted, else it + returns an empty string. */ const wasmfsDir = function f(wasmUtil){ if(undefined !== f._) return f._; @@ -47,6 +47,7 @@ }; const log = (...args)=>logMsg('stdout',args); const logErr = (...args)=>logMsg('stderr',args); + const realSahName = 'opfs-sahpool-speedtest1'; const runSpeedtest = async function(cliFlagsArray){ const scope = App.wasm.scopedAllocPush(); @@ -57,7 +58,6 @@ ]; App.logBuffer.length = 0; const ndxSahPool = argv.indexOf('opfs-sahpool'); - const realSahName = 'opfs-sahpool-speedtest1'; if(ndxSahPool>0){ argv[ndxSahPool] = realSahName; log("Updated argv for opfs-sahpool: --vfs",realSahName); @@ -73,7 +73,7 @@ clearOnInit: true, verbosity: 2 }).then(PoolUtil=>{ - log("opfs-sahpool successfully installed as",realSahName); + log("opfs-sahpool successfully installed as",PoolUtil.vfsName); App.sqlite3.$SAHPoolUtil = PoolUtil; //console.log("sqlite3.oo1.OpfsSAHPoolDb =", App.sqlite3.oo1.OpfsSAHPoolDb); }); @@ -102,19 +102,6 @@ } }; - const sahpSanityChecks = function(sqlite3){ - log("Attempting OpfsSAHPoolDb sanity checks..."); - const db = new sqlite3.oo1.OpfsSAHPoolDb('opfs-sahpoool.db'); - const fn = db.filename; - db.exec([ - 'create table t(a);', - 'insert into t(a) values(1),(2),(3);' - ]); - db.close(); - sqlite3.wasm.sqlite3_wasm_vfs_unlink(sqlite3_vfs_find("opfs-sahpool"), fn); - log("SAH sanity checks done."); - }; - const EmscriptenModule = { print: log, printErr: logErr, diff --git a/manifest b/manifest index 0c8757df1c..766de1838f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sthe\srecovery\sextension,\sif\sa\spayload\ssize\sis\sunreasonably\slarge,\sit\sis\nprobably\scorrupt,\sso\struncate\sit. -D 2023-11-29T13:47:46.693 +C Update\ssome\sOPFS-related\shelp\stext\sin\sWASM\stests.\sMinor\scleanups\sin\sspeedtest1-worker.js. +D 2023-11-30T10:00:25.069 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -621,8 +621,8 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js e0153f9af6500805c6f09c0b3cfdb7d857e9d6863dbee9d50d1628fccf5f4b4d F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5653284071715 F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2 -F ext/wasm/index-dist.html 22379774f0ad4edcaaa8cf9c674c82e794cc557719a8addabed74eb8069d412e -F ext/wasm/index.html 4e7847b909f4ae0da8c829b150b79454050e53b3658431f138636257729cd42b +F ext/wasm/index-dist.html e91d76e4581185238fd3d42ed86ec600f7023ed3e3a944c5c356f25304bf1263 +F ext/wasm/index.html b31ce41c0da476d5ffcef23069b9d3415b419d65af5779096ebcfbcbade453a9 F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54 F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337 @@ -631,7 +631,7 @@ F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd84223150 F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe F ext/wasm/speedtest1-worker.html e33e2064bda572c0c3ebaec7306c35aa758d9d27e245d67e807f8cc4a9351cc5 -F ext/wasm/speedtest1-worker.js 315d26198c46be7c85e26fda15d80ef882424276abde25ffd8b026fb02a35d8c +F ext/wasm/speedtest1-worker.js 4d2ea70a3c24e05bdca78025202841f33d298c4fa9541a0070c3228661f89ecd F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 @@ -2143,8 +2143,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 cad269d5e274443c39203a56603b991accc0399135d436996fc039d1d28ec9db -R 4a3a6ee7f774b8b1af828ef09f5272a7 -U drh -Z 25e836823c87bebe85ade3cc7e139b02 +P 988c3179e978a3a6d42541e9c7a2ab98150383671810926503376ed808f150ff +R 73ee9ef0435c8cda72f87d3ec03cb69f +U stephan +Z 88450718d2dd1d2993dee724cc33772c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6a5cca1b23..af5689a923 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -988c3179e978a3a6d42541e9c7a2ab98150383671810926503376ed808f150ff \ No newline at end of file +263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a \ No newline at end of file From ad27c437fff38bd5ef06e609b9aa2eb457c4fc2a Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 12:04:14 +0000 Subject: [PATCH 277/347] New test cases for insert/set/replace with paths that indicate substructure that does not yet exist. FossilOrigin-Name: 146c717c51940b2139befc45ac74e7a1c36ef3c32fd3cfe35b334488eebe6298 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- test/json101.test | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index d50bd996f4..c9a863d174 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sjson_insert(),\sjson_replace(),\sjson_set()\sto\suse\sJSONB\sinternally.\nMostly\sworking,\sbut\ssome\scorner\scases\sare\sstill\snot\squite\sright. -D 2023-11-30T00:52:33.320 +C New\stest\scases\sfor\sinsert/set/replace\swith\spaths\sthat\sindicate\ssubstructure\nthat\sdoes\snot\syet\sexist. +D 2023-11-30T12:04:14.610 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1325,7 +1325,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 8f5d1a3350c36945c8b58f538e1c92cfd87fd50ab6f5e3d5f4cf3cdb03b9546d +F test/json101.test 861a5d75c296709471e88aa48d82095ed600dedd23ba29b904b91e64d319b7c3 F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2145,11 +2145,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 e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad -R 4c74c3d174d84fc0b9e05e4cabac42e8 -T *branch * jsonb-insert -T *sym-jsonb-insert * -T -sym-jsonb * +P 99c8f6bd5c9a31b6d00f92e383bec8a8235ed553916ad59adbb1b7663f6ebff1 +R 757617988b02e4847aadc75efb2ede4b U drh -Z 396365e2a8dc6b109097922e3ec543f6 +Z 50c123f2db65971d190ff942903bde3b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bed72fa4ce..01f2a5b9d8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -99c8f6bd5c9a31b6d00f92e383bec8a8235ed553916ad59adbb1b7663f6ebff1 \ No newline at end of file +146c717c51940b2139befc45ac74e7a1c36ef3c32fd3cfe35b334488eebe6298 \ No newline at end of file diff --git a/test/json101.test b/test/json101.test index 3c3b1a5ec0..d7b301e6df 100644 --- a/test/json101.test +++ b/test/json101.test @@ -1139,4 +1139,29 @@ do_execsql_test json101-23.2 { FROM (SELECT json_set('[]','$[#]',0,'$[#]',1) AS j); } {{[0,1]} 0 1} +# Insert/Set/Replace where the path specifies substructure that +# does not yet exist +# +proc tx x {return [string map [list ( \173 ) \175 ' \042 < \133 > \135] $x]} +foreach {id start path ins set repl} { + 1 {{}} {$.a.b.c} ('a':('b':('c':'NEW'))) ('a':('b':('c':'NEW'))) () + 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4) + 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':'NEW'))) ('a':('b':('c':'NEW'))) ('a':()) + 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':'NEW')>)> <0,1,2,('a':<('b':'NEW')>)> <0,1,2> + 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2> + 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':'NEW')>),2> <0,('a':<('b':'NEW')>),2> <0,(),2> + 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':'NEW')> <0,1,2,<('b':'NEW')> <0,1,2> + 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2> +} { + do_execsql_test json101-24.$id.insert { + SELECT json_insert($start,$path,'NEW'); + } [list [tx $ins]] + do_execsql_test json101-24.$id.set { + SELECT json_set($start,$path,'NEW'); + } [list [tx $set]] + do_execsql_test json101-24.$id.replace { + SELECT json_replace($start,$path,'NEW'); + } [list [tx $repl]] +} + finish_test From 48222bef664ad4b51cdadccfc44426c69a66a5f9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 16:16:10 +0000 Subject: [PATCH 278/347] New JSON test cases showing insert or set with missing substructure. FossilOrigin-Name: 6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2 --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/json101.test | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 766de1838f..9f44093bfe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\ssome\sOPFS-related\shelp\stext\sin\sWASM\stests.\sMinor\scleanups\sin\sspeedtest1-worker.js. -D 2023-11-30T10:00:25.069 +C New\sJSON\stest\scases\sshowing\sinsert\sor\sset\swith\smissing\ssubstructure. +D 2023-11-30T16:16:10.846 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1324,7 +1324,7 @@ F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35 F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x -F test/json101.test bc05d2476fd6f7ead31ec05b43d1b24b2b193ae112fd8f0d2ed56d9a904f9fa5 +F test/json101.test 4d8faf6a77152edadaf615bdfac4fb5556d131230cc2558f0912bed00a54943b F test/json102.test 4c69694773a470f1fda34e5f4ba24920b35184fb66050b450fc2ef9ab5ad310b F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2143,8 +2143,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 988c3179e978a3a6d42541e9c7a2ab98150383671810926503376ed808f150ff -R 73ee9ef0435c8cda72f87d3ec03cb69f -U stephan -Z 88450718d2dd1d2993dee724cc33772c +P 263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a +R 0e9b3f433035365e3e741a55f5ab0daf +U drh +Z 72d2be7125d7371db00777b78c0fb9f8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index af5689a923..fcbc7b3b7f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a \ No newline at end of file +6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2 \ No newline at end of file diff --git a/test/json101.test b/test/json101.test index c62991bbbf..5745e9d4cd 100644 --- a/test/json101.test +++ b/test/json101.test @@ -1054,6 +1054,30 @@ do_execsql_test json101-23.2 { FROM (SELECT json_set('[]','$[#]',0,'$[#]',1) AS j); } {{[0,1]} 0 1} +# Insert/Set/Replace where the path specifies substructure that +# does not yet exist +# +proc tx x {return [string map [list ( \173 ) \175 ' \042 < \133 > \135] $x]} +foreach {id start path ins set repl} { + 1 {{}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) () + 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4) + 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) ('a':()) + 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':9)>)> <0,1,2,('a':<('b':9)>)> <0,1,2> + 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2> + 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':9)>),2> <0,('a':<('b':9)>),2> <0,(),2> + 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':9)>> <0,1,2,<('b':9)>> <0,1,2> + 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2> +} { + do_execsql_test json101-24.$id.insert { + SELECT json_insert($start,$path,9); + } [list [tx $ins]] + do_execsql_test json101-24.$id.set { + SELECT json_set($start,$path,9); + } [list [tx $set]] + do_execsql_test json101-24.$id.replace { + SELECT json_replace($start,$path,9); + } [list [tx $repl]] +} finish_test From 7394a6ef571081ed4f8bd88f77121b0b9338fa70 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 16:17:09 +0000 Subject: [PATCH 279/347] Simplification of the new JSON insert/set test cases. FossilOrigin-Name: 04c0d5644372446c924a2e31a26edf51ddc563a1990d170b0ed4739e3e8b239b --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/json101.test | 22 +++++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index c9a863d174..2c2e1cf626 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\sfor\sinsert/set/replace\swith\spaths\sthat\sindicate\ssubstructure\nthat\sdoes\snot\syet\sexist. -D 2023-11-30T12:04:14.610 +C Simplification\sof\sthe\snew\sJSON\sinsert/set\stest\scases. +D 2023-11-30T16:17:09.157 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1325,7 +1325,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286 F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x -F test/json101.test 861a5d75c296709471e88aa48d82095ed600dedd23ba29b904b91e64d319b7c3 +F test/json101.test 70587d7d35ef9e2126364ba70f0c951f70827cfbd28649d779ff3df7e8f87547 F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 @@ -2145,8 +2145,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 99c8f6bd5c9a31b6d00f92e383bec8a8235ed553916ad59adbb1b7663f6ebff1 -R 757617988b02e4847aadc75efb2ede4b +P 146c717c51940b2139befc45ac74e7a1c36ef3c32fd3cfe35b334488eebe6298 +R 8e1036414b3f5ed40f0248ecb25d6019 U drh -Z 50c123f2db65971d190ff942903bde3b +Z df0518617355b4fc840dfe4615c492b7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 01f2a5b9d8..b89e5d463a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -146c717c51940b2139befc45ac74e7a1c36ef3c32fd3cfe35b334488eebe6298 \ No newline at end of file +04c0d5644372446c924a2e31a26edf51ddc563a1990d170b0ed4739e3e8b239b \ No newline at end of file diff --git a/test/json101.test b/test/json101.test index d7b301e6df..bae68e7344 100644 --- a/test/json101.test +++ b/test/json101.test @@ -1144,23 +1144,23 @@ do_execsql_test json101-23.2 { # proc tx x {return [string map [list ( \173 ) \175 ' \042 < \133 > \135] $x]} foreach {id start path ins set repl} { - 1 {{}} {$.a.b.c} ('a':('b':('c':'NEW'))) ('a':('b':('c':'NEW'))) () - 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4) - 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':'NEW'))) ('a':('b':('c':'NEW'))) ('a':()) - 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':'NEW')>)> <0,1,2,('a':<('b':'NEW')>)> <0,1,2> - 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2> - 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':'NEW')>),2> <0,('a':<('b':'NEW')>),2> <0,(),2> - 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':'NEW')> <0,1,2,<('b':'NEW')> <0,1,2> - 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2> + 1 {{}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) () + 2 {{a:4}} {$.a.b.c} ('a':4) ('a':4) ('a':4) + 3 {{a:{}}} {$.a.b.c} ('a':('b':('c':9))) ('a':('b':('c':9))) ('a':()) + 4 {[0,1,2]} {$[3].a[0].b} <0,1,2,('a':<('b':9)>)> <0,1,2,('a':<('b':9)>)> <0,1,2> + 5 {[0,1,2]} {$[1].a[0].b} <0,1,2> <0,1,2> <0,1,2> + 6 {[0,{},2]} {$[1].a[0].b} <0,('a':<('b':9)>),2> <0,('a':<('b':9)>),2> <0,(),2> + 7 {[0,1,2]} {$[3][0].b} <0,1,2,<('b':9)>> <0,1,2,<('b':9)>> <0,1,2> + 8 {[0,1,2]} {$[1][0].b} <0,1,2> <0,1,2> <0,1,2> } { do_execsql_test json101-24.$id.insert { - SELECT json_insert($start,$path,'NEW'); + SELECT json_insert($start,$path,9); } [list [tx $ins]] do_execsql_test json101-24.$id.set { - SELECT json_set($start,$path,'NEW'); + SELECT json_set($start,$path,9); } [list [tx $set]] do_execsql_test json101-24.$id.replace { - SELECT json_replace($start,$path,'NEW'); + SELECT json_replace($start,$path,9); } [list [tx $repl]] } From 66795962b59af6deab38b178a8a3b03f223c309a Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 19:06:27 +0000 Subject: [PATCH 280/347] Enhance json_set() and json_insert() so that they create missing substructure. FossilOrigin-Name: cc7a641ab5ae739d31c24f0ad0caeb15a481a63fa8f13720718ea922c25862ff --- manifest | 12 +++++----- manifest.uuid | 2 +- src/json.c | 63 +++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 2c2e1cf626..dd2fca3cd7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplification\sof\sthe\snew\sJSON\sinsert/set\stest\scases. -D 2023-11-30T16:17:09.157 +C Enhance\sjson_set()\sand\sjson_insert()\sso\sthat\sthey\screate\smissing\nsubstructure. +D 2023-11-30T19:06:27.003 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 8b509dead923cd44f31e4866d1024f1b602bdf01c9d41a08cc36aa5f1203fdf8 +F src/json.c 92cc05dcf87cdace6fbf2a76e0cb2b030f04ddb1819e024af4ce8885261b4cef F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 146c717c51940b2139befc45ac74e7a1c36ef3c32fd3cfe35b334488eebe6298 -R 8e1036414b3f5ed40f0248ecb25d6019 +P 04c0d5644372446c924a2e31a26edf51ddc563a1990d170b0ed4739e3e8b239b +R 4a2861d7ab7860f80e426ce44e32c50f U drh -Z df0518617355b4fc840dfe4615c492b7 +Z 1fffc27f10fd5ca429d5618946ebb513 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b89e5d463a..7e844fc2a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -04c0d5644372446c924a2e31a26edf51ddc563a1990d170b0ed4739e3e8b239b \ No newline at end of file +cc7a641ab5ae739d31c24f0ad0caeb15a481a63fa8f13720718ea922c25862ff \ No newline at end of file diff --git a/src/json.c b/src/json.c index b3b4f993e6..b8b01bf452 100644 --- a/src/json.c +++ b/src/json.c @@ -3154,6 +3154,7 @@ static u32 jsonLookupBlobStep( u32 i, j, k, nKey, sz, n, iEnd, rc; const char *zKey; u8 x; + static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; if( zPath[0]==0 ){ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ @@ -3225,25 +3226,44 @@ static u32 jsonLookupBlobStep( } if( j>iEnd ) return JSON_BLOB_ERROR; if( pParse->eEdit>=JEDIT_INS ){ - u32 nIns; - u8 aLabel[16]; - JsonParse ix; + u32 nIns; /* Total bytes to insert (label+value) */ + JsonParse v; /* BLOB encoding of the value to be inserted */ + JsonParse ix; /* Header of the label to be inserted */ + u8 aLabel[16]; /* Buffer space for ix */ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); ix.aBlob = aLabel; ix.nBlobAlloc = sizeof(aLabel); jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); - if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+pParse->nIns) ){ - nIns = ix.nBlob + nKey + pParse->nIns; + memset(&v, 0, sizeof(v)); + if( zPath[i]==0 ){ + v.nBlob = pParse->nIns; + v.aBlob = pParse->aIns; + }else{ + v.nBlob = 1; + v.aBlob = (u8*)&emptyObject[zPath[i]=='.']; + v.eEdit = pParse->eEdit; + v.nIns = pParse->nIns; + v.aIns = pParse->aIns; + rc = jsonLookupBlobStep(&v, 0, &zPath[i], 0); + if( JSON_BLOB_ISERROR(rc) || v.oom ){ + pParse->oom |= v.oom; + jsonParseReset(&v); + return rc; + } + } + if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ + nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); k += nKey; - memcpy(&pParse->aBlob[k], pParse->aIns, pParse->nIns); + memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); } + jsonParseReset(&v); return j; } }else if( zPath[0]=='[' ){ @@ -3292,12 +3312,31 @@ static u32 jsonLookupBlobStep( } if( j>iEnd ) return JSON_BLOB_ERROR; if( k>0 ) return JSON_BLOB_NOTFOUND; - if( pParse->eEdit>=JEDIT_INS - && jsonBlobMakeEditable(pParse, pParse->nIns) - ){ + if( pParse->eEdit>=JEDIT_INS ){ + JsonParse v; testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); - jsonBlobEdit(pParse, j, 0, pParse->aIns, pParse->nIns); + memset(&v, 0, sizeof(v)); + if( zPath[i+1]==0 ){ + v.aBlob = pParse->aIns; + v.nBlob = pParse->nIns; + }else{ + v.nBlob = 1; + v.aBlob = (u8*)&emptyObject[zPath[i+1]=='.']; + v.eEdit = pParse->eEdit; + v.nIns = pParse->nIns; + v.aIns = pParse->aIns; + rc = jsonLookupBlobStep(&v, 0, &zPath[i+1], 0); + if( JSON_BLOB_ISERROR(rc) || v.oom ){ + pParse->oom |= v.oom; + jsonParseReset(&v); + return rc; + } + } + if( jsonBlobMakeEditable(pParse, v.nBlob) ){ + jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); + } + jsonParseReset(&v); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return j; } @@ -3754,6 +3793,10 @@ static void jsonReturnParse( JsonParse *p ){ int flgs; + if( p->oom ){ + sqlite3_result_error_nomem(ctx); + return; + } flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( flgs & JSON_BLOB ){ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, From 276042bd23b7dab88ca6029177225404f4f7f973 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 19:29:56 +0000 Subject: [PATCH 281/347] Convert json_type() to use JSONB internally. FossilOrigin-Name: 83074835b900ce85cf67059e674ce959801505c37592671af25ca0af7ed483f1 --- manifest | 13 ++++----- manifest.uuid | 2 +- src/json.c | 77 ++++++++++++++++++++++----------------------------- 3 files changed, 40 insertions(+), 52 deletions(-) diff --git a/manifest b/manifest index 0b047f5b82..38eb4c4f95 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sjson_insert(),\sjson_replace(),\sand\sjson_set()\sover\sto\susing\sonly\nJSONB\sinternally. -D 2023-11-30T19:11:14.034 +C Convert\sjson_type()\sto\suse\sJSONB\sinternally. +D 2023-11-30T19:29:56.759 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 92cc05dcf87cdace6fbf2a76e0cb2b030f04ddb1819e024af4ce8885261b4cef +F src/json.c 3d897ec27a8640c94e5d8293f8aa64226205140150639571c6f745c42d545e3e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,9 +2145,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 e7a8ba35bff6fde55827f978de5b343b6c134c7fa53827f5c63915a9dc2598ad cc7a641ab5ae739d31c24f0ad0caeb15a481a63fa8f13720718ea922c25862ff -R 4a2861d7ab7860f80e426ce44e32c50f -T +closed cc7a641ab5ae739d31c24f0ad0caeb15a481a63fa8f13720718ea922c25862ff +P 4e2083e86f19ef7634f0b253fb924e52014b43ed0ce8acc51c36f3c5682180a6 +R 4b874649acdc6c51075a8e11e402ee44 U drh -Z 60442442b5f0c2166085d00e76659699 +Z 2246ad2d1a16f9f5b929395a88459b9a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 24560706af..cd1d889728 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e2083e86f19ef7634f0b253fb924e52014b43ed0ce8acc51c36f3c5682180a6 \ No newline at end of file +83074835b900ce85cf67059e674ce959801505c37592671af25ca0af7ed483f1 \ No newline at end of file diff --git a/src/json.c b/src/json.c index b8b01bf452..4f47b7a758 100644 --- a/src/json.c +++ b/src/json.c @@ -1887,42 +1887,6 @@ static char *jsonPathSyntaxError(const char *zErr, sqlite3_context *ctx){ return 0; } -/* -** Do a node lookup using zPath. Return a pointer to the node on success. -** Return NULL if not found or if there is an error. -** -** On an error, write an error message into pCtx and increment the -** pParse->nErr counter. -** -** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if -** nodes are appended. -*/ -static JsonNode *jsonLookup( - JsonParse *pParse, /* The JSON to search */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - sqlite3_context *pCtx /* Report errors here, if not NULL */ -){ - const char *zErr = 0; - JsonNode *pNode = 0; - - if( zPath==0 ) return 0; - if( zPath[0]!='$' ){ - zErr = zPath; - goto lookup_err; - } - zPath++; - pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); - if( zErr==0 ) return pNode; - -lookup_err: - pParse->nErr++; - assert( zErr!=0 && pCtx!=0 ); - jsonPathSyntaxError(zErr, pCtx); - return 0; -} - - /* ** Report the wrong number of arguments for json_insert(), json_replace() ** or json_set(). @@ -4666,20 +4630,45 @@ static void jsonTypeFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - const char *zPath; - JsonNode *pNode; + const char *zPath = 0; + u32 i; - p = jsonParseCached(ctx, argv[0], ctx, 0); + p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==2 ){ + if( sqlite3_value_type(argv[1])==SQLITE_NULL ){ + goto json_type_done; + } + if( sqlite3_value_bytes(argv[1])==0 ){ + jsonBadPathError(ctx, ""); + goto json_type_done; + } zPath = (const char*)sqlite3_value_text(argv[1]); - pNode = jsonLookup(p, zPath, 0, ctx); + if( zPath==0 ){ + sqlite3_result_error_nomem(ctx); + goto json_type_done; + } + if( zPath[0]!='$' ){ + jsonBadPathError(ctx, zPath); + goto json_type_done; + } + i = jsonLookupBlobStep(p, 0, zPath+1, 0); + if( JSON_BLOB_ISERROR(i) ){ + if( i==JSON_BLOB_NOTFOUND ){ + /* no-op */ + }else if( i==JSON_BLOB_PATHERROR ){ + jsonBadPathError(ctx, zPath); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + goto json_type_done; + } }else{ - pNode = p->aNode; - } - if( pNode ){ - sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC); + i = 0; } + sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); +json_type_done: + jsonParseFree(p); } /* From 68003d9f1807195955716abf3454a3bccd6d82a4 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 30 Nov 2023 20:34:24 +0000 Subject: [PATCH 282/347] Add a basic batch-mode SQL runner for the SAH Pool VFS, for use in comparing it against WebSQL. Bring the WebSQL batch runner up to date, noting that it cannot run without addition of an "origin trial" activation key from Google because that's now the only way to enable WebSQL in Chrome (that part is not checked in because that key is private). Minor code-adjacent cleanups. FossilOrigin-Name: 883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62 --- ext/wasm/GNUmakefile | 2 +- ext/wasm/batch-runner-sahpool.html | 86 ++++++++ ext/wasm/batch-runner-sahpool.js | 341 +++++++++++++++++++++++++++++ ext/wasm/batch-runner.js | 24 +- ext/wasm/demo-123.js | 15 +- ext/wasm/speedtest1-worker.html | 2 +- manifest | 22 +- manifest.uuid | 2 +- 8 files changed, 470 insertions(+), 24 deletions(-) create mode 100644 ext/wasm/batch-runner-sahpool.html create mode 100644 ext/wasm/batch-runner-sahpool.js diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index c0cab212d8..3ef478612c 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -854,7 +854,7 @@ dir.sql := sql speedtest1 := ../../speedtest1 speedtest1.c := ../../test/speedtest1.c speedtest1.sql := $(dir.sql)/speedtest1.sql -speedtest1.cliflags := --size 25 --big-transactions +speedtest1.cliflags := --size 10 --big-transactions $(speedtest1): $(MAKE) -C ../.. speedtest1 $(speedtest1.sql): $(speedtest1) $(MAKEFILE) diff --git a/ext/wasm/batch-runner-sahpool.html b/ext/wasm/batch-runner-sahpool.html new file mode 100644 index 0000000000..ad7e7b5408 --- /dev/null +++ b/ext/wasm/batch-runner-sahpool.html @@ -0,0 +1,86 @@ + + + + + + + + sqlite3-api batch SQL runner for the SAHPool VFS + + +
    sqlite3-api batch SQL runner for the SAHPool VFS
    +
    + + + + +
    +
    + + + + diff --git a/ext/wasm/batch-runner-sahpool.js b/ext/wasm/batch-runner-sahpool.js new file mode 100644 index 0000000000..dfa5044a9e --- /dev/null +++ b/ext/wasm/batch-runner-sahpool.js @@ -0,0 +1,341 @@ +/* + 2023-11-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. + + *********************************************************************** + + A basic batch SQL runner for the SAHPool VFS. This file must be run in + a worker thread. This is not a full-featured app, just a way to get some + measurements for batch execution of SQL for the OPFS SAH Pool VFS. +*/ +'use strict'; + +const wMsg = function(msgType,...args){ + postMessage({ + type: msgType, + data: args + }); +}; +const toss = function(...args){throw new Error(args.join(' '))}; +const warn = (...args)=>{ wMsg('warn',...args); }; +const error = (...args)=>{ wMsg('error',...args); }; +const log = (...args)=>{ wMsg('stdout',...args); } +let sqlite3; +const urlParams = new URL(globalThis.location.href).searchParams; +const cacheSize = (()=>{ + if(urlParams.has('cachesize')) return +urlParams.get('cachesize'); + return 200; +})(); + + +/** Throws if the given sqlite3 result code is not 0. */ +const checkSqliteRc = (dbh,rc)=>{ + if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh)); +}; + +const sqlToDrop = [ + "SELECT type,name FROM sqlite_schema ", + "WHERE name NOT LIKE 'sqlite\\_%' escape '\\' ", + "AND name NOT LIKE '\\_%' escape '\\'" +].join(''); + +const clearDbSqlite = function(db){ + // This would be SO much easier with the oo1 API, but we specifically want to + // inject metrics we can't get via that API, and we cannot reliably (OPFS) + // open the same DB twice to clear it using that API, so... + const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); + log("reset db rc =",rc,db.id, db.filename); +}; + +const App = { + db: undefined, + cache:Object.create(null), + log: log, + warn: warn, + error: error, + metrics: { + fileCount: 0, + runTimeMs: 0, + prepareTimeMs: 0, + stepTimeMs: 0, + stmtCount: 0, + strcpyMs: 0, + sqlBytes: 0 + }, + fileList: undefined, + execSql: async function(name,sql){ + const db = this.db; + const banner = "========================================"; + this.log(banner, + "Running",name,'('+sql.length,'bytes)'); + const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; + let pStmt = 0, pSqlBegin; + const metrics = db.metrics = Object.create(null); + metrics.prepTotal = metrics.stepTotal = 0; + metrics.stmtCount = 0; + metrics.malloc = 0; + metrics.strcpy = 0; + if(this.gotErr){ + this.error("Cannot run SQL: error cleanup is pending."); + return; + } + // Run this async so that the UI can be updated for the above header... + const endRun = ()=>{ + metrics.evalSqlEnd = performance.now(); + metrics.evalTimeTotal = (metrics.evalSqlEnd - metrics.evalSqlStart); + this.log("metrics:",JSON.stringify(metrics, undefined, ' ')); + this.log("prepare() count:",metrics.stmtCount); + this.log("Time in prepare_v2():",metrics.prepTotal,"ms", + "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); + this.log("Time in step():",metrics.stepTotal,"ms", + "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); + this.log("Total runtime:",metrics.evalTimeTotal,"ms"); + this.log("Overhead (time - prep - step):", + (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); + this.log(banner,"End of",name); + this.metrics.prepareTimeMs += metrics.prepTotal; + this.metrics.stepTimeMs += metrics.stepTotal; + this.metrics.stmtCount += metrics.stmtCount; + this.metrics.strcpyMs += metrics.strcpy; + this.metrics.sqlBytes += sql.length; + }; + + const runner = function(resolve, reject){ + ++this.metrics.fileCount; + metrics.evalSqlStart = performance.now(); + const stack = wasm.scopedAllocPush(); + try { + let t, rc; + let sqlByteLen = sql.byteLength; + const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); + t = performance.now(); + pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); + metrics.malloc = performance.now() - t; + metrics.byteLength = sqlByteLen; + let pSql = pSqlBegin; + const pSqlEnd = pSqlBegin + sqlByteLen; + t = performance.now(); + wasm.heap8().set(sql, pSql); + wasm.poke(pSql + sqlByteLen, 0); + //log("SQL:",wasm.cstrToJs(pSql)); + metrics.strcpy = performance.now() - t; + let breaker = 0; + while(pSql && wasm.peek8(pSql)){ + wasm.pokePtr(ppStmt, 0); + wasm.pokePtr(pzTail, 0); + t = performance.now(); + rc = capi.sqlite3_prepare_v2( + db.handle, pSql, sqlByteLen, ppStmt, pzTail + ); + metrics.prepTotal += performance.now() - t; + checkSqliteRc(db.handle, rc); + pStmt = wasm.peekPtr(ppStmt); + pSql = wasm.peekPtr(pzTail); + sqlByteLen = pSqlEnd - pSql; + if(!pStmt) continue/*empty statement*/; + ++metrics.stmtCount; + t = performance.now(); + rc = capi.sqlite3_step(pStmt); + capi.sqlite3_finalize(pStmt); + pStmt = 0; + metrics.stepTotal += performance.now() - t; + switch(rc){ + case capi.SQLITE_ROW: + case capi.SQLITE_DONE: break; + default: checkSqliteRc(db.handle, rc); toss("Not reached."); + } + } + resolve(this); + }catch(e){ + if(pStmt) capi.sqlite3_finalize(pStmt); + this.gotErr = e; + reject(e); + }finally{ + capi.sqlite3_exec(db.handle,"rollback;",0,0,0); + wasm.scopedAllocPop(stack); + } + }.bind(this); + const p = new Promise(runner); + return p.catch( + (e)=>this.error("Error via execSql("+name+",...):",e.message) + ).finally(()=>{ + endRun(); + }); + }, + + /** + Loads batch-runner.list and populates the selection list from + it. Returns a promise which resolves to nothing in particular + when it completes. Only intended to be run once at the start + of the app. + */ + loadSqlList: async function(){ + const infile = 'batch-runner.list'; + this.log("Loading list of SQL files:", infile); + let txt; + try{ + const r = await fetch(infile); + if(404 === r.status){ + toss("Missing file '"+infile+"'."); + } + if(!r.ok) toss("Loading",infile,"failed:",r.statusText); + txt = await r.text(); + }catch(e){ + this.error(e.message); + throw e; + } + App.fileList = txt.split(/\n+/).filter(x=>!!x); + this.log("Loaded",infile); + }, + + /** Fetch ./fn and return its contents as a Uint8Array. */ + fetchFile: async function(fn, cacheIt=false){ + if(cacheIt && this.cache[fn]) return this.cache[fn]; + this.log("Fetching",fn,"..."); + let sql; + try { + const r = await fetch(fn); + if(!r.ok) toss("Fetch failed:",r.statusText); + sql = new Uint8Array(await r.arrayBuffer()); + }catch(e){ + this.error(e.message); + throw e; + } + this.log("Fetched",sql.length,"bytes from",fn); + if(cacheIt) this.cache[fn] = sql; + return sql; + }/*fetchFile()*/, + + /** + Converts this.metrics() to a form which is suitable for easy conversion to + CSV. It returns an array of arrays. The first sub-array is the column names. + The 2nd and subsequent are the values, one per test file (only the most recent + metrics are kept for any given file). + */ + metricsToArrays: function(){ + const rc = []; + Object.keys(this.dbs).sort().forEach((k)=>{ + const d = this.dbs[k]; + const m = d.metrics; + delete m.evalSqlStart; + delete m.evalSqlEnd; + const mk = Object.keys(m).sort(); + if(!rc.length){ + rc.push(['db', ...mk]); + } + const row = [k.split('/').pop()/*remove dir prefix from filename*/]; + rc.push(row); + row.push(...mk.map((kk)=>m[kk])); + }); + return rc; + }, + + metricsToBlob: function(colSeparator='\t'){ + const ar = [], ma = this.metricsToArrays(); + if(!ma.length){ + this.error("Metrics are empty. Run something."); + return; + } + ma.forEach(function(row){ + ar.push(row.join(colSeparator),'\n'); + }); + return new Blob(ar); + }, + + /** + Fetch file fn and eval it as an SQL blob. This is an async + operation and returns a Promise which resolves to this + object on success. + */ + evalFile: async function(fn){ + const sql = await this.fetchFile(fn); + return this.execSql(fn,sql); + }/*evalFile()*/, + + /** + Fetches the handle of the db associated with + this.e.selImpl.value, opening it if needed. + */ + initDb: function(){ + const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; + const stack = wasm.scopedAllocPush(); + let pDb = 0; + const d = Object.create(null); + d.filename = "/batch.db"; + try{ + const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + const ppDb = wasm.scopedAllocPtr(); + const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, this.PoolUtil.vfsName); + pDb = wasm.peekPtr(ppDb) + if(rc) toss("sqlite3_open_v2() failed with code",rc); + capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0); + this.log("cache_size =",cacheSize); + }catch(e){ + if(pDb) capi.sqlite3_close_v2(pDb); + throw e; + }finally{ + wasm.scopedAllocPop(stack); + } + d.handle = pDb; + this.log("Opened db:",d.filename,'@',d.handle); + return d; + }, + + closeDb: function(){ + if(this.db.handle){ + this.sqlite3.capi.sqlite3_close_v2(this.db.handle); + this.db.handle = undefined; + } + }, + + run: async function(sqlite3){ + delete this.run; + this.sqlite3 = sqlite3; + const capi = sqlite3.capi, wasm = sqlite3.wasm; + this.log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + this.log("WASM heap size =",wasm.heap8().length); + let timeStart; + sqlite3.installOpfsSAHPoolVfs({ + clearOnInit: true, initialCapacity: 4, + name: 'batch-sahpool', + verbosity: 2 + }).then(PoolUtil=>{ + App.PoolUtil = PoolUtil; + App.db = App.initDb(); + }) + .then(async ()=>this.loadSqlList()) + .then(async ()=>{ + timeStart = performance.now(); + for(let i = 0; i < App.fileList.length; ++i){ + const fn = App.fileList[i]; + await App.evalFile(fn); + if(App.gotErr) throw App.gotErr; + } + }) + .then(()=>{ + App.metrics.runTimeMs = performance.now() - timeStart; + App.log("total metrics:",JSON.stringify(App.metrics, undefined, ' ')); + App.log("Reload the page to run this again."); + App.closeDb(); + App.PoolUtil.removeVfs(); + }) + .catch(e=>this.error("ERROR:",e)); + }/*run()*/ +}/*App*/; + +let sqlite3Js = 'sqlite3.js'; +if(urlParams.has('sqlite3.dir')){ + sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; +} +importScripts(sqlite3Js); +globalThis.sqlite3InitModule().then(async function(sqlite3_){ + log("Done initializing. Running batch runner..."); + sqlite3 = sqlite3_; + App.run(sqlite3_); +}); diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index ff287a66e7..e7a322b7f0 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -72,7 +72,6 @@ App.logHtml("reset db rc =",rc,db.id, db.filename); }; - const E = (s)=>document.querySelector(s); const App = { e: { @@ -91,6 +90,15 @@ db: Object.create(null), dbs: Object.create(null), cache:{}, + metrics: { + fileCount: 0, + runTimeMs: 0, + prepareTimeMs: 0, + stepTimeMs: 0, + stmtCount: 0, + strcpyMs: 0, + sqlBytes: 0 + }, log: console.log.bind(console), warn: console.warn.bind(console), cls: function(){this.e.output.innerHTML = ''}, @@ -117,7 +125,6 @@ "Running",name,'('+sql.length,'bytes) using',db.id); const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; let pStmt = 0, pSqlBegin; - const stack = wasm.scopedAllocPush(); const metrics = db.metrics = Object.create(null); metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; @@ -142,6 +149,11 @@ this.logHtml("Overhead (time - prep - step):", (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); this.logHtml(banner,"End of",name); + this.metrics.prepareTimeMs += metrics.prepTotal; + this.metrics.stepTimeMs += metrics.stepTotal; + this.metrics.stmtCount += metrics.stmtCount; + this.metrics.strcpyMs += metrics.strcpy; + this.metrics.sqlBytes += sql.length; }; let runner; @@ -214,7 +226,9 @@ }.bind(this); }else{/*sqlite3 db...*/ runner = function(resolve, reject){ + ++this.metrics.fileCount; metrics.evalSqlStart = performance.now(); + const stack = wasm.scopedAllocPush(); try { let t; let sqlByteLen = sql.byteLength; @@ -269,7 +283,7 @@ let p; if(1){ p = new Promise(function(res,rej){ - setTimeout(()=>runner(res, rej), 50)/*give UI a chance to output the "running" banner*/; + setTimeout(()=>runner(res, rej), 0)/*give UI a chance to output the "running" banner*/; }); }else{ p = new Promise(runner); @@ -401,7 +415,7 @@ }); return new Blob(ar); }, - + downloadMetrics: function(){ const b = this.metricsToBlob(); if(!b) return; @@ -576,6 +590,8 @@ const timeTotal = performance.now() - timeStart; who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); who.clearStorage(); + App.metrics.runTimeMs = timeTotal; + who.logHtml("Total metrics:",JSON.stringify(App.metrics,undefined,' ')); }, false); }/*run()*/ }/*App*/; diff --git a/ext/wasm/demo-123.js b/ext/wasm/demo-123.js index 8e03ee80fa..9f90ca7568 100644 --- a/ext/wasm/demo-123.js +++ b/ext/wasm/demo-123.js @@ -20,7 +20,7 @@ the main (UI) thread. */ let logHtml; - if(self.window === self /* UI thread */){ + if(globalThis.window === globalThis /* UI thread */){ console.log("Running demo from main UI thread."); logHtml = function(cssClass,...args){ const ln = document.createElement('div'); @@ -250,7 +250,7 @@ }/*demo1()*/; log("Loading and initializing sqlite3 module..."); - if(self.window!==self) /*worker thread*/{ + if(globalThis.window!==globalThis) /*worker thread*/{ /* If sqlite3.js is in a directory other than this script, in order to get sqlite3.js to resolve sqlite3.wasm properly, we have to @@ -262,19 +262,20 @@ that's not needed. URL arguments passed as part of the filename via importScripts() - are simply lost, and such scripts see the self.location of + are simply lost, and such scripts see the globalThis.location of _this_ script. */ let sqlite3Js = 'sqlite3.js'; - const urlParams = new URL(self.location.href).searchParams; + const urlParams = new URL(globalThis.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; } importScripts(sqlite3Js); } - self.sqlite3InitModule({ - // We can redirect any stdout/stderr from the module - // like so... + globalThis.sqlite3InitModule({ + /* We can redirect any stdout/stderr from the module like so, but + note that doing so makes use of Emscripten-isms, not + well-defined sqlite APIs. */ print: log, printErr: error }).then(function(sqlite3){ diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index 3adf8f2ee9..8c9a77dc5e 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -350,7 +350,7 @@ eControls.classList.remove('hidden'); break; case 'stdout': log(msg.data); break; - case 'stdout': logErr(msg.data); break; + case 'stderr': logErr(msg.data); break; case 'run-start': eControls.disabled = true; log("Running speedtest1 with argv =",msg.data.join(' ')); diff --git a/manifest b/manifest index 9f44093bfe..d6a02fa193 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\sJSON\stest\scases\sshowing\sinsert\sor\sset\swith\smissing\ssubstructure. -D 2023-11-30T16:16:10.846 +C Add\sa\sbasic\sbatch-mode\sSQL\srunner\sfor\sthe\sSAH\sPool\sVFS,\sfor\suse\sin\scomparing\sit\sagainst\sWebSQL.\sBring\sthe\sWebSQL\sbatch\srunner\sup\sto\sdate,\snoting\sthat\sit\scannot\srun\swithout\saddition\sof\san\s"origin\strial"\sactivation\skey\sfrom\sGoogle\sbecause\sthat's\snow\sthe\sonly\sway\sto\senable\sWebSQL\sin\sChrome\s(that\spart\sis\snot\schecked\sin\sbecause\sthat\skey\sis\sprivate).\sMinor\scode-adjacent\scleanups. +D 2023-11-30T20:34:24.440 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -568,7 +568,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 +F ext/wasm/GNUmakefile 57439eec2b8b4d4074e5d861e93aab3f32bb0f44339a2b472c23f4e638b7e8a3 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff @@ -598,8 +598,10 @@ F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91cc F ext/wasm/api/sqlite3-wasm.c d0e09eb5ed3743c00294e30019e591c3aa150572ae7ffe8a8994568a7377589f F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 569d4e859968e65f55dec5fac0b879828a23c8b179162cc7812fec19f844dd21 F ext/wasm/api/sqlite3-worker1.c-pp.js a541112aa51e16705f13a99bb943c64efe178aa28c86704a955f8fd9afe4ba37 +F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7 +F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 -F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e +F ext/wasm/batch-runner.js 05ec254f5dbfe605146d9640b3db17d6ef8c3fbef6aa8396051ca72bb5884e3f F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25b4 F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 @@ -607,7 +609,7 @@ F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a7 F ext/wasm/common/whwasmutil.js 4c64594eecc7af4ae64259e95a71ba2a7edf118881aaff0bba86d0c7164e78e4 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 -F ext/wasm/demo-123.js 38aa8faec4d0ace1c973bc8a7a1533584463ebeecd4c420daa7d9687beeb9cb5 +F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32e7be5bf F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8 F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98ab22f5786620b3354ed15f @@ -630,7 +632,7 @@ F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe -F ext/wasm/speedtest1-worker.html e33e2064bda572c0c3ebaec7306c35aa758d9d27e245d67e807f8cc4a9351cc5 +F ext/wasm/speedtest1-worker.html 864b65ed78ce24847a348c180e7f267621a02ca027068a1863ec1c90187c1852 F ext/wasm/speedtest1-worker.js 4d2ea70a3c24e05bdca78025202841f33d298c4fa9541a0070c3228661f89ecd F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x @@ -2143,8 +2145,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 263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a -R 0e9b3f433035365e3e741a55f5ab0daf -U drh -Z 72d2be7125d7371db00777b78c0fb9f8 +P 6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2 +R 0685d189d0b0c5921f2e4eda3787211e +U stephan +Z 5692f67161061876d9b3bcabcb3928cc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fcbc7b3b7f..98d7aac12d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2 \ No newline at end of file +883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62 \ No newline at end of file From 38aeb97f27045f8978ce14e33155cb4a45e2483c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 20:57:48 +0000 Subject: [PATCH 283/347] Convert json_valid() over to using only JSONB as its internal format. FossilOrigin-Name: 7b5756fa6d00b093bf083a8d7a5ef5485f7a09e4eac473785c8380688f861a1b --- manifest | 12 +- manifest.uuid | 2 +- src/json.c | 792 +++----------------------------------------------- 3 files changed, 43 insertions(+), 763 deletions(-) diff --git a/manifest b/manifest index 38eb4c4f95..0d5accceac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sjson_type()\sto\suse\sJSONB\sinternally. -D 2023-11-30T19:29:56.759 +C Convert\sjson_valid()\sover\sto\susing\sonly\sJSONB\sas\sits\sinternal\sformat. +D 2023-11-30T20:57:48.608 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 3d897ec27a8640c94e5d8293f8aa64226205140150639571c6f745c42d545e3e +F src/json.c 04044605d6eb39c6c8a4cdd42a01c256d10eed88421770c7e92d391164ed5a5b F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 4e2083e86f19ef7634f0b253fb924e52014b43ed0ce8acc51c36f3c5682180a6 -R 4b874649acdc6c51075a8e11e402ee44 +P 83074835b900ce85cf67059e674ce959801505c37592671af25ca0af7ed483f1 +R 2a1e3393332b0e1afccf3cc9eb62658b U drh -Z 2246ad2d1a16f9f5b929395a88459b9a +Z 8664a63b705df80c188b26ef2d7670eb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cd1d889728..0476c44651 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -83074835b900ce85cf67059e674ce959801505c37592671af25ca0af7ed483f1 \ No newline at end of file +7b5756fa6d00b093bf083a8d7a5ef5485f7a09e4eac473785c8380688f861a1b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 4f47b7a758..f9e2ee5aa9 100644 --- a/src/json.c +++ b/src/json.c @@ -375,13 +375,13 @@ struct JsonParse { ** Allowed values for the flgs argument to jsonParseFuncArg(); */ #define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ +#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */ /************************************************************************** ** Forward references **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); static int jsonParseAddNode(JsonParse*,u32,u32,const char*); -static int jsonXlateBlobToNode(JsonParse *pParse, u32 i); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); @@ -1003,645 +1003,12 @@ static const struct NanInfName { { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, }; -/* -** Translate a single element of JSON text beginning at pParse->zJson[i] into -** its JsonNode representation. Append the translation onto the -** pParse->aNode[] array, which is increased in size as necessary. -** Return the pJson->zJson[] index of the first character past the end of -** the element that was parsed. -** -** Special return values: -** -** 0 End of input -** -1 Syntax error -** -2 '}' seen \ -** -3 ']' seen \___ For these returns, pParse->iErr is set to -** -4 ',' seen / the index in zJson[] of the seen character -** -5 ':' seen / -*/ -static int jsonXlateTextToNode(JsonParse *pParse, u32 i){ - char c; - u32 j; - int iThis; - int x; - JsonNode *pNode; - const char *z = pParse->zJson; -json_parse_restart: - switch( (u8)z[i] ){ - case '{': { - /* Parse object */ - iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - if( iThis<0 ) return -1; - if( ++pParse->iDepth > JSON_MAX_DEPTH ){ - pParse->iErr = i; - return -1; - } - for(j=i+1;;j++){ - u32 nNode = pParse->nNode; - x = jsonXlateTextToNode(pParse, j); - if( x<=0 ){ - if( x==(-2) ){ - j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; - break; - } - j += json5Whitespace(&z[j]); - if( sqlite3JsonId1(z[j]) - || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) - ){ - int k = j+1; - while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) - || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) - ){ - k++; - } - jsonParseAddNode(pParse, JSON_STRING, k-j, &z[j]); - pParse->hasNonstd = 1; - x = k; - }else{ - if( x!=-1 ) pParse->iErr = j; - return -1; - } - } - if( pParse->oom ) return -1; - pNode = &pParse->aNode[nNode]; - if( pNode->eType!=JSON_STRING ){ - pParse->iErr = j; - return -1; - } - pNode->jnFlags |= JNODE_LABEL; - j = x; - if( z[j]==':' ){ - j++; - }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); - if( z[j]==':' ){ - j++; - goto parse_object_value; - } - } - x = jsonXlateTextToNode(pParse, j); - if( x!=(-5) ){ - if( x!=(-1) ) pParse->iErr = j; - return -1; - } - j = pParse->iErr+1; - } - parse_object_value: - x = jsonXlateTextToNode(pParse, j); - if( x<=0 ){ - if( x!=(-1) ) pParse->iErr = j; - return -1; - } - j = x; - if( z[j]==',' ){ - continue; - }else if( z[j]=='}' ){ - break; - }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); - if( z[j]==',' ){ - continue; - }else if( z[j]=='}' ){ - break; - } - } - x = jsonXlateTextToNode(pParse, j); - if( x==(-4) ){ - j = pParse->iErr; - continue; - } - if( x==(-2) ){ - j = pParse->iErr; - break; - } - } - pParse->iErr = j; - return -1; - } - if( !pParse->oom ){ - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; - } - pParse->iDepth--; - return j+1; - } - case '[': { - /* Parse array */ - iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); - if( iThis<0 ) return -1; - if( ++pParse->iDepth > JSON_MAX_DEPTH ){ - pParse->iErr = i; - return -1; - } - memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); - for(j=i+1;;j++){ - x = jsonXlateTextToNode(pParse, j); - if( x<=0 ){ - if( x==(-3) ){ - j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; - break; - } - if( x!=(-1) ) pParse->iErr = j; - return -1; - } - j = x; - if( z[j]==',' ){ - continue; - }else if( z[j]==']' ){ - break; - }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); - if( z[j]==',' ){ - continue; - }else if( z[j]==']' ){ - break; - } - } - x = jsonXlateTextToNode(pParse, j); - if( x==(-4) ){ - j = pParse->iErr; - continue; - } - if( x==(-3) ){ - j = pParse->iErr; - break; - } - } - pParse->iErr = j; - return -1; - } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; - pParse->iDepth--; - return j+1; - } - case '\'': { - u8 jnFlags; - char cDelim; - pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; - goto parse_string; - case '"': - /* Parse string */ - jnFlags = 0; - parse_string: - cDelim = z[i]; - for(j=i+1; 1; j++){ - if( jsonIsOk[(unsigned char)z[j]] ) continue; - c = z[j]; - if( c==cDelim ){ - break; - }else if( c=='\\' ){ - c = z[++j]; - if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' - || c=='n' || c=='r' || c=='t' - || (c=='u' && jsonIs4Hex(&z[j+1])) ){ - jnFlags |= JNODE_ESCAPE; - }else if( c=='\'' || c=='0' || c=='v' || c=='\n' - || (0xe2==(u8)c && 0x80==(u8)z[j+1] - && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) - || (c=='x' && jsonIs2Hex(&z[j+1])) ){ - jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); - pParse->hasNonstd = 1; - }else if( c=='\r' ){ - if( z[j+1]=='\n' ) j++; - jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); - pParse->hasNonstd = 1; - }else{ - pParse->iErr = j; - return -1; - } - }else if( c<=0x1f ){ - /* Control characters are not allowed in strings */ - pParse->iErr = j; - return -1; - } - } - jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j-1-i, &z[i+1]); - return j+1; - } - case 't': { - if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); - return i+4; - } - pParse->iErr = i; - return -1; - } - case 'f': { - if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); - return i+5; - } - pParse->iErr = i; - return -1; - } - case '+': { - u8 seenDP, seenE, jnFlags; - pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; - goto parse_number; - case '.': - if( sqlite3Isdigit(z[i+1]) ){ - pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; - seenE = 0; - seenDP = JSON_REAL; - goto parse_number_2; - } - pParse->iErr = i; - return -1; - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - /* Parse number */ - jnFlags = 0; - parse_number: - seenDP = JSON_INT; - seenE = 0; - assert( '-' < '0' ); - assert( '+' < '0' ); - assert( '.' < '0' ); - c = z[i]; - - if( c<='0' ){ - if( c=='0' ){ - if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ - assert( seenDP==JSON_INT ); - pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; - for(j=i+3; sqlite3Isxdigit(z[j]); j++){} - goto parse_number_finish; - }else if( sqlite3Isdigit(z[i+1]) ){ - pParse->iErr = i+1; - return -1; - } - }else{ - if( !sqlite3Isdigit(z[i+1]) ){ - /* JSON5 allows for "+Infinity" and "-Infinity" using exactly - ** that case. SQLite also allows these in any case and it allows - ** "+inf" and "-inf". */ - if( (z[i+1]=='I' || z[i+1]=='i') - && sqlite3StrNICmp(&z[i+1], "inf",3)==0 - ){ - pParse->hasNonstd = 1; - if( z[i]=='-' ){ - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); - }else{ - jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); - } - return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); - } - if( z[i+1]=='.' ){ - pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; - goto parse_number_2; - } - pParse->iErr = i; - return -1; - } - if( z[i+1]=='0' ){ - if( sqlite3Isdigit(z[i+2]) ){ - pParse->iErr = i+1; - return -1; - }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ - pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; - for(j=i+4; sqlite3Isxdigit(z[j]); j++){} - goto parse_number_finish; - } - } - } - } - parse_number_2: - for(j=i+1;; j++){ - c = z[j]; - if( sqlite3Isdigit(c) ) continue; - if( c=='.' ){ - if( seenDP==JSON_REAL ){ - pParse->iErr = j; - return -1; - } - seenDP = JSON_REAL; - continue; - } - if( c=='e' || c=='E' ){ - if( z[j-1]<'0' ){ - if( ALWAYS(z[j-1]=='.') - && ALWAYS(j-2>=i) - && sqlite3Isdigit(z[j-2]) - ){ - pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; - }else{ - pParse->iErr = j; - return -1; - } - } - if( seenE ){ - pParse->iErr = j; - return -1; - } - seenDP = JSON_REAL; - seenE = 1; - c = z[j+1]; - if( c=='+' || c=='-' ){ - j++; - c = z[j+1]; - } - if( c<'0' || c>'9' ){ - pParse->iErr = j; - return -1; - } - continue; - } - break; - } - if( z[j-1]<'0' ){ - if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ - pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; - }else{ - pParse->iErr = j; - return -1; - } - } - parse_number_finish: - jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); - return j; - } - case '}': { - pParse->iErr = i; - return -2; /* End of {...} */ - } - case ']': { - pParse->iErr = i; - return -3; /* End of [...] */ - } - case ',': { - pParse->iErr = i; - return -4; /* List separator */ - } - case ':': { - pParse->iErr = i; - return -5; /* Object label/value separator */ - } - case 0: { - return 0; /* End of file */ - } - case 0x09: - case 0x0a: - case 0x0d: - case 0x20: { - do{ - i++; - }while( fast_isspace(z[i]) ); - goto json_parse_restart; - } - case 0x0b: - case 0x0c: - case '/': - case 0xc2: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xef: { - j = json5Whitespace(&z[i]); - if( j>0 ){ - i += j; - pParse->hasNonstd = 1; - goto json_parse_restart; - } - pParse->iErr = i; - return -1; - } - case 'n': { - if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return i+4; - } - /* fall-through into the default case that checks for NaN */ - } - default: { - u32 k; - int nn; - c = z[i]; - for(k=0; khasNonstd = 1; - return i + nn; - } - pParse->iErr = i; - return -1; /* Syntax error */ - } - } /* End switch(z[i]) */ -} - -/* -** Parse JSON (either pure RFC-8259 JSON text, or JSON-5 text, or a JSONB -** blob) into the JsonNode representation. -** -** Return 0 on success or non-zero if there are any errors. -** If an error occurs, free all memory held by pParse, but not pParse itself. -** -** pParse must be initialized with pParse->zJson set to the input text or -** blob prior to calling this routine. -*/ -static int jsonParse( - JsonParse *pParse, /* Initialize and fill this JsonParse object */ - sqlite3_context *pCtx /* Report errors here */ -){ - int i; - const char *zJson = pParse->zJson; - if( pParse->isBinary ){ - pParse->aBlob = (u8*)pParse->zJson; - pParse->nBlob = pParse->nJson; - i = jsonXlateBlobToNode(pParse, 0); - }else{ - i = jsonXlateTextToNode(pParse, 0); - } - if( pParse->oom ) i = -1; - if( !pParse->isBinary && i>0 ){ - assert( pParse->iDepth==0 ); - while( fast_isspace(zJson[i]) ) i++; - if( zJson[i] ){ - i += json5Whitespace(&zJson[i]); - if( zJson[i] ){ - jsonParseReset(pParse); - return 1; - } - pParse->hasNonstd = 1; - } - } - if( i<=0 ){ - if( pCtx!=0 ){ - if( pParse->oom ){ - sqlite3_result_error_nomem(pCtx); - }else{ - sqlite3_result_error(pCtx, "malformed JSON", -1); - } - } - jsonParseReset(pParse); - return 1; - } - return 0; -} - /* ** Magic number used for the JSON parse cache in sqlite3_get_auxdata() */ #define JSON_CACHE_ID (-429938) /* First cache entry */ #define JSON_CACHE_SZ 4 /* Max number of cache entries */ -/* -** Obtain a complete parse of the JSON found in the pJson argument -** -** Use the sqlite3_get_auxdata() cache to find a preexisting parse -** if it is available. If the cache is not available or if it -** is no longer valid, parse the JSON again and return the new parse. -** Also register the new parse so that it will be available for -** future sqlite3_get_auxdata() calls. -** -** If an error occurs and pErrCtx!=0 then report the error on pErrCtx -** and return NULL. -** -** The returned pointer (if it is not NULL) is owned by the cache in -** most cases, not the caller. The caller does NOT need to invoke -** jsonParseFree(), in most cases. -** -** Except, if an error occurs and pErrCtx==0 then return the JsonParse -** object with JsonParse.nErr non-zero and the caller will own the JsonParse -** object. In that case, it will be the responsibility of the caller to -** invoke jsonParseFree(). To summarize: -** -** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the -** cache. Call does not need to -** free it. -** -** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller -** and so the caller must free it. -*/ -static JsonParse *jsonParseCached( - sqlite3_context *pCtx, /* Context to use for cache search */ - sqlite3_value *pJson, /* Function param containing JSON text */ - sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ - int bUnedited /* No prior edits allowed */ -){ - char *zJson; - int nJson; - JsonParse *p; - JsonParse *pMatch = 0; - int iKey; - int iMinKey = 0; - u32 iMinHold = 0xffffffff; - u32 iMaxHold = 0; - int bJsonRCStr; - int isBinary; - - if( jsonFuncArgMightBeBinary(pJson) ){ - zJson = (char*)sqlite3_value_blob(pJson); - isBinary = 1; - }else{ - zJson = (char*)sqlite3_value_text(pJson); - isBinary = 0; - } - nJson = sqlite3_value_bytes(pJson); - - if( zJson==0 ) return 0; - for(iKey=0; iKeynJson==nJson - && (p->hasMod==0 || bUnedited==0) - && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0) - ){ - p->nErr = 0; - p->useMod = 0; - pMatch = p; - }else - if( pMatch==0 - && p->zAlt!=0 - && bUnedited==0 - && p->nAlt==nJson - && memcmp(p->zAlt, zJson, nJson)==0 - ){ - p->nErr = 0; - p->useMod = 1; - pMatch = p; - }else if( p->iHoldiHold; - iMinKey = iKey; - } - if( p->iHold>iMaxHold ){ - iMaxHold = p->iHold; - } - } - if( pMatch ){ - /* The input JSON text was found in the cache. Use the preexisting - ** parse of this JSON */ - pMatch->nErr = 0; - pMatch->iHold = iMaxHold+1; - assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ - return pMatch; - } - - /* The input JSON was not found anywhere in the cache. We will need - ** to parse it ourselves and generate a new JsonParse object. - */ - bJsonRCStr = sqlite3ValueIsOfClass(pJson,sqlite3RCStrUnref); - p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) ); - if( p==0 ){ - sqlite3_result_error_nomem(pCtx); - return 0; - } - memset(p, 0, sizeof(*p)); - if( bJsonRCStr ){ - p->zJson = sqlite3RCStrRef(zJson); - p->bJsonIsRCStr = 1; - }else{ - p->zJson = (char*)&p[1]; - memcpy(p->zJson, zJson, nJson+(isBinary==0)); - } - p->nJPRef = 1; - p->isBinary = isBinary; - p->nJson = nJson; - if( jsonParse(p, pErrCtx) ){ - if( pErrCtx==0 ){ - p->nErr = 1; - assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ - return p; - } - jsonParseFree(p); - return 0; - } - p->iHold = iMaxHold+1; - /* Transfer ownership of the new JsonParse to the cache */ - sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, - (void(*)(void*))jsonParseFree); - return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); -} /* ** Compare the OBJECT label at pNode against zKey,nKey. Return true on @@ -2289,7 +1656,11 @@ json_parse_restart: parse_string: cDelim = z[i]; nn = pParse->nJson; - for(j=i+1; j=nn ){ + pParse->iErr = j; + return -1; + } if( jsonIsOk[(unsigned char)z[j]] ) continue; c = z[j]; if( c==cDelim ){ @@ -2920,115 +2291,6 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ return sz+n==(u32)nBlob; } -/* Translate a single element of JSONB into the JsonNode format. The -** first byte of the element to be translated is at pParse->aBlob[i]. -** Return the index in pParse->aBlob[] of the first byte past the end -** of the JSONB element. Append the JsonNode translation in -** pParse->aNode[], which is increased in size as necessary. -*/ -static int jsonXlateBlobToNode(JsonParse *pParse, u32 i){ - u8 t; /* Node type */ - u32 sz; /* Node size */ - u32 x; /* Index of payload start */ - - const char *zPayload; - x = jsonbPayloadSize(pParse, i, &sz); - if( x==0 ) return -1; - t = pParse->zJson[i] & 0x0f; - zPayload = &pParse->zJson[i+x]; - switch( t ){ - case JSONB_NULL: { - if( sz>0 ) return -1; - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - break; - } - case JSONB_TRUE: { - if( sz>0 ) return -1; - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); - break; - } - case JSONB_FALSE: { - if( sz>0 ) return -1; - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); - break; - } - case JSONB_INT: { - if( sz==0 ) return -1; - jsonParseAddNode(pParse, JSON_INT, sz, zPayload); - break; - } - case JSONB_INT5: { - if( sz==0 ) return -1; - pParse->hasNonstd = 1; - jsonParseAddNode(pParse, JSON_INT | (JNODE_JSON5<<8), sz, zPayload); - break; - } - case JSONB_FLOAT: { - if( sz==0 ) return -1; - jsonParseAddNode(pParse, JSON_REAL, sz, zPayload); - break; - } - case JSONB_FLOAT5: { - if( sz==0 ) return -1; - pParse->hasNonstd = 1; - jsonParseAddNode(pParse, JSON_REAL | (JNODE_JSON5<<8), sz, zPayload); - break; - } - case JSONB_TEXTRAW: { - jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), sz, zPayload); - break; - } - case JSONB_TEXT: { - jsonParseAddNode(pParse, JSON_STRING, sz, zPayload); - break; - } - case JSONB_TEXTJ: { - jsonParseAddNode(pParse, JSON_STRING | (JNODE_ESCAPE<<8), sz, zPayload); - break; - } - case JSONB_TEXT5: { - pParse->hasNonstd = 1; - jsonParseAddNode(pParse, JSON_STRING | ((JNODE_ESCAPE|JNODE_JSON5)<<8), - sz, zPayload); - break; - } - case JSONB_ARRAY: { - int iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); - u32 j = i+x; - while( joom ){ - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; - } - break; - } - case JSONB_OBJECT: { - int iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - u32 j = i+x, k = 0; - while( joom ){ - pParse->aNode[pParse->nNode-1].jnFlags |= JNODE_LABEL; - } - j = (u32)r; - } - if( !pParse->oom ){ - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; - } - if( k&1 ) return -1; - break; - } - default: { - return -1; - } - } - return i+x+sz; -} - /* ** Given that a JSONB_ARRAY object starts at offset i, return ** the number of entries in that array. @@ -3730,16 +2992,27 @@ static JsonParse *jsonParseFuncArg( p->nJson = sqlite3_value_bytes(pArg); if( p->nJson==0 ) goto json_pfa_malformed; if( p->zJson==0 ) goto json_pfa_oom; + if( flgs & JSON_KEEPERROR ) ctx = 0; if( jsonConvertTextToBlob(p, ctx) ){ - jsonParseFree(p); - return 0; + if( flgs & JSON_KEEPERROR ){ + p->nErr = 1; + return p; + }else{ + jsonParseFree(p); + return 0; + } } return p; json_pfa_malformed: - jsonParseFree(p); - sqlite3_result_error(ctx, "malformed JSON", -1); - return 0; + if( flgs & JSON_KEEPERROR ){ + p->nErr = 1; + return p; + }else{ + jsonParseFree(p); + sqlite3_result_error(ctx, "malformed JSON", -1); + return 0; + } json_pfa_oom: jsonParseFree(p); @@ -4761,15 +4034,22 @@ static void jsonValidFunc( break; } default: { + JsonParse px; if( (flags & 0x3)==0 ) break; - p = jsonParseCached(ctx, argv[0], 0, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else if( p->nErr ){ + memset(&px, 0, sizeof(px)); + + p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR); + if( p ){ + if( p->oom ){ + sqlite3_result_error_nomem(ctx); + }else if( p->nErr ){ + /* no-op */ + }else if( (flags & 0x02)!=0 || p->hasNonstd==0 || p->useMod ){ + res = 1; + } jsonParseFree(p); - }else if( (flags & 0x02)!=0 || p->hasNonstd==0 || p->useMod ){ - res = 1; + }else{ + sqlite3_result_error_nomem(ctx); } break; } From 4b9ed1b25657801bc43840bcf6cfde5cd6b69c33 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 30 Nov 2023 23:36:14 +0000 Subject: [PATCH 284/347] Remove all trace of JsonNode from the JSON implementation. The JSONB format is used as the internal binary encoding for searching and editing. FossilOrigin-Name: 11ebb5f712cc7a515e2e0f2be8c1d71de20c97fe5b74c4f4d72c84fd21182d35 --- manifest | 12 +- manifest.uuid | 2 +- src/json.c | 477 ++++---------------------------------------------- 3 files changed, 43 insertions(+), 448 deletions(-) diff --git a/manifest b/manifest index 0d5accceac..0f5a595ba2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Convert\sjson_valid()\sover\sto\susing\sonly\sJSONB\sas\sits\sinternal\sformat. -D 2023-11-30T20:57:48.608 +C Remove\sall\strace\sof\sJsonNode\sfrom\sthe\sJSON\simplementation.\s\sThe\sJSONB\sformat\nis\sused\sas\sthe\sinternal\sbinary\sencoding\sfor\ssearching\sand\sediting. +D 2023-11-30T23:36:14.577 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 04044605d6eb39c6c8a4cdd42a01c256d10eed88421770c7e92d391164ed5a5b +F src/json.c c4f3602115334c210fbcdfb9565ba9fd322d29f7327b598fc0dd5dea4cb1b068 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 83074835b900ce85cf67059e674ce959801505c37592671af25ca0af7ed483f1 -R 2a1e3393332b0e1afccf3cc9eb62658b +P 7b5756fa6d00b093bf083a8d7a5ef5485f7a09e4eac473785c8380688f861a1b +R 38620fda20feff15c621a1f3bd3d0184 U drh -Z 8664a63b705df80c188b26ef2d7670eb +Z 66786c8d6e89beb8e06f2e4708cb9069 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0476c44651..be5cc905b1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7b5756fa6d00b093bf083a8d7a5ef5485f7a09e4eac473785c8380688f861a1b \ No newline at end of file +11ebb5f712cc7a515e2e0f2be8c1d71de20c97fe5b74c4f4d72c84fd21182d35 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f9e2ee5aa9..713dc76da9 100644 --- a/src/json.c +++ b/src/json.c @@ -20,7 +20,7 @@ ** All generated JSON text still conforms strictly to RFC-8259, but text ** with JSON-5 extensions is accepted as input. ** -** Beginning with version 3.44.0 (pending), these routines also accept +** Beginning with version 3.45.0 (pending), these routines also accept ** BLOB values that have JSON encoded using a binary representation we ** call JSONB. The name JSONB comes from PostgreSQL, however the on-disk ** format SQLite JSONB is completely different and incompatible with @@ -133,7 +133,7 @@ #define JSONB_ARRAY 11 /* An array */ #define JSONB_OBJECT 12 /* An object */ -/* Human-readalbe names for the JSONB values: +/* Human-readable names for the JSONB values: */ static const char * const jsonbType[] = { "null", "true", "false", "integer", "integer", @@ -201,9 +201,7 @@ static const char jsonIsOk[256] = { /* Objects */ typedef struct JsonString JsonString; -typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; -typedef struct JsonCleanup JsonCleanup; /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator @@ -224,49 +222,11 @@ struct JsonString { #define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ #define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ -/* A deferred cleanup task. A list of JsonCleanup objects might be -** run when the JsonParse object is destroyed. -*/ -struct JsonCleanup { - JsonCleanup *pJCNext; /* Next in a list */ - void (*xOp)(void*); /* Routine to run */ - void *pArg; /* Argument to xOp() */ -}; - -/* JSON type values for JsonNode.eType -*/ -#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */ -#define JSON_NULL 1 -#define JSON_TRUE 2 -#define JSON_FALSE 3 -#define JSON_INT 4 -#define JSON_REAL 5 -#define JSON_STRING 6 -#define JSON_ARRAY 7 -#define JSON_OBJECT 8 - -/* Human-readalbe names for the JsonNode types: -*/ -static const char * const jsonType[] = { - "subst", - "null", "true", "false", "integer", "real", "text", "array", "object" -}; - /* The "subtype" set for text JSON values passed through using ** sqlite3_result_subtype() and sqlite3_value_subtype(). */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ -/* Bit values for the JsonNode.jnFlag field -*/ -#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ -#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ -#define JNODE_REMOVE 0x04 /* Do not output */ -#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */ -#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */ -#define JNODE_LABEL 0x20 /* Is a label of an object */ -#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */ - /* ** Bit values for the flags passed into jsonExtractFunc() or ** jsonSetFunc() via the user-data value. @@ -278,76 +238,35 @@ static const char * const jsonType[] = { #define JSON_BLOB 0x08 /* Use the BLOB output format */ -/* A single node of parsed JSON. An array of these nodes describes -** a parse of JSON + edits. +/* A parsed JSON value. Lifecycle: ** -** Use the json_parse() SQL function (available when compiled with -** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including -** a complete listing and decoding of the array of JsonNodes. -*/ -struct JsonNode { - u8 eType; /* One of the JSON_ type values */ - u8 jnFlags; /* JNODE flags */ - u8 eU; /* Which union element to use */ - u32 n; /* Bytes of content for INT, REAL or STRING - ** Number of sub-nodes for ARRAY and OBJECT - ** Node that SUBST applies to */ - union { - const char *zJContent; /* 1: Content for INT, REAL, and STRING */ - u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ - u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ - u32 iPrev; /* 4: Previous SUBST node, or 0 */ - } u; -}; - - -/* A parsed and possibly edited JSON string. Lifecycle: +** 1. JSON comes in and is parsed into a JSONB value in aBlob. The +** original text is stored in zJson. ** -** 1. JSON comes in and is parsed into an array aNode[]. The original -** JSON text is stored in zJson. +** 2. The aBlob is searched using the JSON path notation, if needed. +** +** 3. Zero or more changes are made to aBlob (via json_remove() or +** json_replace() or similar). ** -** 2. Zero or more changes are made (via json_remove() or json_replace() -** or similar) to the aNode[] array. +** 4. New JSON text is generated from the aBlob for output. ** -** 3. A new, edited and mimified JSON string is generated from aNode -** and stored in zAlt. The JsonParse object always owns zAlt. -** -** Step 1 always happens. Step 2 and 3 may or may not happen, depending -** on the operation. -** -** aNode[].u.zJContent entries typically point into zJson. Hence zJson -** must remain valid for the lifespan of the parse. For edits, -** aNode[].u.zJContent might point to malloced space other than zJson. -** Entries in pClup are responsible for freeing that extra malloced space. -** -** When walking the parse tree in aNode[], edits are ignored if useMod is -** false. +** Step 1 is omitted if the input is a BLOB in the JSONB format. Step 4 +** is omitted if the output is JSONB or some other value that is not +** JSON text. */ struct JsonParse { - u32 nNode; /* Number of slots of aNode[] used */ - u32 nAlloc; /* Number of slots of aNode[] allocated */ - JsonNode *aNode; /* Array of nodes containing the parse */ + u8 *aBlob; /* JSONB representation of JSON value */ + u32 nBlob; /* Bytes of aBlob[] actually used */ + u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ char *zJson; /* Original JSON string (before edits) */ - char *zAlt; /* Revised and/or mimified JSON */ - JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ - u8 useMod; /* Actually use the edits contain inside aNode */ - u8 hasMod; /* aNode contains edits from the original zJson */ - u8 isBinary; /* True if zJson is the binary encoding */ u32 nJPRef; /* Number of references to this object */ int nJson; /* Length of the zJson string in bytes */ - int nAlt; /* Length of alternative JSON string zAlt, in bytes */ u32 iErr; /* Error location in zJson[] */ - u32 iSubst; /* Last JSON_SUBST entry in aNode[] */ - u32 iHold; /* Age of this entry in the cache for LRU replacement */ - /* Storage for the binary JSONB format */ - u32 nBlob; /* Bytes of aBlob[] actually used */ - u32 nBlobAlloc; /* Bytes allocated to aBlob[] */ - u8 *aBlob; /* BLOB representation of zJson */ /* Search and edit information. See jsonLookupBlobStep() */ u8 eEdit; /* Edit operation to apply */ int delta; /* Size change due to the edit */ @@ -381,9 +300,7 @@ struct JsonParse { ** Forward references **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); -static int jsonParseAddNode(JsonParse*,u32,u32,const char*); static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); -static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); @@ -678,49 +595,20 @@ static void jsonReturnString(JsonString *p){ } /************************************************************************** -** Utility routines for dealing with JsonNode and JsonParse objects +** Utility routines for dealing with JsonParse objects **************************************************************************/ -/* -** Return the number of consecutive JsonNode slots need to represent -** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and -** OBJECT types, the number might be larger. -** -** Appended elements are not counted. The value returned is the number -** by which the JsonNode counter should increment in order to go to the -** next peer value. -*/ -static u32 jsonNodeSize(JsonNode *pNode){ - return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; -} - /* ** Reclaim all memory allocated by a JsonParse object. But do not ** delete the JsonParse object itself. */ static void jsonParseReset(JsonParse *pParse){ - while( pParse->pClup ){ - JsonCleanup *pTask = pParse->pClup; - pParse->pClup = pTask->pJCNext; - pTask->xOp(pTask->pArg); - sqlite3_free(pTask); - } assert( pParse->nJPRef<=1 ); - if( pParse->aNode ){ - sqlite3_free(pParse->aNode); - pParse->aNode = 0; - } - pParse->nNode = 0; - pParse->nAlloc = 0; if( pParse->bJsonIsRCStr ){ sqlite3RCStrUnref(pParse->zJson); pParse->zJson = 0; pParse->bJsonIsRCStr = 0; } - if( pParse->zAlt ){ - sqlite3RCStrUnref(pParse->zAlt); - pParse->zAlt = 0; - } if( pParse->nBlobAlloc ){ sqlite3_free(pParse->aBlob); pParse->aBlob = 0; @@ -788,61 +676,6 @@ static u32 jsonHexToInt4(const char *z){ #endif -/* -** Add a single node to pParse->aNode after first expanding the -** size of the aNode array. Return the index of the new node. -** -** If an OOM error occurs, set pParse->oom and return -1. -*/ -static JSON_NOINLINE int jsonParseAddNodeExpand( - JsonParse *pParse, /* Append the node to this object */ - u32 eType, /* Node type */ - u32 n, /* Content size or sub-node count */ - const char *zContent /* Content */ -){ - u32 nNew; - JsonNode *pNew; - assert( pParse->nNode>=pParse->nAlloc ); - if( pParse->oom ) return -1; - nNew = pParse->nAlloc*2 + 10; - pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); - if( pNew==0 ){ - pParse->oom = 1; - return -1; - } - pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode); - pParse->aNode = pNew; - assert( pParse->nNodenAlloc ); - return jsonParseAddNode(pParse, eType, n, zContent); -} - -/* -** Create a new JsonNode instance based on the arguments and append that -** instance to the JsonParse. Return the index in pParse->aNode[] of the -** new node, or -1 if a memory allocation fails. -*/ -static int jsonParseAddNode( - JsonParse *pParse, /* Append the node to this object */ - u32 eType, /* Node type */ - u32 n, /* Content size or sub-node count */ - const char *zContent /* Content */ -){ - JsonNode *p; - assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc ); - if( pParse->nNode>=pParse->nAlloc ){ - return jsonParseAddNodeExpand(pParse, eType, n, zContent); - } - assert( pParse->aNode!=0 ); - p = &pParse->aNode[pParse->nNode]; - assert( p!=0 ); - p->eType = (u8)(eType & 0xff); - p->jnFlags = (u8)(eType >> 8); - JSON_VVA( p->eU = zContent ? 1 : 0 ); - p->n = n; - p->u.zJContent = zContent; - return pParse->nNode++; -} - /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ @@ -996,11 +829,11 @@ static const struct NanInfName { char *zMatch; char *zRepl; } aNanInfName[] = { - { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" }, - { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, - { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, - { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, - { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, + { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" }, + { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" }, + { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" }, + { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" }, + { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, }; /* @@ -1010,232 +843,6 @@ static const struct NanInfName { #define JSON_CACHE_SZ 4 /* Max number of cache entries */ -/* -** Compare the OBJECT label at pNode against zKey,nKey. Return true on -** a match. -*/ -static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ - if( pNode->eType!=JSON_STRING ) return 0; - if( pNode->n!=nKey ) return 0; - return strncmp(pNode->u.zJContent, zKey, nKey)==0; -} - -/* -** Search along zPath to find the node specified. Return a pointer -** to that node, or NULL if zPath is malformed or if there is no such -** node. -** -** If pApnd!=0, then try to append new nodes to complete zPath if it is -** possible to do so and if no existing node corresponds to zPath. If -** new nodes are appended *pApnd is set to 1. -*/ -static JsonNode *jsonLookupStep( - JsonParse *pParse, /* The JSON to search */ - u32 iRoot, /* Begin the search at this node */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - const char **pzErr /* Make *pzErr point to any syntax error in zPath */ -){ - u32 i, j, nKey; - const char *zKey; - JsonNode *pRoot; - if( pParse->oom ) return 0; - pRoot = &pParse->aNode[iRoot]; - if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){ - while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ - u32 idx = (u32)(pRoot - pParse->aNode); - i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pRoot = &pParse->aNode[i+1]; - iRoot = i+1; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - if( pRoot->jnFlags & JNODE_REMOVE ){ - return 0; - } - } - if( zPath[0]==0 ) return pRoot; - if( zPath[0]=='.' ){ - if( pRoot->eType!=JSON_OBJECT ) return 0; - zPath++; - if( zPath[0]=='"' ){ - zKey = zPath + 1; - for(i=1; zPath[i] && zPath[i]!='"'; i++){} - nKey = i-1; - if( zPath[i] ){ - i++; - }else{ - *pzErr = zPath; - return 0; - } - testcase( nKey==0 ); - }else{ - zKey = zPath; - for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} - nKey = i; - if( nKey==0 ){ - *pzErr = zPath; - return 0; - } - } - j = 1; - for(;;){ - while( j<=pRoot->n ){ - if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ - return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); - } - j++; - j += jsonNodeSize(&pRoot[j]); - } - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( pApnd ){ - u32 iStart, iLabel; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); - iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - zPath += i; - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - JSON_VVA( pRoot->eU = 2 ); - pParse->aNode[iLabel].jnFlags |= JNODE_RAW; - } - return pNode; - } - }else if( zPath[0]=='[' ){ - i = 0; - j = 1; - while( sqlite3Isdigit(zPath[j]) ){ - i = i*10 + zPath[j] - '0'; - j++; - } - if( j<2 || zPath[j]!=']' ){ - if( zPath[1]=='#' ){ - JsonNode *pBase = pRoot; - int iBase = iRoot; - if( pRoot->eType!=JSON_ARRAY ) return 0; - for(;;){ - while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - i++; - } - j += jsonNodeSize(&pBase[j]); - } - if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pBase->eU==2 ); - iBase = pBase->u.iAppend; - pBase = &pParse->aNode[iBase]; - j = 1; - } - j = 2; - if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ - unsigned int x = 0; - j = 3; - do{ - x = x*10 + zPath[j] - '0'; - j++; - }while( sqlite3Isdigit(zPath[j]) ); - if( x>i ) return 0; - i -= x; - } - if( zPath[j]!=']' ){ - *pzErr = zPath; - return 0; - } - }else{ - *pzErr = zPath; - return 0; - } - } - if( pRoot->eType!=JSON_ARRAY ) return 0; - zPath += j + 1; - j = 1; - for(;;){ - while( j<=pRoot->n - && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) - ){ - if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; - j += jsonNodeSize(&pRoot[j]); - } - if( i==0 && j<=pRoot->n ) break; - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( j<=pRoot->n ){ - return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); - } - if( i==0 && pApnd ){ - u32 iStart; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - JSON_VVA( pRoot->eU = 2 ); - } - return pNode; - } - }else{ - *pzErr = zPath; - } - return 0; -} - -/* -** Append content to pParse that will complete zPath. Return a pointer -** to the inserted node, or return NULL if the append fails. -*/ -static JsonNode *jsonLookupAppend( - JsonParse *pParse, /* Append content to the JSON parse */ - const char *zPath, /* Description of content to append */ - int *pApnd, /* Set this flag to 1 */ - const char **pzErr /* Make this point to any syntax error */ -){ - *pApnd = 1; - if( zPath[0]==0 ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; - } - if( zPath[0]=='.' ){ - jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - }else if( strncmp(zPath,"[0]",3)==0 ){ - jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); - }else{ - return 0; - } - if( pParse->oom ) return 0; - return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); -} - /* ** Compute the text of an error in JSON path syntax. ** @@ -1919,7 +1526,7 @@ json_parse_restart: continue; } if( sqlite3Isalnum(z[i+nn]) ) continue; - if( aNanInfName[k].eType==JSON_REAL ){ + if( aNanInfName[k].eType==JSONB_FLOAT ){ jsonBlobAppendOneByte(pParse, JSONB_FLOAT | 0x50); jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); }else{ @@ -3127,7 +2734,7 @@ static void jsonDebugPrintBlob( } } if( showContent ){ - if( sz==0 && x<=JSON_FALSE ){ + if( sz==0 && x<=JSONB_FALSE ){ printf("\n"); }else{ u32 i; @@ -4044,7 +3651,7 @@ static void jsonValidFunc( sqlite3_result_error_nomem(ctx); }else if( p->nErr ){ /* no-op */ - }else if( (flags & 0x02)!=0 || p->hasNonstd==0 || p->useMod ){ + }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){ res = 1; } jsonParseFree(p); @@ -4060,30 +3667,19 @@ static void jsonValidFunc( /* ** json_error_position(JSON) ** -** If the argument is not an interpretable JSON string, then return the 1-based -** character position at which the parser first recognized that the input -** was in error. The left-most character is 1. If the string is valid -** JSON, then return 0. +** If the argument is NULL, return NULL ** -** Note that json_valid() is only true for strictly conforming canonical JSON. -** But this routine returns zero if the input contains extension. Thus: +** If the argument is TEXT then try to interpret that text as JSON and +** return the 1-based character position for where the parser first recognized +** that the input was not valid JSON, or return 0 if the input text looks +** ok. JSON-5 extensions are accepted. ** -** (1) If the input X is strictly conforming canonical JSON: +** If the argument is a BLOB then try to interpret the blob as a JSONB +** and return the 1-based byte offset of the first position that is +** misformatted. Return 0 if the input BLOB seems to be well-formed. ** -** json_valid(X) returns true -** json_error_position(X) returns 0 -** -** (2) If the input X is JSON but it includes extension (such as JSON5) that -** are not part of RFC-8259: -** -** json_valid(X) returns false -** json_error_position(X) return 0 -** -** (3) If the input X cannot be interpreted as JSON even taking extensions -** into account: -** -** json_valid(X) return false -** json_error_position(X) returns 1 or more +** Numeric inputs are converted into text, which is usually valid +** JSON-5, so they should return 0. */ static void jsonErrorFunc( sqlite3_context *ctx, @@ -4684,7 +4280,7 @@ static int jsonEachColumn( break; } case JEACH_JSON: { - if( p->sParse.isBinary ){ + if( p->sParse.zJson==0 ){ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, SQLITE_STATIC); }else{ @@ -4795,7 +4391,6 @@ static int jsonEachFilter( if( p->sParse.aBlob==0 ){ return SQLITE_NOMEM; } - p->sParse.isBinary = 1; }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); p->sParse.nJson = sqlite3_value_bytes(argv[0]); From ca1ce7773c42cb128b424c77755f4413663bd81c Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 1 Dec 2023 12:57:12 +0000 Subject: [PATCH 285/347] First attempt to get the JSON text-to-binary cache working. All test cases pass, but the cache seems not to help much. FossilOrigin-Name: 25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c --- manifest | 16 +-- manifest.uuid | 2 +- src/json.c | 267 ++++++++++++++++++++++++++++++++++++------------ src/printf.c | 2 +- src/sqliteInt.h | 3 + 5 files changed, 215 insertions(+), 75 deletions(-) diff --git a/manifest b/manifest index 0f5a595ba2..3cac80e1a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sall\strace\sof\sJsonNode\sfrom\sthe\sJSON\simplementation.\s\sThe\sJSONB\sformat\nis\sused\sas\sthe\sinternal\sbinary\sencoding\sfor\ssearching\sand\sediting. -D 2023-11-30T23:36:14.577 +C First\sattempt\sto\sget\sthe\sJSON\stext-to-binary\scache\sworking.\s\sAll\stest\scases\npass,\sbut\sthe\scache\sseems\snot\sto\shelp\smuch. +D 2023-12-01T12:57:12.578 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c c4f3602115334c210fbcdfb9565ba9fd322d29f7327b598fc0dd5dea4cb1b068 +F src/json.c 9a96faf916f572deb94b6cc7673ba6e299c48d153c18218e104a2c6ae37c2c8e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -724,7 +724,7 @@ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 F src/pragma.c b5b4cff830575e6188cd56a295a57448d2b9dbc53f0dae58e22b97354cda3781 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 371f6115cb69286ebc12c6f2d7511279c2e47d9f54f475d46a554d687a3b312c -F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750 +F src/printf.c 18fbdf028345c8fbe6044f5f5bfda5a10d48d6287afef088cc21b0ca57985640 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 @@ -733,7 +733,7 @@ F src/shell.c.in 7bb83293775e1a5586d65212997442bc7acc70a2f1b781745da64ec3c2e4ea9 F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 6b82eb99a9d2887e873fb29e56befb7c50cf4624df615d23a28f071dc8abf5f6 +F src/sqliteInt.h aab66d149269f15f6f1011081b389f001f00b84045c69a4f9ec96dd68cc3a7d7 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2145,8 +2145,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 7b5756fa6d00b093bf083a8d7a5ef5485f7a09e4eac473785c8380688f861a1b -R 38620fda20feff15c621a1f3bd3d0184 +P 11ebb5f712cc7a515e2e0f2be8c1d71de20c97fe5b74c4f4d72c84fd21182d35 +R 18f6bb3dc7a73112aa5c773b9941447e U drh -Z 66786c8d6e89beb8e06f2e4708cb9069 +Z 9a005b4c0b238db6966807971445cc41 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index be5cc905b1..298549f7a8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -11ebb5f712cc7a515e2e0f2be8c1d71de20c97fe5b74c4f4d72c84fd21182d35 \ No newline at end of file +25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c \ No newline at end of file diff --git a/src/json.c b/src/json.c index 713dc76da9..44fa5bf0b2 100644 --- a/src/json.c +++ b/src/json.c @@ -165,7 +165,7 @@ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -#define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) +#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) /* ** Characters that are special to JSON. Control charaters, @@ -200,9 +200,35 @@ static const char jsonIsOk[256] = { #endif /* Objects */ +typedef struct JsonCache JsonCache; +typedef struct JsonCacheLine JsonCacheLine; typedef struct JsonString JsonString; typedef struct JsonParse JsonParse; + +/* +** Magic number used for the JSON parse cache in sqlite3_get_auxdata() +*/ +#define JSON_CACHE_ID (-429938) /* Cache entry */ +#define JSON_CACHE_SIZE 4 /* Max number of cache entries */ + +/* A cache mapping JSON text into JSONB blobs. +** +** All content, both JSON text and the JSONB blobs, is stored as RCStr +** objects. +*/ +struct JsonCacheLine { + u32 nJson; /* Size of the JSON text, in bytes */ + u32 nBlob; /* Size of the corresponding JSONB, in bytes */ + char *zJson; /* RCStr holding the JSON text */ + char *aBlob; /* RCStr holding the corresponding JSONB */ +}; +struct JsonCache { + sqlite3 *db; /* Database connection */ + int nUsed; /* Number of active entries in the cache */ + JsonCacheLine a[JSON_CACHE_SIZE]; /* One line for each cache entry */ +}; + /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator ** that can be and is used to create strings other than JSON. @@ -258,11 +284,11 @@ struct JsonParse { u8 *aBlob; /* JSONB representation of JSON value */ u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ - char *zJson; /* Original JSON string (before edits) */ + char *zJson; /* Json text used for parsing */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ - u8 bJsonIsRCStr; /* True if zJson is an RCStr */ + u8 bBlobIsRCStr; /* True if aBlob is an RCStr */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ u32 nJPRef; /* Number of references to this object */ int nJson; /* Length of the zJson string in bytes */ @@ -304,6 +330,113 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); +/************************************************************************** +** Utility routines for dealing with JsonCache objects +**************************************************************************/ + +/* +** Free a JsonCache object. +*/ +static void jsonCacheDelete(JsonCache *p){ + int i; + for(i=0; inUsed; i++){ + sqlite3RCStrUnref(p->a[i].zJson); + sqlite3RCStrUnref(p->a[i].aBlob); + } + sqlite3DbFree(p->db, p); +} +static void jsonCacheDeleteGeneric(void *p){ + jsonCacheDelete((JsonCache*)p); +} + +/* +** Insert a new entry into the cache. If the cache is full, expell +** the least recently used entry. Return SQLITE_OK on success or a +** result code otherwise. +** +** Both the input JSON and JSONB must be RCStr objects. +*/ +static int jsonCacheInsert( + sqlite3_context *ctx, /* The SQL statement context holding the cache */ + char *zJson, /* The key. Must be an RCStr! */ + u32 nJson, /* Number of bytes in zJson */ + char *aBlob, /* The value. Not an RCStr */ + u32 nBlob /* Number of bytes in aBlob */ +){ + JsonCache *p; + char *aRCBlob = 0; + + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ){ + sqlite3 *db = sqlite3_context_db_handle(ctx); + p = sqlite3DbMallocZero(db, sizeof(*p)); + if( p==0 ) return SQLITE_NOMEM; + p->db = db; + sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric); + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ) return SQLITE_NOMEM; + } + aRCBlob = sqlite3RCStrNew( nBlob ); + if( aRCBlob==0 ) return SQLITE_NOMEM; + memcpy(aRCBlob, aBlob, nBlob); + if( p->nUsed >= JSON_CACHE_SIZE ){ + sqlite3RCStrUnref(p->a[0].zJson); + sqlite3RCStrUnref(p->a[0].aBlob); + memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); + p->nUsed = JSON_CACHE_SIZE-1; + } + p->a[p->nUsed].nJson = nJson; + p->a[p->nUsed].nBlob = nBlob; + p->a[p->nUsed].zJson = sqlite3RCStrRef(zJson); + p->a[p->nUsed].aBlob = aRCBlob; + p->nUsed++; + return SQLITE_OK; +} + +/* +** Search for a cached translation of zJson (size: nJson bytes) into +** JSONB. Return it if found. +** +** The returned value is an RCStr object if it is not NULL. +** The caller is responsible for incrementing the reference count. +*/ +static u8 *jsonCacheSearch( + sqlite3_context *ctx, /* The SQL statement context holding the cache */ + char *zJson, /* The key. Might or might not be an RCStr */ + u32 nJson, /* Size of the key in bytes */ + u32 *pnBlob /* OUT: Size of the result in bytes */ +){ + JsonCache *p; + int i; + + assert( pnBlob!=0 ); + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ){ + *pnBlob = 0; + return 0; + } + for(i=0; inUsed; i++){ + if( p->a[i].zJson==zJson ) break; + } + if( i>=p->nUsed ){ + for(i=0; inUsed; i++){ + if( p->a[i].nJson!=nJson ) continue; + if( memcmp(p->a[i].zJson, zJson, nJson)==0 ) break; + } + } + if( inUsed ){ + if( inUsed-1 ){ + JsonCacheLine tmp = p->a[i]; + memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); + p->a[p->nUsed-1] = tmp; + } + *pnBlob = p->a[i].nBlob; + return (u8*)p->a[i].aBlob; + }else{ + *pnBlob = 0; + return 0; + } +} /************************************************************************** ** Utility routines for dealing with JsonString objects @@ -569,8 +702,18 @@ static void jsonAppendSqlValue( ** the result of the SQL function. ** ** The JsonString is reset. +** +** If pParse and ctx are both non-NULL and if pParse->aBlob is valid +** then an attempt is made to cache the translation from JSON text into +** the blob. */ -static void jsonReturnString(JsonString *p){ +static void jsonReturnString( + JsonString *p, /* String to return */ + JsonParse *pParse, /* JSONB source or NULL */ + sqlite3_context *ctx /* Where to cache */ +){ + assert( (pParse!=0)==(ctx!=0) ); + assert( ctx==0 || ctx==p->pCtx ); if( p->eErr==0 ){ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); if( flags & JSON_BLOB ){ @@ -580,6 +723,16 @@ static void jsonReturnString(JsonString *p){ SQLITE_TRANSIENT, SQLITE_UTF8); }else if( jsonForceRCStr(p) ){ sqlite3RCStrRef(p->zBuf); + if( pParse ){ + int rc = jsonCacheInsert(ctx, p->zBuf, p->nUsed, + (char*)pParse->aBlob, pParse->nBlob); + if( rc==SQLITE_NOMEM ){ + sqlite3RCStrUnref(p->zBuf); + sqlite3_result_error_nomem(ctx); + jsonStringReset(p); + return; + } + } sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, sqlite3RCStrUnref, SQLITE_UTF8); @@ -604,10 +757,12 @@ static void jsonReturnString(JsonString *p){ */ static void jsonParseReset(JsonParse *pParse){ assert( pParse->nJPRef<=1 ); - if( pParse->bJsonIsRCStr ){ - sqlite3RCStrUnref(pParse->zJson); - pParse->zJson = 0; - pParse->bJsonIsRCStr = 0; + if( pParse->bBlobIsRCStr ){ + assert( pParse->nBlobAlloc==0 ); + sqlite3RCStrUnref((char*)pParse->aBlob); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->bBlobIsRCStr = 0; } if( pParse->nBlobAlloc ){ sqlite3_free(pParse->aBlob); @@ -636,46 +791,18 @@ static void jsonParseFree(JsonParse *pParse){ } } -/* -** Translate a single byte of Hex into an integer. -** This routine only works if h really is a valid hexadecimal -** character: 0..9a..fA..F -*/ -static u8 jsonHexToInt(int h){ - if( !sqlite3Isxdigit(h) ) return 0; -#ifdef SQLITE_EBCDIC - h += 9*(1&~(h>>4)); -#else - h += 9*(1&(h>>6)); -#endif - return (u8)(h & 0xf); -} - /* ** Convert a 4-byte hex string into an integer */ static u32 jsonHexToInt4(const char *z){ u32 v; - v = (jsonHexToInt(z[0])<<12) - + (jsonHexToInt(z[1])<<8) - + (jsonHexToInt(z[2])<<4) - + jsonHexToInt(z[3]); + v = (sqlite3HexToInt(z[0])<<12) + + (sqlite3HexToInt(z[1])<<8) + + (sqlite3HexToInt(z[2])<<4) + + sqlite3HexToInt(z[3]); return v; } -/* -** A macro to hint to the compiler that a function should not be -** inlined. -*/ -#if defined(__GNUC__) -# define JSON_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) && _MSC_VER>=1310 -# define JSON_NOINLINE __declspec(noinline) -#else -# define JSON_NOINLINE -#endif - - /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ @@ -836,12 +963,6 @@ static const struct NanInfName { { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, }; -/* -** Magic number used for the JSON parse cache in sqlite3_get_auxdata() -*/ -#define JSON_CACHE_ID (-429938) /* First cache entry */ -#define JSON_CACHE_SZ 4 /* Max number of cache entries */ - /* ** Compute the text of an error in JSON path syntax. @@ -1147,8 +1268,8 @@ json_parse_restart: if( z[j]==':' ){ j++; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==':' ){ j++; goto parse_object_value; @@ -1173,8 +1294,8 @@ json_parse_restart: }else if( z[j]=='}' ){ break; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ @@ -1225,8 +1346,8 @@ json_parse_restart: }else if( z[j]==']' ){ break; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==',' ){ continue; }else if( z[j]==']' ){ @@ -1488,7 +1609,7 @@ json_parse_restart: case 0x20: { do{ i++; - }while( fast_isspace(z[i]) ); + }while( jsonIsspace(z[i]) ); goto json_parse_restart; } case 0x0b: @@ -1560,7 +1681,7 @@ static int jsonConvertTextToBlob( if( pParse->oom ) i = -1; if( i>0 ){ assert( pParse->iDepth==0 ); - while( fast_isspace(zJson[i]) ) i++; + while( jsonIsspace(zJson[i]) ) i++; if( zJson[i] ){ i += json5Whitespace(&zJson[i]); if( zJson[i] ){ @@ -2197,7 +2318,7 @@ static void jsonReturnTextJsonFromBlob( x.nBlob = nBlob; jsonStringInit(&s, ctx); jsonXlateBlobToText(&x, 0, &s); - jsonReturnString(&s); + jsonReturnString(&s, 0, 0); } @@ -2338,7 +2459,7 @@ static void jsonReturnFromBlob( }else if( c=='0' ){ c = 0; }else if( c=='x' ){ - c = (jsonHexToInt(z[iIn+1])<<4) | jsonHexToInt(z[iIn+2]); + c = (sqlite3HexToInt(z[iIn+1])<<4) | sqlite3HexToInt(z[iIn+2]); iIn += 2; }else if( c=='\r' && z[i+1]=='\n' ){ iIn++; @@ -2455,7 +2576,7 @@ static int jsonFunctionArgToBlob( } /* -** Generate a bad path error for json_extract() +** Generate a bad path error. */ static void jsonBadPathError( sqlite3_context *ctx, /* The function call containing the error */ @@ -2599,6 +2720,22 @@ static JsonParse *jsonParseFuncArg( p->nJson = sqlite3_value_bytes(pArg); if( p->nJson==0 ) goto json_pfa_malformed; if( p->zJson==0 ) goto json_pfa_oom; + + p->aBlob = jsonCacheSearch(ctx, p->zJson, p->nJson, &p->nBlob); + if( p->aBlob ){ + if( flgs & JSON_EDITABLE ){ + u8 *pNew = sqlite3_malloc64( p->nBlob ); + if( pNew==0 ) goto json_pfa_oom; + memcpy(pNew, p->aBlob, p->nBlob); + p->aBlob = pNew; + p->nBlobAlloc = p->nBlob; + }else{ + sqlite3RCStrRef((char*)p->aBlob); + p->bBlobIsRCStr = 1; + } + return p; + } + if( flgs & JSON_KEEPERROR ) ctx = 0; if( jsonConvertTextToBlob(p, ctx) ){ if( flgs & JSON_KEEPERROR ){ @@ -2650,7 +2787,7 @@ static void jsonReturnParse( JsonString s; jsonStringInit(&s, ctx); jsonXlateBlobToText(p, 0, &s); - jsonReturnString(&s); + jsonReturnString(&s, p, ctx); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } } @@ -2890,7 +3027,7 @@ static void jsonQuoteFunc( jsonStringInit(&jx, ctx); jsonAppendSqlValue(&jx, argv[0]); - jsonReturnString(&jx); + jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -2914,7 +3051,7 @@ static void jsonArrayFunc( jsonAppendSqlValue(&jx, argv[i]); } jsonAppendChar(&jx, ']'); - jsonReturnString(&jx); + jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -3058,7 +3195,7 @@ static void jsonExtractFunc( if( flags & JSON_JSON ){ jsonStringInit(&jx, ctx); jsonXlateBlobToText(p, j, &jx); - jsonReturnString(&jx); + jsonReturnString(&jx, 0, 0); jsonStringReset(&jx); assert( (flags & JSON_BLOB)==0 ); sqlite3_result_subtype(ctx, JSON_SUBTYPE); @@ -3091,7 +3228,7 @@ static void jsonExtractFunc( } if( argc>2 ){ jsonAppendChar(&jx, ']'); - jsonReturnString(&jx); + jsonReturnString(&jx, 0, 0); if( (flags & JSON_BLOB)==0 ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -3389,7 +3526,7 @@ static void jsonObjectFunc( jsonAppendSqlValue(&jx, argv[i+1]); } jsonAppendChar(&jx, '}'); - jsonReturnString(&jx); + jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -3767,7 +3904,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ jsonAppendChar(pStr, ']'); flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->eErr ){ - jsonReturnString(pStr); + jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); @@ -3887,7 +4024,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ pStr->pCtx = ctx; flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->eErr ){ - jsonReturnString(pStr); + jsonReturnString(pStr, 0, 0); return; }else if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); diff --git a/src/printf.c b/src/printf.c index 3c0b182d39..c6b3803ca9 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1369,7 +1369,7 @@ void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ /***************************************************************************** -** Reference counted string storage +** Reference counted string/blob storage *****************************************************************************/ /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9e57354db8..c7ced86999 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4116,6 +4116,9 @@ struct sqlite3_str { ** ** 3. Make a (read-only) copy of a read-only RCStr string using ** sqlite3RCStrRef(). +** +** "String" is in the name, but an RCStr object can also be used to hold +** binary data. */ struct RCStr { u64 nRCRef; /* Number of references */ From 5bfa7e65d10aa3fc9ad8c1a8a139ea516f11e4b7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 1 Dec 2023 13:28:13 +0000 Subject: [PATCH 286/347] Cache is working better, but does not preserve the hasJson5 flag. FossilOrigin-Name: a12add7ab9f5aee5bb2ede0c4d22e599dd28f7a107dce72b2ea48ef92d233e8a --- manifest | 15 +++++++++------ manifest.uuid | 2 +- src/json.c | 32 ++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 3cac80e1a7..8aa6ceaf3b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C First\sattempt\sto\sget\sthe\sJSON\stext-to-binary\scache\sworking.\s\sAll\stest\scases\npass,\sbut\sthe\scache\sseems\snot\sto\shelp\smuch. -D 2023-12-01T12:57:12.578 +C Cache\sis\sworking\sbetter,\sbut\sdoes\snot\spreserve\sthe\shasJson5\sflag. +D 2023-12-01T13:28:13.897 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9a96faf916f572deb94b6cc7673ba6e299c48d153c18218e104a2c6ae37c2c8e +F src/json.c 32e3741e5ff9f8380ebf84dcd6d3c28ddcd7f6a2923b72a05165ad108ca0c278 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 11ebb5f712cc7a515e2e0f2be8c1d71de20c97fe5b74c4f4d72c84fd21182d35 -R 18f6bb3dc7a73112aa5c773b9941447e +P 25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c +R b145e990ca1c690b2dc7d50ed4bd4abc +T *branch * jsonb-cache +T *sym-jsonb-cache * +T -sym-jsonb * U drh -Z 9a005b4c0b238db6966807971445cc41 +Z 663fd17309f10179421e079add21165c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 298549f7a8..697f12705c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c \ No newline at end of file +a12add7ab9f5aee5bb2ede0c4d22e599dd28f7a107dce72b2ea48ef92d233e8a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 44fa5bf0b2..5f02a1c8ed 100644 --- a/src/json.c +++ b/src/json.c @@ -358,13 +358,15 @@ static void jsonCacheDeleteGeneric(void *p){ */ static int jsonCacheInsert( sqlite3_context *ctx, /* The SQL statement context holding the cache */ - char *zJson, /* The key. Must be an RCStr! */ + char *zJson, /* The key. */ u32 nJson, /* Number of bytes in zJson */ - char *aBlob, /* The value. Not an RCStr */ + int bJsonIsRCStr, /* True if zJson is already an RCStr */ + u8 *aBlob, /* The value. */ u32 nBlob /* Number of bytes in aBlob */ ){ JsonCache *p; - char *aRCBlob = 0; + char *aRCBlob; + char *zRCJson; p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ){ @@ -379,6 +381,17 @@ static int jsonCacheInsert( aRCBlob = sqlite3RCStrNew( nBlob ); if( aRCBlob==0 ) return SQLITE_NOMEM; memcpy(aRCBlob, aBlob, nBlob); + if( bJsonIsRCStr ){ + zRCJson = sqlite3RCStrRef(zJson); + }else{ + zRCJson = sqlite3RCStrNew( nJson ); + if( zRCJson==0 ){ + sqlite3RCStrUnref(aRCBlob); + return SQLITE_NOMEM; + } + memcpy(zRCJson, zJson, nJson); + zRCJson[nJson] = 0; + } if( p->nUsed >= JSON_CACHE_SIZE ){ sqlite3RCStrUnref(p->a[0].zJson); sqlite3RCStrUnref(p->a[0].aBlob); @@ -387,7 +400,7 @@ static int jsonCacheInsert( } p->a[p->nUsed].nJson = nJson; p->a[p->nUsed].nBlob = nBlob; - p->a[p->nUsed].zJson = sqlite3RCStrRef(zJson); + p->a[p->nUsed].zJson = zRCJson; p->a[p->nUsed].aBlob = aRCBlob; p->nUsed++; return SQLITE_OK; @@ -429,6 +442,7 @@ static u8 *jsonCacheSearch( JsonCacheLine tmp = p->a[i]; memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); p->a[p->nUsed-1] = tmp; + i = p->nUsed - 1; } *pnBlob = p->a[i].nBlob; return (u8*)p->a[i].aBlob; @@ -724,8 +738,8 @@ static void jsonReturnString( }else if( jsonForceRCStr(p) ){ sqlite3RCStrRef(p->zBuf); if( pParse ){ - int rc = jsonCacheInsert(ctx, p->zBuf, p->nUsed, - (char*)pParse->aBlob, pParse->nBlob); + int rc = jsonCacheInsert(ctx, p->zBuf, p->nUsed, 1, + pParse->aBlob, pParse->nBlob); if( rc==SQLITE_NOMEM ){ sqlite3RCStrUnref(p->zBuf); sqlite3_result_error_nomem(ctx); @@ -2746,6 +2760,12 @@ static JsonParse *jsonParseFuncArg( return 0; } } + if( ctx ){ + int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); + int rc = jsonCacheInsert(ctx, p->zJson, p->nJson, isRCStr, + p->aBlob, p->nBlob); + if( rc==SQLITE_NOMEM ) goto json_pfa_oom; + } return p; json_pfa_malformed: From 063d0d4c3a453939237e64c8f9b3b6d96cad0a65 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 1 Dec 2023 18:46:14 +0000 Subject: [PATCH 287/347] Fix up the JSON cache to work better. FossilOrigin-Name: 1fdbc39521f63aedc6f08ecaafa54ea467b8c6316a692a18ad01eecbf22a0977 --- manifest | 15 ++-- manifest.uuid | 2 +- src/json.c | 201 +++++++++++++++++++++++++------------------------- 3 files changed, 109 insertions(+), 109 deletions(-) diff --git a/manifest b/manifest index 8aa6ceaf3b..9b30ebcd15 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Cache\sis\sworking\sbetter,\sbut\sdoes\snot\spreserve\sthe\shasJson5\sflag. -D 2023-12-01T13:28:13.897 +C Fix\sup\sthe\sJSON\scache\sto\swork\sbetter. +D 2023-12-01T18:46:14.485 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 32e3741e5ff9f8380ebf84dcd6d3c28ddcd7f6a2923b72a05165ad108ca0c278 +F src/json.c 3556e879386b0af1c542ed13ebb2f58c48d3b8b3ab13a96ab77ad2a9493cc715 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,11 +2145,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 25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c -R b145e990ca1c690b2dc7d50ed4bd4abc -T *branch * jsonb-cache -T *sym-jsonb-cache * -T -sym-jsonb * +P a12add7ab9f5aee5bb2ede0c4d22e599dd28f7a107dce72b2ea48ef92d233e8a +R bed96169a434a08ba8168cff3e09a739 U drh -Z 663fd17309f10179421e079add21165c +Z 599839251e392336311bcdbf8af02666 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 697f12705c..c67da5411d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a12add7ab9f5aee5bb2ede0c4d22e599dd28f7a107dce72b2ea48ef92d233e8a \ No newline at end of file +1fdbc39521f63aedc6f08ecaafa54ea467b8c6316a692a18ad01eecbf22a0977 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 5f02a1c8ed..08e0f4fb63 100644 --- a/src/json.c +++ b/src/json.c @@ -201,7 +201,6 @@ static const char jsonIsOk[256] = { /* Objects */ typedef struct JsonCache JsonCache; -typedef struct JsonCacheLine JsonCacheLine; typedef struct JsonString JsonString; typedef struct JsonParse JsonParse; @@ -217,16 +216,10 @@ typedef struct JsonParse JsonParse; ** All content, both JSON text and the JSONB blobs, is stored as RCStr ** objects. */ -struct JsonCacheLine { - u32 nJson; /* Size of the JSON text, in bytes */ - u32 nBlob; /* Size of the corresponding JSONB, in bytes */ - char *zJson; /* RCStr holding the JSON text */ - char *aBlob; /* RCStr holding the corresponding JSONB */ -}; struct JsonCache { - sqlite3 *db; /* Database connection */ - int nUsed; /* Number of active entries in the cache */ - JsonCacheLine a[JSON_CACHE_SIZE]; /* One line for each cache entry */ + sqlite3 *db; /* Database connection */ + int nUsed; /* Number of active entries in the cache */ + JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */ }; /* An instance of this object represents a JSON string @@ -285,13 +278,14 @@ struct JsonParse { u32 nBlob; /* Bytes of aBlob[] actually used */ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ char *zJson; /* Json text used for parsing */ + int nJson; /* Length of the zJson string in bytes */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ - u8 bBlobIsRCStr; /* True if aBlob is an RCStr */ + u8 bJsonIsRCStr; /* True if zJson is an RCStr */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ + u8 bReadOnly; /* Do not modify. */ u32 nJPRef; /* Number of references to this object */ - int nJson; /* Length of the zJson string in bytes */ u32 iErr; /* Error location in zJson[] */ /* Search and edit information. See jsonLookupBlobStep() */ u8 eEdit; /* Edit operation to apply */ @@ -330,6 +324,9 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); static u32 jsonXlateBlobToText(const JsonParse*,u32,JsonString*); static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); +static void jsonParseFree(JsonParse*); + + /************************************************************************** ** Utility routines for dealing with JsonCache objects **************************************************************************/ @@ -340,8 +337,7 @@ static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); static void jsonCacheDelete(JsonCache *p){ int i; for(i=0; inUsed; i++){ - sqlite3RCStrUnref(p->a[i].zJson); - sqlite3RCStrUnref(p->a[i].aBlob); + jsonParseFree(p->a[i]); } sqlite3DbFree(p->db, p); } @@ -358,16 +354,12 @@ static void jsonCacheDeleteGeneric(void *p){ */ static int jsonCacheInsert( sqlite3_context *ctx, /* The SQL statement context holding the cache */ - char *zJson, /* The key. */ - u32 nJson, /* Number of bytes in zJson */ - int bJsonIsRCStr, /* True if zJson is already an RCStr */ - u8 *aBlob, /* The value. */ - u32 nBlob /* Number of bytes in aBlob */ + JsonParse *pParse /* The parse object to be added to the cache */ ){ JsonCache *p; - char *aRCBlob; - char *zRCJson; + assert( pParse->zJson!=0 ); + assert( pParse->bJsonIsRCStr ); p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ){ sqlite3 *db = sqlite3_context_db_handle(ctx); @@ -378,76 +370,61 @@ static int jsonCacheInsert( p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ) return SQLITE_NOMEM; } - aRCBlob = sqlite3RCStrNew( nBlob ); - if( aRCBlob==0 ) return SQLITE_NOMEM; - memcpy(aRCBlob, aBlob, nBlob); - if( bJsonIsRCStr ){ - zRCJson = sqlite3RCStrRef(zJson); - }else{ - zRCJson = sqlite3RCStrNew( nJson ); - if( zRCJson==0 ){ - sqlite3RCStrUnref(aRCBlob); - return SQLITE_NOMEM; - } - memcpy(zRCJson, zJson, nJson); - zRCJson[nJson] = 0; - } if( p->nUsed >= JSON_CACHE_SIZE ){ - sqlite3RCStrUnref(p->a[0].zJson); - sqlite3RCStrUnref(p->a[0].aBlob); + jsonParseFree(p->a[0]); memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); p->nUsed = JSON_CACHE_SIZE-1; } - p->a[p->nUsed].nJson = nJson; - p->a[p->nUsed].nBlob = nBlob; - p->a[p->nUsed].zJson = zRCJson; - p->a[p->nUsed].aBlob = aRCBlob; + pParse->eEdit = 0; + pParse->nJPRef++; + pParse->bReadOnly = 1; + p->a[p->nUsed] = pParse; p->nUsed++; return SQLITE_OK; } /* -** Search for a cached translation of zJson (size: nJson bytes) into -** JSONB. Return it if found. -** -** The returned value is an RCStr object if it is not NULL. -** The caller is responsible for incrementing the reference count. +** Search for a cached translation the json text supplied by pArg. Return +** the JsonParse object if found. */ -static u8 *jsonCacheSearch( +static JsonParse *jsonCacheSearch( sqlite3_context *ctx, /* The SQL statement context holding the cache */ - char *zJson, /* The key. Might or might not be an RCStr */ - u32 nJson, /* Size of the key in bytes */ - u32 *pnBlob /* OUT: Size of the result in bytes */ + sqlite3_value *pArg /* Function argument containing SQL text */ ){ JsonCache *p; int i; + const char *zJson; + int nJson; + + if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){ + return 0; + } + zJson = (const char*)sqlite3_value_text(pArg); + if( zJson==0 ) return 0; + nJson = sqlite3_value_bytes(pArg); - assert( pnBlob!=0 ); p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); if( p==0 ){ - *pnBlob = 0; return 0; } for(i=0; inUsed; i++){ - if( p->a[i].zJson==zJson ) break; + if( p->a[i]->zJson==zJson ) break; } if( i>=p->nUsed ){ for(i=0; inUsed; i++){ - if( p->a[i].nJson!=nJson ) continue; - if( memcmp(p->a[i].zJson, zJson, nJson)==0 ) break; + if( p->a[i]->nJson!=nJson ) continue; + if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break; } } if( inUsed ){ if( inUsed-1 ){ - JsonCacheLine tmp = p->a[i]; + JsonParse *tmp = p->a[i]; memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); p->a[p->nUsed-1] = tmp; i = p->nUsed - 1; } - *pnBlob = p->a[i].nBlob; - return (u8*)p->a[i].aBlob; + return p->a[i]; }else{ - *pnBlob = 0; return 0; } } @@ -717,9 +694,9 @@ static void jsonAppendSqlValue( ** ** The JsonString is reset. ** -** If pParse and ctx are both non-NULL and if pParse->aBlob is valid -** then an attempt is made to cache the translation from JSON text into -** the blob. +** If pParse and ctx are both non-NULL, then the SQL string in p is +** loaded into the zJson field of the pParse object as a RCStr and the +** pParse is added to the cache. */ static void jsonReturnString( JsonString *p, /* String to return */ @@ -736,18 +713,19 @@ static void jsonReturnString( sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); }else if( jsonForceRCStr(p) ){ - sqlite3RCStrRef(p->zBuf); - if( pParse ){ - int rc = jsonCacheInsert(ctx, p->zBuf, p->nUsed, 1, - pParse->aBlob, pParse->nBlob); + if( pParse && pParse->bJsonIsRCStr==0 ){ + int rc; + pParse->zJson = sqlite3RCStrRef(p->zBuf); + pParse->nJson = p->nUsed; + pParse->bJsonIsRCStr = 1; + rc = jsonCacheInsert(ctx, pParse); if( rc==SQLITE_NOMEM ){ - sqlite3RCStrUnref(p->zBuf); sqlite3_result_error_nomem(ctx); jsonStringReset(p); return; } } - sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, + sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, sqlite3RCStrUnref, SQLITE_UTF8); }else{ @@ -771,12 +749,11 @@ static void jsonReturnString( */ static void jsonParseReset(JsonParse *pParse){ assert( pParse->nJPRef<=1 ); - if( pParse->bBlobIsRCStr ){ - assert( pParse->nBlobAlloc==0 ); - sqlite3RCStrUnref((char*)pParse->aBlob); - pParse->aBlob = 0; - pParse->nBlob = 0; - pParse->bBlobIsRCStr = 0; + if( pParse->bJsonIsRCStr ){ + sqlite3RCStrUnref(pParse->zJson); + pParse->zJson = 0; + pParse->nJson = 0; + pParse->bJsonIsRCStr = 0; } if( pParse->nBlobAlloc ){ sqlite3_free(pParse->aBlob); @@ -1048,6 +1025,7 @@ static int jsonBlobExpand(JsonParse *pParse, u32 N){ static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ u8 *aOld; u32 nSize; + assert( !pParse->bReadOnly ); if( pParse->nBlobAlloc>0 ) return 1; aOld = pParse->aBlob; nSize = pParse->nBlob + nExtra; @@ -2671,6 +2649,11 @@ jsonInsertIntoBlob_patherror: return; } +/* +** Make a copy of a JsonParse object. The copy will be editable. +*/ + + /* ** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, ** from the SQL function argument pArg. Return a pointer to the new @@ -2692,17 +2675,40 @@ static JsonParse *jsonParseFuncArg( sqlite3_value *pArg, u32 flgs ){ - int eType; /* Datatype of pArg */ - JsonParse *p = 0; /* Value to be returned */ + int eType; /* Datatype of pArg */ + JsonParse *p = 0; /* Value to be returned */ + JsonParse *pFromCache = 0; /* Value taken from cache */ assert( ctx!=0 ); eType = sqlite3_value_type(pArg); if( eType==SQLITE_NULL ){ return 0; } + pFromCache = jsonCacheSearch(ctx, pArg); + if( pFromCache ){ + pFromCache->nJPRef++; + if( (flgs & JSON_EDITABLE)==0 ){ + return pFromCache; + } + } +rebuild_from_cache: p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ) goto json_pfa_oom; memset(p, 0, sizeof(*p)); + p->nJPRef = 1; + if( pFromCache!=0 ){ + u32 nBlob = pFromCache->nBlob; + p->aBlob = sqlite3_malloc64( nBlob ); + if( p->aBlob==0 ) goto json_pfa_oom; + memcpy(p->aBlob, pFromCache->aBlob, nBlob); + p->nBlobAlloc = p->nBlob = nBlob; + p->hasNonstd = pFromCache->hasNonstd; + jsonParseFree(pFromCache); + return p; + }else{ + jsonParseFree(pFromCache); + pFromCache = 0; + } if( eType==SQLITE_BLOB ){ u32 n, sz = 0; p->aBlob = (u8*)sqlite3_value_blob(pArg); @@ -2729,29 +2735,11 @@ static JsonParse *jsonParseFuncArg( } return p; } - /* TODO: Check in the cache */ p->zJson = (char*)sqlite3_value_text(pArg); p->nJson = sqlite3_value_bytes(pArg); if( p->nJson==0 ) goto json_pfa_malformed; if( p->zJson==0 ) goto json_pfa_oom; - - p->aBlob = jsonCacheSearch(ctx, p->zJson, p->nJson, &p->nBlob); - if( p->aBlob ){ - if( flgs & JSON_EDITABLE ){ - u8 *pNew = sqlite3_malloc64( p->nBlob ); - if( pNew==0 ) goto json_pfa_oom; - memcpy(pNew, p->aBlob, p->nBlob); - p->aBlob = pNew; - p->nBlobAlloc = p->nBlob; - }else{ - sqlite3RCStrRef((char*)p->aBlob); - p->bBlobIsRCStr = 1; - } - return p; - } - - if( flgs & JSON_KEEPERROR ) ctx = 0; - if( jsonConvertTextToBlob(p, ctx) ){ + if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ if( flgs & JSON_KEEPERROR ){ p->nErr = 1; return p; @@ -2759,12 +2747,26 @@ static JsonParse *jsonParseFuncArg( jsonParseFree(p); return 0; } - } - if( ctx ){ + }else{ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); - int rc = jsonCacheInsert(ctx, p->zJson, p->nJson, isRCStr, - p->aBlob, p->nBlob); + int rc; + if( !isRCStr ){ + char *zNew = sqlite3RCStrNew( p->nJson ); + if( zNew==0 ) goto json_pfa_oom; + memcpy(zNew, p->zJson, p->nJson); + p->zJson = zNew; + p->zJson[p->nJson] = 0; + }else{ + sqlite3RCStrRef(p->zJson); + } + p->bJsonIsRCStr = 1; + rc = jsonCacheInsert(ctx, p); if( rc==SQLITE_NOMEM ) goto json_pfa_oom; + if( flgs & JSON_EDITABLE ){ + pFromCache = p; + p = 0; + goto rebuild_from_cache; + } } return p; @@ -2779,6 +2781,7 @@ json_pfa_malformed: } json_pfa_oom: + jsonParseFree(pFromCache); jsonParseFree(p); sqlite3_result_error_nomem(ctx); return 0; From b5effc0605abcf3f67a4c62ef42d0ded03a214fd Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 1 Dec 2023 20:09:59 +0000 Subject: [PATCH 288/347] Different approach to querying a tokendata=1 table. Saves cpu and memory. FossilOrigin-Name: c523f40895866e6fc979a26483dbea8206126b4bbdf4b73b77263c09e13c855e --- ext/fts5/fts5Int.h | 19 +- ext/fts5/fts5_expr.c | 6 +- ext/fts5/fts5_index.c | 553 ++++++++++++++++++++++++++--- ext/fts5/fts5_main.c | 7 +- ext/fts5/test/fts5origintext.test | 10 +- ext/fts5/test/fts5origintext2.test | 1 + ext/fts5/test/fts5origintext3.test | 9 + manifest | 24 +- manifest.uuid | 2 +- 9 files changed, 541 insertions(+), 90 deletions(-) diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 9d2622448d..cee9528858 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -385,17 +385,18 @@ struct Fts5IndexIter { /* ** Values used as part of the flags argument passed to IndexQuery(). */ -#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ -#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ -#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ -#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ +#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ +#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ +#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ +#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ -#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 -#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 -#define FTS5INDEX_QUERY_SKIPHASH 0x0040 +#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 +#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 +#define FTS5INDEX_QUERY_SKIPHASH 0x0040 +#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 /* ** Create/destroy an Fts5Index object. @@ -467,7 +468,7 @@ int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken() and xPhraseToken(). */ -int sqlite3Fts5IterToken(Fts5IndexIter*, int, int, const char**, int*); +int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); /* ** Insert or remove data to or from the index. Each time a document is @@ -548,7 +549,7 @@ int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); /* Used to populate hash tables for xInstToken in detail=none/column mode. */ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); int sqlite3Fts5IndexIterWriteTokendata( - Fts5IndexIter*, const char*, int, int iCol, int iOff + Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff ); /* diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 6589d1b3e6..89e7cbf364 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3003,6 +3003,7 @@ static int fts5ExprPopulatePoslistsCb( Fts5Expr *pExpr = p->pExpr; int i; int nQuery = nToken; + i64 iRowid = pExpr->pRoot->iRowid; UNUSED_PARAM2(iUnused1, iUnused2); @@ -3025,7 +3026,7 @@ static int fts5ExprPopulatePoslistsCb( int iCol = p->iOff>>32; int iTokOff = p->iOff & 0x7FFFFFFF; rc = sqlite3Fts5IndexIterWriteTokendata( - pT->pIter, pToken, nToken, iCol, iTokOff + pT->pIter, pToken, nToken, iRowid, iCol, iTokOff ); } if( rc ) return rc; @@ -3210,6 +3211,7 @@ int sqlite3Fts5ExprInstToken( ){ Fts5ExprPhrase *pPhrase = 0; Fts5IndexIter *pIter = 0; + i64 iRowid = pExpr->pRoot->iRowid; if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ return SQLITE_RANGE; @@ -3220,6 +3222,6 @@ int sqlite3Fts5ExprInstToken( } pIter = pPhrase->aTerm[iToken].pIter; - return sqlite3Fts5IterToken(pIter, iCol, iOff+iToken, ppOut, pnOut); + return sqlite3Fts5IterToken(pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut); } diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 4b7c8d3335..5f39a1e764 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -327,6 +327,8 @@ typedef struct Fts5TokenMapEntry Fts5TokenMapEntry; typedef struct Fts5TokenMapToken Fts5TokenMapToken; typedef struct Fts5TokenMap Fts5TokenMap; +typedef struct Fts5TokenDataIter Fts5TokenDataIter; + struct Fts5Data { u8 *p; /* Pointer to buffer containing record */ int nn; /* Size of record in bytes */ @@ -594,9 +596,16 @@ struct Fts5SegIter { ** poslist: ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. ** There is no way to tell if this is populated or not. +** +** pColset: +** If not NULL, points to an object containing a set of column indices. +** Only matches that occur in one of these columns will be returned. +** The Fts5Iter does not own the Fts5Colset object, and so it is not +** freed when the iterator is closed - it is owned by the upper layer. */ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ + Fts5TokenDataIter *pTokenDataIter; Fts5Index *pIndex; /* Index that owns this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ @@ -3780,6 +3789,28 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ } } +static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ + int iIter; + for(iIter=pIter->nSeg-1; iIter>0; iIter--){ + int iEq; + if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ + Fts5SegIter *pSeg = &pIter->aSeg[iEq]; + if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); + fts5MultiIterAdvanced(p, pIter, iEq, iIter); + } + } + fts5MultiIterSetEof(pIter); + fts5AssertMultiIterSetup(p, pIter); + + if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) + || fts5MultiIterIsDeleted(pIter) + ){ + fts5MultiIterNext(p, pIter, 0, 0); + }else if( pIter->base.bEof==0 ){ + Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; + pIter->xSetOutputs(pIter, pSeg); + } +} /* ** Allocate a new Fts5Iter object. @@ -3866,26 +3897,7 @@ static void fts5MultiIterNew( ** aFirst[] array. Or, if an error has occurred, free the iterator ** object and set the output variable to NULL. */ if( p->rc==SQLITE_OK ){ - for(iIter=pNew->nSeg-1; iIter>0; iIter--){ - int iEq; - if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ - Fts5SegIter *pSeg = &pNew->aSeg[iEq]; - if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); - fts5MultiIterAdvanced(p, pNew, iEq, iIter); - } - } - fts5MultiIterSetEof(pNew); - fts5AssertMultiIterSetup(p, pNew); - - if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew)) - || fts5MultiIterIsDeleted(pNew) - ){ - fts5MultiIterNext(p, pNew, 0, 0); - }else if( pNew->base.bEof==0 ){ - Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst]; - pNew->xSetOutputs(pNew, pSeg); - } - + fts5MultiIterFinishSetup(p, pNew); }else{ fts5MultiIterFree(pNew); *ppOut = 0; @@ -6718,6 +6730,431 @@ int sqlite3Fts5IndexWrite( return rc; } +/* +** pToken points to a buffer of size nToken bytes containing a search +** term, including the index number at the start, used on a tokendata=1 +** table. This function returns true if the term in buffer pBuf matches +** token pToken/nToken. +*/ +static int fts5IsTokendataPrefix( + Fts5Buffer *pBuf, + const u8 *pToken, + int nToken +){ + return ( + pBuf->n>=nToken + && 0==memcmp(pBuf->p, pToken, nToken) + && (pBuf->n==nToken || pBuf->p[nToken]==0x00) + ); +} + +/* +** Ensure the segment-iterator passed as the only argument points to EOF. +*/ +static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ + fts5DataRelease(pSeg->pLeaf); + pSeg->pLeaf = 0; +} + +typedef struct Fts5TokenDataMap Fts5TokenDataMap; +struct Fts5TokenDataMap { + i64 iRowid; + i64 iPos; + int iIter; +}; + +struct Fts5TokenDataIter { + int nIter; + int nIterAlloc; + + int nMap; + int nMapAlloc; + Fts5TokenDataMap *aMap; + + Fts5PoslistReader *aPoslistReader; + Fts5Iter *apIter[1]; +}; + +static Fts5TokenDataIter *fts5AppendTokendataIter( + Fts5Index *p, + Fts5TokenDataIter *pIn, + Fts5Iter *pAppend +){ + Fts5TokenDataIter *pRet = pIn; + + if( p->rc==SQLITE_OK ){ + if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ + int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; + int nByte = nAlloc * sizeof(Fts5Iter*); + Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); + + if( pNew==0 ){ + p->rc = SQLITE_NOMEM; + }else{ + if( pIn==0 ) memset(pNew, 0, nByte); + pRet = pNew; + pNew->nIterAlloc = nAlloc; + } + } + } + if( p->rc ){ + sqlite3Fts5IterClose((Fts5IndexIter*)pAppend); + }else{ + pRet->apIter[pRet->nIter++] = pAppend; + } + + return pRet; +} + +static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ + if( pSet ){ + int ii; + for(ii=0; iinIter; ii++){ + fts5MultiIterFree(pSet->apIter[ii]); + } + sqlite3_free(pSet->aPoslistReader); + sqlite3_free(pSet->aMap); + sqlite3_free(pSet); + } +} + +static int fts5TokendataIterToken( + Fts5Iter *pIter, + i64 iRowid, + int iCol, int iOff, + const char **ppOut, int *pnOut +){ + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + Fts5TokenDataMap *aMap = pT->aMap; + i64 iPos = (((i64)iCol)<<32) + iOff; + + int i1 = 0; + int i2 = pT->nMap; + int iTest = 0; + + while( i2>i1 ){ + iTest = (i1 + i2) / 2; + + if( aMap[iTest].iRowidiRowid ){ + i2 = iTest; + }else{ + if( aMap[iTest].iPosiPos ){ + i2 = iTest; + }else{ + break; + } + } + } + + if( i2>i1 ){ + Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; + *ppOut = (const char*)pMap->aSeg[0].term.p+1; + *pnOut = pMap->aSeg[0].term.n-1; + } + + return SQLITE_OK; +} + +static void fts5TokendataIterAppendMap( + Fts5Index *p, + Fts5TokenDataIter *pT, + int iIter, + i64 iRowid, + i64 iPos +){ + if( p->rc==SQLITE_OK ){ + if( pT->nMap==pT->nMapAlloc ){ + int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; + int nByte = nNew * sizeof(Fts5TokenDataMap); + Fts5TokenDataMap *aNew; + + aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); + if( aNew==0 ){ + p->rc = SQLITE_NOMEM; + return; + } + + pT->aMap = aNew; + pT->nMapAlloc = nNew; + } + + pT->aMap[pT->nMap].iRowid = iRowid; + pT->aMap[pT->nMap].iPos = iPos; + pT->aMap[pT->nMap].iIter = iIter; + pT->nMap++; + } +} + +static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ + int ii; + int nHit = 0; + i64 iRowid = SMALLEST_INT64; + int iMin = 0; + + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + + pIter->base.nData = 0; + pIter->base.pData = 0; + + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( p->base.bEof==0 ){ + if( nHit==0 || p->base.iRowidbase.iRowid; + nHit = 1; + pIter->base.pData = p->base.pData; + pIter->base.nData = p->base.nData; + iMin = ii; + }else if( p->base.iRowid==iRowid ){ + nHit++; + } + } + } + + if( nHit==0 ){ + pIter->base.bEof = 1; + }else{ + int eDetail = pIter->pIndex->pConfig->eDetail; + pIter->base.bEof = 0; + pIter->base.iRowid = iRowid; + + if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ + fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); + }else + if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ + int nReader = 0; + int nByte = 0; + i64 iPrev = 0; + + /* Allocate array of iterators if they are not already allocated. */ + if( pT->aPoslistReader==0 ){ + pT->aPoslistReader = sqlite3Fts5MallocZero( + &pIter->pIndex->rc, sizeof(Fts5PoslistReader) * pT->nIter + ); + if( pT->aPoslistReader==0 ) return; + } + + /* Populate an iterator for each poslist that will be merged */ + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( iRowid==p->base.iRowid ){ + sqlite3Fts5PoslistReaderInit( + p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] + ); + nByte += p->base.nData; + } + } + + /* Ensure the output buffer is large enough */ + if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){ + return; + } + + /* Ensure the token-mapping is large enough */ + if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ + int nNew = (pT->nMapAlloc + nByte) * 2; + Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( + pT->aMap, nNew*sizeof(Fts5TokenDataMap) + ); + if( aNew==0 ){ + pIter->pIndex->rc = SQLITE_NOMEM; + return; + } + pT->aMap = aNew; + pT->nMapAlloc = nNew; + } + + pIter->poslist.n = 0; + + while( 1 ){ + i64 iMinPos = LARGEST_INT64; + + /* Find smallest position */ + iMin = 0; + for(ii=0; iiaPoslistReader[ii]; + if( pReader->bEof==0 ){ + if( pReader->iPosiPos; + iMin = ii; + } + } + } + + /* If all readers were at EOF, break out of the loop. */ + if( iMinPos==LARGEST_INT64 ) break; + + sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); + sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); + + if( eDetail==FTS5_DETAIL_FULL ){ + pT->aMap[pT->nMap].iPos = iMinPos; + pT->aMap[pT->nMap].iIter = iMin; + pT->aMap[pT->nMap].iRowid = iRowid; + pT->nMap++; + } + } + + pIter->base.pData = pIter->poslist.p; + pIter->base.nData = pIter->poslist.n; + } + } +} + +static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ + int ii; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( p->base.bEof==0 + && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidpIndex, p, bFrom, iFrom); + while( bFrom && p->base.bEof==0 + && p->base.iRowidpIndex->rc==SQLITE_OK + ){ + fts5MultiIterNext(p->pIndex, p, 0, 0); + } + } + } + + fts5IterSetOutputsTokendata(pIter); +} + +static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ + if( pIter && pIter->aSeg[0].pLeaf==0 ){ + fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); + } +} + +static Fts5Iter *fts5SetupTokendataIter( + Fts5Index *p, /* FTS index to query */ + int bDesc, /* True for "ORDER BY rowid DESC" */ + const u8 *pToken, /* Buffer containing query term */ + int nToken, /* Size of buffer pToken in bytes */ + Fts5Colset *pColset /* Colset to filter on */ +){ + Fts5Iter *pRet = 0; + Fts5TokenDataIter *pSet = 0; + Fts5Structure *pStruct = 0; + const int flags = FTS5INDEX_QUERY_SKIPEMPTY | FTS5INDEX_QUERY_SCAN; + + assert( bDesc==0 ); + + Fts5Buffer bSeek = {0, 0, 0}; + Fts5Buffer *pSmall = 0; + + fts5IndexFlush(p); + pStruct = fts5StructureRead(p); + + while( 1 ){ + Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; + Fts5Iter *pNew = 0; + Fts5SegIter *pNewIter = 0; + Fts5SegIter *pPrevIter = 0; + + int iLvl, iSeg, ii; + + pNew = fts5MultiIterAlloc(p, pStruct->nSegment); + if( pNew==0 ) break; + + if( pSmall ){ + fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p); + fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0"); + }else{ + fts5BufferSet(&p->rc, &bSeek, nToken, pToken); + } + + pNewIter = &pNew->aSeg[0]; + pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); + + pNewIter++; + if( pPrevIter ){ + if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ + fts5SegIterSetEOF(pPrevIter); + } + pPrevIter++; + } + } + } + fts5TokendataSetTermIfEof(pPrev, pSmall); + + pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); + pNew->pColset = pColset; + fts5IterSetOutputCb(&p->rc, pNew); + + /* Loop through all segments in the new iterator. Find the smallest + ** term that any segment-iterator points to. Iterator pNew will be + ** used for this term. Also, set any iterator that points to a term that + ** does not match pToken/nToken to point to EOF */ + pSmall = 0; + for(ii=0; iinSeg; ii++){ + Fts5SegIter *pII = &pNew->aSeg[ii]; + if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ + fts5SegIterSetEOF(pII); + } + if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ + pSmall = &pII->term; + } + } + + /* If pSmall is still NULL at this point, then the new iterator does + ** not point to any terms that match the query. So delete it and break + ** out of the loop - all required iterators have been collected. */ + if( pSmall==0 ){ + sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + break; + } + + /* Append this iterator to the set and continue. */ + pSet = fts5AppendTokendataIter(p, pSet, pNew); + } + + if( p->rc==SQLITE_OK && pSet ){ + int ii; + for(ii=0; iinIter; ii++){ + Fts5Iter *pIter = pSet->apIter[ii]; + int iSeg; + for(iSeg=0; iSegnSeg; iSeg++){ + pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM; + } + fts5MultiIterFinishSetup(p, pIter); + } + } + + if( p->rc==SQLITE_OK ){ + pRet = fts5MultiIterAlloc(p, 0); + } + if( pRet ){ + pRet->pTokenDataIter = pSet; + if( pSet ){ + fts5IterSetOutputsTokendata(pRet); + }else{ + pRet->base.bEof = 1; + } + }else{ + fts5TokendataIterDelete(pSet); + } + + fts5StructureRelease(pStruct); + fts5BufferFree(&bSeek); + return pRet; +} + + /* ** Open a new iterator to iterate though all rowid that match the ** specified token or token prefix. @@ -6739,6 +7176,7 @@ int sqlite3Fts5IndexQuery( if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ + int bTokendata = (flags&FTS5INDEX_QUERY_NOTOKENDATA)?0:pConfig->bTokendata; if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* Figure out which index to search and set iIdx accordingly. If this @@ -6766,7 +7204,7 @@ int sqlite3Fts5IndexQuery( } } - if( iIdx<=pConfig->nPrefix && (pConfig->bTokendata==0 || iIdx!=0) ){ + if( iIdx<=pConfig->nPrefix && (bTokendata==0 || iIdx!=0) ){ /* Straight index lookup */ Fts5Structure *pStruct = fts5StructureRead(p); buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); @@ -6776,6 +7214,10 @@ int sqlite3Fts5IndexQuery( ); fts5StructureRelease(pStruct); } + }else if( bTokendata && iIdx==0 ){ + int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; + buf.p[0] = '0'; + pRet = fts5SetupTokendataIter(p, bDesc, buf.p, nToken+1, pColset); }else{ /* Scan multiple terms in the main index */ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; @@ -6816,7 +7258,11 @@ int sqlite3Fts5IndexQuery( int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; assert( pIter->pIndex->rc==SQLITE_OK ); - fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); + if( pIter->pTokenDataIter ){ + fts5TokendataIterNext(pIter, 0, 0); + }else{ + fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); + } return fts5IndexReturn(pIter->pIndex); } @@ -6849,7 +7295,11 @@ int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ */ int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); + if( pIter->pTokenDataIter ){ + fts5TokendataIterNext(pIter, 1, iMatch); + }else{ + fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); + } return fts5IndexReturn(pIter->pIndex); } @@ -6869,12 +7319,18 @@ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ */ int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, + i64 iRowid, int iCol, int iOff, const char **ppOut, int *pnOut ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5TokenMap *pMap = pIter->pTokenMap; + + if( pIter->pTokenDataIter ){ + return fts5TokendataIterToken(pIter, iRowid, iCol, iOff, ppOut, pnOut); + } + if( pMap ){ if( pMap->bHashed==0 ){ Fts5Index *p = pIter->pIndex; @@ -6899,53 +7355,29 @@ int sqlite3Fts5IterToken( void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL ); - if( pIter->pTokenMap ){ - pIter->pTokenMap->nEntry = 0; + if( pIter->pTokenDataIter ){ + pIter->pTokenDataIter->nMap = 0; } } int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter *pIndexIter, const char *pToken, int nToken, - int iCol, int iOff + i64 iRowid, int iCol, int iOff ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; Fts5Index *p = pIter->pIndex; + assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); - if( pIter->pTokenMap==0 ){ - pIter->pTokenMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); - } - if( p->rc==SQLITE_OK ){ - Fts5TokenMap *pMap = pIter->pTokenMap; + if( pT ){ int ii; - for(ii=0; iinToken; ii++){ - if( nToken==pMap->aToken[ii].nTerm - && 0==memcmp(pMap->aToken[ii].pTerm, pToken, nToken) - ){ - break; - } + for(ii=0; iinIter; ii++){ + Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; + if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; } - if( ii==pMap->nToken ){ - fts5TokenMapTerm(p, pMap, (const u8*)pToken, nToken); - } - if( pMap->nEntry>=pMap->nEntryAlloc ){ - int nNew = pMap->nEntryAlloc ? pMap->nEntryAlloc*2 : 32; - Fts5TokenMapEntry *aNew = (Fts5TokenMapEntry*)sqlite3_realloc( - pMap->aEntry, nNew * sizeof(Fts5TokenMapEntry) - ); - if( aNew==0 ){ - p->rc = SQLITE_NOMEM; - }else{ - pMap->aEntry = aNew; - pMap->nEntryAlloc = nNew; - } - } - if( p->rc==SQLITE_OK ){ - Fts5TokenMapEntry *pEntry = &pMap->aEntry[pMap->nEntry++]; - pEntry->iRowid = pIndexIter->iRowid; - pEntry->iCol = iCol; - pEntry->iOff = iOff; - pEntry->iTok = ii+1; + if( iinIter ){ + fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); } } return fts5IndexReturn(p); @@ -6958,6 +7390,7 @@ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5Index *pIndex = pIter->pIndex; + fts5TokendataIterDelete(pIter->pTokenDataIter); fts5MultiIterFree(pIter); sqlite3Fts5IndexCloseReader(pIndex); } @@ -7465,7 +7898,9 @@ static int fts5QueryCksum( int eDetail = p->pConfig->eDetail; u64 cksum = *pCksum; Fts5IndexIter *pIter = 0; - int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); + int rc = sqlite3Fts5IndexQuery( + p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter + ); while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){ i64 rowid = pIter->iRowid; diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 9f19b24b88..34050474f8 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -656,12 +656,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ } idxStr[iIdxStr] = '\0'; - /* Set idxFlags flags for the ORDER BY clause */ + /* Set idxFlags flags for the ORDER BY clause + ** + ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". + */ if( pInfo->nOrderBy==1 ){ int iSort = pInfo->aOrderBy[0].iColumn; if( iSort==(pConfig->nCol+1) && bSeenMatch ){ idxFlags |= FTS5_BI_ORDER_RANK; - }else if( iSort==-1 ){ + }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ idxFlags |= FTS5_BI_ORDER_ROWID; } if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 845e8145db..8273b3ca4d 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -141,7 +141,7 @@ do_execsql_test 3.1.1 { SELECT rowid FROM ft('hello') } 1 do_execsql_test 3.1.2 { SELECT rowid FROM ft('Hello') } 2 do_execsql_test 3.1.3 { SELECT rowid FROM ft('HELLO') } 3 -do_execsql_test 3.0 { +do_execsql_test 3.2 { CREATE VIRTUAL TABLE ft2 USING fts5(x, tokenize="origintext unicode61", tokendata=1, @@ -160,11 +160,11 @@ do_execsql_test 3.0 { #db func b b #execsql_pp { SELECT b(term) FROM vocab } -do_execsql_test 3.1.1 { SELECT rowid FROM ft2('hello') } {1 2 3} -do_execsql_test 3.1.2 { SELECT rowid FROM ft2('Hello') } {1 2 3} -do_execsql_test 3.1.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3} +do_execsql_test 3.3.1 { SELECT rowid FROM ft2('hello') } {1 2 3} +do_execsql_test 3.3.2 { SELECT rowid FROM ft2('Hello') } {1 2 3} +do_execsql_test 3.3.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3} -do_execsql_test 3.1.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10} +do_execsql_test 3.3.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10} #------------------------------------------------------------------------- # diff --git a/ext/fts5/test/fts5origintext2.test b/ext/fts5/test/fts5origintext2.test index 26f9864098..948db1c519 100644 --- a/ext/fts5/test/fts5origintext2.test +++ b/ext/fts5/test/fts5origintext2.test @@ -102,6 +102,7 @@ breakpoint do_execsql_test 1.11 { SELECT rowid FROM ft('hello'); } {1 2 3} do_execsql_test 1.12 { SELECT rowid FROM ft('today'); } {4 5 6} do_execsql_test 1.13 { SELECT rowid FROM ft('world'); } {7 8 9} +do_execsql_test 1.14 { SELECT rowid FROM ft('hello') ORDER BY rank; } {1 2 3} finish_test diff --git a/ext/fts5/test/fts5origintext3.test b/ext/fts5/test/fts5origintext3.test index 57b5984f4d..2b1e5c6387 100644 --- a/ext/fts5/test/fts5origintext3.test +++ b/ext/fts5/test/fts5origintext3.test @@ -46,6 +46,7 @@ foreach_detail_mode $testprefix { SELECT fts5_test_poslist(ft) FROM ft('hello'); } {{0.0.0 0.0.2 0.0.4}} +breakpoint do_execsql_test 1.3 { SELECT insttoken(ft, 0, 0), @@ -54,6 +55,14 @@ foreach_detail_mode $testprefix { FROM ft('hello'); } {hello.Hello hello.HELLO hello} + do_execsql_test 1.4 { + SELECT + insttoken(ft, 0, 0), + insttoken(ft, 1, 0), + insttoken(ft, 2, 0) + FROM ft('hello') ORDER BY rank; + } {hello.Hello hello.HELLO hello} + } finish_test diff --git a/manifest b/manifest index 13a7da6a20..04745fb5d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssigned\sinteger\soverflow\sin\sfts5. -D 2023-11-29T16:22:39.960 +C Different\sapproach\sto\squerying\sa\stokendata=1\stable.\sSaves\scpu\sand\smemory. +D 2023-12-01T20:09:59.031 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -90,14 +90,14 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h 5e5630fc81e212f658afaa5b2650dac939d2729d0723aef1eeaff908f1725648 -F ext/fts5/fts5Int.h 782151060d176be22861f57bf38e087a82cfb0dfc4b2fa6f9ccbc2641b6d01e3 +F ext/fts5/fts5Int.h 2dc73393460e5c5cab67adc7e32e1387cc225b57e05f629d490e65cddea1a8c5 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c 5d557c7ebefaeac5a5111cc47d4fee8a2fc6bc15245d5c99eebf53dd04bf794e +F ext/fts5/fts5_expr.c aac8026aedf56c9a6e32b31c89f9dd7e5548378457085093307d06be14f1a176 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c bafdef8be40a20bb86a131af4f09b56919f416fa13d1a86af1bf92bad9a2870d -F ext/fts5/fts5_main.c 55b53085dbd1693b5735463198a8d124dfbc27f08311c839637b44b8254ef7cb +F ext/fts5/fts5_index.c b6012920df8963245226bb536db7ddea62dfd7a860d6112887b175f7aaf55b82 +F ext/fts5/fts5_main.c 20596de592af135f68b9be875f0a28715f6562bbdedd215e1c89eac1b42e97f9 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -190,9 +190,9 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 7caef7634889bab8b44d145141c0d9325299398fb89b116bccd6262fde5659db -F ext/fts5/test/fts5origintext2.test 26482f4af1f2785cb01d06af9aae202289b6e8cf7b708d18aea305b459c2f302 -F ext/fts5/test/fts5origintext3.test 87a212b8235794348c56cb70f21e122d182a5af688c56057b90b7c151d0aa347 +F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0703aff5981625b527e62 +F ext/fts5/test/fts5origintext2.test 3259b331073fec918e02fd4d14d50586f9a3531da047a2a8f4624983eb654229 +F ext/fts5/test/fts5origintext3.test cb0f5835f8dff5954ee20570b68ee520cf04a08f6f9ca967b9d01d27e532da37 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2146,8 +2146,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 554fc13f2ca5f2ebd9ad0206034c25b556ff40db3106051c5e539f2e142e88ea -R 5c7c1632cd09d2b131adef895eab90a8 +P 60e46c7ec68fd8caaed960ca06d98fb06855b2d0bb860dd2fb7b5e89a5e9c7b4 +R e2c2c11ec4bab5e354a94b25a41ae3cd U dan -Z 009b023eba91b6acc35beb7d051a677a +Z f7d96142774ad25f02aad90982aa0fc8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index df2c2789ad..e669309199 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60e46c7ec68fd8caaed960ca06d98fb06855b2d0bb860dd2fb7b5e89a5e9c7b4 \ No newline at end of file +c523f40895866e6fc979a26483dbea8206126b4bbdf4b73b77263c09e13c855e \ No newline at end of file From f4c2962558e19b1b86734919a74c50856121d3d6 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 1 Dec 2023 20:37:11 +0000 Subject: [PATCH 289/347] Remove old code for tokendata=1 queries. FossilOrigin-Name: b0a489e8e1bf0290c2117ab32d78b1cc7d67bcb226b55ec044c8367ebde3815b --- ext/fts5/fts5_index.c | 423 +++++---------------------------- ext/fts5/test/fts5simple2.test | 4 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 67 insertions(+), 376 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 5f39a1e764..05f4c5e6aa 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -323,10 +323,6 @@ typedef struct Fts5SegWriter Fts5SegWriter; typedef struct Fts5Structure Fts5Structure; typedef struct Fts5StructureLevel Fts5StructureLevel; typedef struct Fts5StructureSegment Fts5StructureSegment; -typedef struct Fts5TokenMapEntry Fts5TokenMapEntry; -typedef struct Fts5TokenMapToken Fts5TokenMapToken; -typedef struct Fts5TokenMap Fts5TokenMap; - typedef struct Fts5TokenDataIter Fts5TokenDataIter; struct Fts5Data { @@ -370,7 +366,6 @@ struct Fts5Index { sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; - sqlite3_stmt *pIdxProbe; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; @@ -618,37 +613,11 @@ struct Fts5Iter { int bRev; /* True to iterate in reverse order */ u8 bSkipEmpty; /* True to skip deleted entries */ - Fts5TokenMap *pTokenMap; i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ Fts5CResult *aFirst; /* Current merge state (see above) */ Fts5SegIter aSeg[1]; /* Array of segment iterators */ }; -struct Fts5TokenMapEntry { - i64 iRowid; - u16 iCol; - int iOff; - int iTok; /* Offset into aToken[] + 1 */ -}; - -struct Fts5TokenMapToken { - u8 *pTerm; - int nTerm; -}; - -struct Fts5TokenMap { - int bHashed; /* True once hashed */ - - int nEntryAlloc; - int nEntry; - Fts5TokenMapEntry *aEntry; - - int nTokenAlloc; - int nToken; - Fts5TokenMapToken *aToken; -}; - - /* ** An instance of the following type is used to iterate through the contents ** of a doclist-index record. @@ -2641,18 +2610,6 @@ static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){ return p->pIdxSelect; } -static sqlite3_stmt *fts5IdxProbeStmt(Fts5Index *p){ - if( p->pIdxProbe==0 ){ - Fts5Config *pConfig = p->pConfig; - fts5IndexPrepareStmt(p, &p->pIdxProbe, sqlite3_mprintf( - "SELECT 1 FROM '%q'.'%q_idx' WHERE " - "segid=? AND term>? AND termzDb, pConfig->zName - )); - } - return p->pIdxProbe; -} - /* ** Initialize the object pIter to point to term pTerm/nTerm within segment ** pSeg. If there is no such term in the index, the iterator is set to EOF. @@ -3073,21 +3030,6 @@ static void fts5SegIterNextFrom( }while( p->rc==SQLITE_OK ); } -/* -** Free the Fts5TokenMap object passed as the only argument. -*/ -static void fts5TokenMapFree(Fts5TokenMap *pMap){ - if( pMap ){ - int ii; - for(ii=0; iinToken; ii++){ - sqlite3_free(pMap->aToken[ii].pTerm); - } - sqlite3_free(pMap->aToken); - sqlite3_free(pMap->aEntry); - sqlite3_free(pMap); - } -} - /* ** Free the iterator object passed as the second argument. */ @@ -3098,7 +3040,6 @@ static void fts5MultiIterFree(Fts5Iter *pIter){ fts5SegIterClear(&pIter->aSeg[i]); } fts5BufferFree(&pIter->poslist); - fts5TokenMapFree(pIter->pTokenMap); sqlite3_free(pIter); } } @@ -3915,7 +3856,6 @@ fts5MultiIterNew_post_check: static void fts5MultiIterNew2( Fts5Index *p, /* FTS5 backend to iterate within */ Fts5Data *pData, /* Doclist to iterate through */ - Fts5TokenMap *pMap, /* Token-map, if any */ int bDesc, /* True for descending rowid order */ Fts5Iter **ppOut /* New object */ ){ @@ -3923,8 +3863,6 @@ static void fts5MultiIterNew2( pNew = fts5MultiIterAlloc(p, 2); if( pNew ){ Fts5SegIter *pIter = &pNew->aSeg[1]; - pNew->pTokenMap = pMap; - pMap = 0; pIter->flags = FTS5_SEGITER_ONETERM; if( pData->szLeaf>0 ){ pIter->pLeaf = pData; @@ -3947,7 +3885,6 @@ static void fts5MultiIterNew2( *ppOut = pNew; } - fts5TokenMapFree(pMap); fts5DataRelease(pData); } @@ -6141,210 +6078,9 @@ static void fts5MergePrefixLists( *p1 = out; } -static u8 *fts5IdxBufferDup(Fts5Index *p, const u8 *pDup, int nDup){ - u8 *pRet = fts5IdxMalloc(p, nDup+1); - if( pRet ){ - memcpy(pRet, pDup, nDup); - } - return pRet; -} - -static void fts5TokenMapTerm( - Fts5Index *p, - Fts5TokenMap *pMap, - const u8 *pTerm, - int nTerm -){ - if( p->rc==SQLITE_OK ){ - Fts5TokenMapToken *pToken = 0; - if( pMap->nToken==pMap->nTokenAlloc ){ - i64 nNew = (pMap->nTokenAlloc ? pMap->nTokenAlloc * 2 : 32); - Fts5TokenMapToken *aNew = sqlite3_realloc64( - pMap->aToken, nNew*sizeof(Fts5TokenMapToken) - ); - if( aNew==0 ){ - p->rc = SQLITE_NOMEM; - return; - } - pMap->nTokenAlloc = nNew; - pMap->aToken = aNew; - } - pToken = &pMap->aToken[pMap->nToken++]; - pToken->nTerm = nTerm; - pToken->pTerm = fts5IdxBufferDup(p, pTerm, nTerm); - } -} - - -static void fts5TokenMapPoslist( - Fts5Index *p, - Fts5TokenMap *pMap, - Fts5Iter *p1 -){ - if( p->rc==SQLITE_OK ){ - const u8 *a = p1->base.pData; - i64 iPos = 0; - int iOff = 0; - - while( 0==sqlite3Fts5PoslistNext64(a, p1->base.nData, &iOff, &iPos) ){ - Fts5TokenMapEntry *pEntry = 0; - int iCol = FTS5_POS2COLUMN(iPos); - int iTokOff = FTS5_POS2OFFSET(iPos); - - if( pMap->nEntry==pMap->nEntryAlloc ){ - i64 nNew = (pMap->nEntryAlloc ? pMap->nEntryAlloc * 2 : 32); - Fts5TokenMapEntry *aNew = sqlite3_realloc64( - pMap->aEntry, nNew*sizeof(Fts5TokenMapEntry) - ); - if( aNew==0 ){ - p->rc = SQLITE_NOMEM; - return; - } - pMap->nEntryAlloc = nNew; - pMap->aEntry = aNew; - } - pEntry = &pMap->aEntry[pMap->nEntry++]; - pEntry->iRowid = p1->base.iRowid; - pEntry->iCol = iCol; - pEntry->iOff = iTokOff; - pEntry->iTok = pMap->nToken; - } - } -} - -static int fts5TokenMapHash(i64 iRowid, int iCol, int iOff){ - return (iRowid + (iRowid << 3) + (iCol << 6) + (iOff << 9)) & 0x7FFFFFFF; -} - -static void fts5TokenMapHashify(Fts5Index *p, Fts5TokenMap *pMap){ - int nHash = pMap->nEntry*2; - Fts5TokenMapEntry *aHash = 0; - - aHash = (Fts5TokenMapEntry*)fts5IdxMalloc(p, nHash*sizeof(Fts5TokenMapEntry)); - if( aHash ){ - int ii; - for(ii=0; iinEntry; ii++){ - Fts5TokenMapEntry *pEntry = &pMap->aEntry[ii]; - Fts5TokenMapEntry *pCopy = 0; - int iHash = fts5TokenMapHash(pEntry->iRowid, pEntry->iCol, pEntry->iOff); - - while( aHash[iHash % nHash].iTok ){ - iHash++; - } - pCopy = &aHash[iHash % nHash]; - memcpy(pCopy, pEntry, sizeof(Fts5TokenMapEntry)); - } - - sqlite3_free(pMap->aEntry); - pMap->aEntry = aHash; - pMap->nEntry = pMap->nEntryAlloc = nHash; - } -} - -static const u8 *fts5TokenMapLookup( - Fts5TokenMap *pMap, - i64 iRowid, - int iCol, - int iOff, - int *pnOut -){ - int iHash = fts5TokenMapHash(iRowid, iCol, iOff) % pMap->nEntry; - - for(; pMap->aEntry[iHash].iTok!=0; iHash = (iHash+1)%pMap->nEntry){ - Fts5TokenMapEntry *pEntry = &pMap->aEntry[iHash]; - if( pEntry->iRowid==iRowid && pEntry->iCol==iCol && pEntry->iOff==iOff ){ - *pnOut = pMap->aToken[pEntry->iTok-1].nTerm; - return pMap->aToken[pEntry->iTok-1].pTerm; - } - } - - *pnOut = 0; - return 0; -} - -/* -** The iterator passed as the second argument has been opened to scan and -** merge doclists for a series of tokens in tokendata=1 mode. This function -** tests whether or not, instead of using the cursor to read doclists to -** merge, it can be used directly by the upper layer. This is the case -** if the cursor currently points to the only token that corresponds to -** the queried term. i.e. if the next token that will be visited by the -** iterator does not match the query. -*/ -int fts5TokendataIterIsOk( - Fts5Index *p, - Fts5Iter *pIter, - const u8 *pToken, - int nToken -){ - int ii; - Fts5Buffer buf = {0, 0, 0}; - int bRet = 1; - Fts5Buffer *pTerm = 0; - - /* Iterator is not usable if it uses the hash table */ - if( pIter->aSeg[0].pSeg==0 ) return 0; - - for(ii=0; bRet && iinSeg; ii++){ - Fts5SegIter *pSeg = &pIter->aSeg[ii]; - Fts5Data *pLeaf = pSeg->pLeaf; - if( pLeaf ){ - - if( pTerm==0 ){ - pTerm = &pSeg->term; - }else{ - if( pSeg->term.n!=pTerm->n - || memcmp(pSeg->term.p, pTerm->p, pTerm->n) - ){ - bRet = 0; - break; - } - } - - if( pSeg->iEndofDoclistszLeaf ){ - /* Next term is on this node. Check it directly. */ - int nPrefix = 0; - fts5GetVarint32(&pLeaf->p[pSeg->iEndofDoclist], nPrefix); - if( nPrefix>=nToken ) bRet = 0; - }else{ - /* Next term is on a subsequent page. In this case query the %_idx - ** table to discover exactly what that next term is. */ - sqlite3_stmt *pProbe = fts5IdxProbeStmt(p); - if( pProbe ){ - int rc = SQLITE_OK; - if( buf.n==0 ){ - sqlite3Fts5BufferAppendBlob(&p->rc, &buf, nToken, pToken); - sqlite3Fts5BufferAppendBlob(&p->rc, &buf, 1, (const u8*)"\1"); - } - sqlite3_bind_int(pProbe, 1, pSeg->pSeg->iSegid); - sqlite3_bind_blob(pProbe,2, pSeg->term.p,pSeg->term.n, SQLITE_STATIC); - sqlite3_bind_blob(pProbe,3, buf.p, buf.n, SQLITE_STATIC); - - if( sqlite3_step(pProbe)==SQLITE_ROW ){ - bRet = 0; - } - rc = sqlite3_reset(pProbe); - if( p->rc==SQLITE_OK ) p->rc = rc; - } - } - } - } - - if( bRet ){ - for(ii=0; iinSeg; ii++){ - Fts5SegIter *pSeg = &pIter->aSeg[ii]; - pSeg->flags |= FTS5_SEGITER_ONETERM; - } - } - - fts5BufferFree(&buf); - return bRet; -} - static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bDesc, /* True for "ORDER BY rowid DESC" */ - int bTokenscan, int iIdx, /* Index to scan for data */ u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ @@ -6355,7 +6091,6 @@ static void fts5SetupPrefixIter( Fts5Buffer *aBuf; int nBuf = 32; int nMerge = 1; - Fts5TokenMap *pMap = 0; void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*); void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*); @@ -6369,8 +6104,6 @@ static void fts5SetupPrefixIter( xAppend = fts5AppendPoslist; } - assert( bTokenscan==0 || iIdx==0 ); - aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p); assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); @@ -6417,90 +6150,69 @@ static void fts5SetupPrefixIter( fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); - if( bDesc==0 && bTokenscan && fts5TokendataIterIsOk(p, p1, pToken,nToken) ){ - /* In this case iterator p1 may be used as is. */ - *ppIter = p1; - }else{ + for( /* no-op */ ; + fts5MultiIterEof(p, p1)==0; + fts5MultiIterNext2(p, p1, &bNewTerm) + ){ + Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; + int nTerm = pSeg->term.n; + const u8 *pTerm = pSeg->term.p; + p1->xSetOutputs(p1, pSeg); - if( iIdx==0 && p->pConfig->eDetail==FTS5_DETAIL_FULL && bTokenscan ){ - pMap = (Fts5TokenMap*)fts5IdxMalloc(p, sizeof(Fts5TokenMap)); + assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); + if( bNewTerm ){ + if( nTermrc!=SQLITE_OK || (aBuf && pStruct) ); - for( /* no-op */ ; - fts5MultiIterEof(p, p1)==0; - fts5MultiIterNext2(p, p1, &bNewTerm) - ){ - Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ]; - int nTerm = pSeg->term.n; - const u8 *pTerm = pSeg->term.p; - p1->xSetOutputs(p1, pSeg); - - assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); - if( bNewTerm ){ - if( nTermnToken && pTerm[nToken]!=0x00 ) break; - } - - if( pMap ){ - if( bNewTerm ){ - fts5TokenMapTerm(p, pMap, &pTerm[1], nTerm-1); + if( p1->base.nData==0 ) continue; + if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ + for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ + int i1 = i*nMerge; + int iStore; + assert( i1+nMerge<=nBuf ); + for(iStore=i1; iStorebase.nData==0 ) continue; - if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ - for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ - int i1 = i*nMerge; - int iStore; - assert( i1+nMerge<=nBuf ); + if( iStore==i1+nMerge ){ + xMerge(p, &doclist, nMerge, &aBuf[i1]); for(iStore=i1; iStorebase.iRowid-(u64)iLastRowid, p1, &doclist); - iLastRowid = p1->base.iRowid; + iLastRowid = 0; } - - assert( (nBuf%nMerge)==0 ); - for(i=0; irc==SQLITE_OK ){ - xMerge(p, &doclist, nMerge, &aBuf[i]); - } - for(iFree=i; iFreep = (u8*)&pData[1]; - pData->nn = pData->szLeaf = doclist.n; - if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); - fts5MultiIterNew2(p, pData, pMap, bDesc, ppIter); - pMap = 0; - } - fts5BufferFree(&doclist); + + xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist); + iLastRowid = p1->base.iRowid; } + + assert( (nBuf%nMerge)==0 ); + for(i=0; irc==SQLITE_OK ){ + xMerge(p, &doclist, nMerge, &aBuf[i]); + } + for(iFree=i; iFreep = (u8*)&pData[1]; + pData->nn = pData->szLeaf = doclist.n; + if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n); + fts5MultiIterNew2(p, pData, bDesc, ppIter); + } + fts5BufferFree(&doclist); } - fts5TokenMapFree(pMap); fts5StructureRelease(pStruct); sqlite3_free(aBuf); } @@ -6634,7 +6346,6 @@ int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); - sqlite3_finalize(p->pIdxProbe); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); @@ -7038,7 +6749,6 @@ static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ static Fts5Iter *fts5SetupTokendataIter( Fts5Index *p, /* FTS index to query */ - int bDesc, /* True for "ORDER BY rowid DESC" */ const u8 *pToken, /* Buffer containing query term */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset /* Colset to filter on */ @@ -7048,8 +6758,6 @@ static Fts5Iter *fts5SetupTokendataIter( Fts5Structure *pStruct = 0; const int flags = FTS5INDEX_QUERY_SKIPEMPTY | FTS5INDEX_QUERY_SCAN; - assert( bDesc==0 ); - Fts5Buffer bSeek = {0, 0, 0}; Fts5Buffer *pSmall = 0; @@ -7204,7 +6912,10 @@ int sqlite3Fts5IndexQuery( } } - if( iIdx<=pConfig->nPrefix && (bTokendata==0 || iIdx!=0) ){ + if( bTokendata && iIdx==0 ){ + buf.p[0] = '0'; + pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); + }else if( iIdx<=pConfig->nPrefix ){ /* Straight index lookup */ Fts5Structure *pStruct = fts5StructureRead(p); buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); @@ -7214,17 +6925,10 @@ int sqlite3Fts5IndexQuery( ); fts5StructureRelease(pStruct); } - }else if( bTokendata && iIdx==0 ){ - int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; - buf.p[0] = '0'; - pRet = fts5SetupTokendataIter(p, bDesc, buf.p, nToken+1, pColset); }else{ /* Scan multiple terms in the main index */ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; - int bTokenscan = (iIdx==0); - fts5SetupPrefixIter( - p, bDesc, bTokenscan, iPrefixIdx, buf.p, nToken+1, pColset, &pRet - ); + fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet); if( pRet==0 ){ assert( p->rc!=SQLITE_OK ); }else{ @@ -7325,26 +7029,11 @@ int sqlite3Fts5IterToken( const char **ppOut, int *pnOut ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - Fts5TokenMap *pMap = pIter->pTokenMap; if( pIter->pTokenDataIter ){ return fts5TokendataIterToken(pIter, iRowid, iCol, iOff, ppOut, pnOut); } - if( pMap ){ - if( pMap->bHashed==0 ){ - Fts5Index *p = pIter->pIndex; - fts5TokenMapHashify(p, pMap); - if( p->rc ){ - return fts5IndexReturn(p); - } - } - *ppOut = (const char*)fts5TokenMapLookup( - pIter->pTokenMap, pIndexIter->iRowid, iCol, iOff, pnOut - ); - }else{ - *ppOut = sqlite3Fts5IterTerm(pIndexIter, pnOut); - } return SQLITE_OK; } diff --git a/ext/fts5/test/fts5simple2.test b/ext/fts5/test/fts5simple2.test index e57cea70fa..6c0e0e1662 100644 --- a/ext/fts5/test/fts5simple2.test +++ b/ext/fts5/test/fts5simple2.test @@ -343,7 +343,9 @@ do_execsql_test 17.0 { INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); COMMIT; } -do_execsql_test 17.1 { SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 } +do_execsql_test 17.1 { + SELECT * FROM t2('y:a*') WHERE rowid BETWEEN 10 AND 20 +} do_execsql_test 17.2 { BEGIN; INSERT INTO t2 VALUES('a aa aaa', 'b bb bbb'); diff --git a/manifest b/manifest index 07814168f5..92cc5ca8d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\swith\sthis\sbranch. -D 2023-12-01T20:10:20.295 +C Remove\sold\scode\sfor\stokendata=1\squeries. +D 2023-12-01T20:37:11.688 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c aac8026aedf56c9a6e32b31c89f9dd7e5548378457085093307d06be14f1a176 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c b6012920df8963245226bb536db7ddea62dfd7a860d6112887b175f7aaf55b82 +F ext/fts5/fts5_index.c 2296bcd6736eaf093212892474619dfdb7ac1e262732e2b72cb528172c0b13d6 F ext/fts5/fts5_main.c 20596de592af135f68b9be875f0a28715f6562bbdedd215e1c89eac1b42e97f9 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -215,7 +215,7 @@ F ext/fts5/test/fts5secure7.test fd03d0868d64340a1db8615b02e5508fea409de13910114 F ext/fts5/test/fts5secure8.test eb3579e9d58b0acad97e8082dee1f99b2d393198f03500b453c2b25761c0c298 F ext/fts5/test/fts5securefault.test dbca2b6a1c16700017f5051138991b705410889933f2a37c57ae8a23b296b10b F ext/fts5/test/fts5simple.test a298670508c1458b88ce6030440f26a30673931884eb5f4094ac1773b3ba217b -F ext/fts5/test/fts5simple2.test 258a1b0c590409bfa5271e872c79572b319d2a56554d0585f68f146a0da603f0 +F ext/fts5/test/fts5simple2.test 8dd2389ee75e21a1429fe87e5f8c7d9a97ad1470304a8a2d3ba4b8c3c345fecd F ext/fts5/test/fts5simple3.test d5c74a9d3ca71bd5dd5cacb7c55b86ea12cdddfc8b1910e3de2995206898380f F ext/fts5/test/fts5synonym.test 1651815b8008de170e8e600dcacc17521d765482ea8f074ae82cfa870d8bb7fb F ext/fts5/test/fts5synonym2.test 8f891fc49cc1e8daed727051e77e1f42849c784a6a54bef82564761b2cb3e016 @@ -2148,8 +2148,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 c523f40895866e6fc979a26483dbea8206126b4bbdf4b73b77263c09e13c855e 883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62 -R 070accca4f04b7da788d8a587600de24 +P 8258967411d3ff212424b25fec79ded0d8ae83e773cd35a0bbf300c94923f25b +R d3ee6e0cbe8554b06da39b239b5142bc U dan -Z 0994bbca14fc53076151cd58ec1e7d74 +Z cbe01276ff2d44c618e4cc899051c453 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1398e08598..359f2cca45 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8258967411d3ff212424b25fec79ded0d8ae83e773cd35a0bbf300c94923f25b \ No newline at end of file +b0a489e8e1bf0290c2117ab32d78b1cc7d67bcb226b55ec044c8367ebde3815b \ No newline at end of file From 3af20cf3a0523ba8a8c7e966abc56eed218f4dd4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 1 Dec 2023 22:01:26 +0000 Subject: [PATCH 290/347] Performance optimization in the JSON parser. FossilOrigin-Name: 68d191f40e708962ec88e0c245b4496bc4a671300484b1cc0f3fc7e6d199a6e6 --- manifest | 13 ++++++------- manifest.uuid | 2 +- src/json.c | 6 ------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 05817d816e..6df01a3cfa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JSON\scache\sis\snow\smore\seffective. -D 2023-12-01T18:49:02.694 +C Performance\soptimization\sin\sthe\sJSON\sparser. +D 2023-12-01T22:01:26.348 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 3556e879386b0af1c542ed13ebb2f58c48d3b8b3ab13a96ab77ad2a9493cc715 +F src/json.c d524f2f13338f84327641edd5b422252d7c93a4f7fc57f083a0d41c767561554 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,9 +2145,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 25ed295f300fea6185104a73721076bccd2b2a6e411c78564266fa6dca4ff70c 1fdbc39521f63aedc6f08ecaafa54ea467b8c6316a692a18ad01eecbf22a0977 -R bed96169a434a08ba8168cff3e09a739 -T +closed 1fdbc39521f63aedc6f08ecaafa54ea467b8c6316a692a18ad01eecbf22a0977 +P 443a3f3a8e64d81cad8300a30e2cc57c4e39f69b5669ac8b550c590ae9f1134a +R a25493165248fa490fe7a3cb972ea0d2 U drh -Z 38a6916a5a09665f20ec73466c161c5c +Z 51cb7fe7cb06e4de05d17231c2159ec6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index caad112349..0d822e57d7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -443a3f3a8e64d81cad8300a30e2cc57c4e39f69b5669ac8b550c590ae9f1134a \ No newline at end of file +68d191f40e708962ec88e0c245b4496bc4a671300484b1cc0f3fc7e6d199a6e6 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 08e0f4fb63..3ae88be9d4 100644 --- a/src/json.c +++ b/src/json.c @@ -1366,7 +1366,6 @@ json_parse_restart: case '\'': { u8 opcode; char cDelim; - int nn; pParse->hasNonstd = 1; opcode = JSONB_TEXT; goto parse_string; @@ -1375,12 +1374,7 @@ json_parse_restart: opcode = JSONB_TEXT; parse_string: cDelim = z[i]; - nn = pParse->nJson; for(j=i+1; 1; j++){ - if( j>=nn ){ - pParse->iErr = j; - return -1; - } if( jsonIsOk[(unsigned char)z[j]] ) continue; c = z[j]; if( c==cDelim ){ From 5ec9c916ad97600b14871534137b0bc3bfd5bf49 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 01:06:33 +0000 Subject: [PATCH 291/347] Fix harmless compiler warnings and enhance performance the parser. FossilOrigin-Name: 285633da6d188547e52f07779e209c9e5f3dc33ce0668e14858f3337889ef4b8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 41 +++++++++++++++++++++++++++++------------ 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 6df01a3cfa..776e83a5e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimization\sin\sthe\sJSON\sparser. -D 2023-12-01T22:01:26.348 +C Fix\sharmless\scompiler\swarnings\sand\senhance\sperformance\sthe\sparser. +D 2023-12-02T01:06:33.225 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d524f2f13338f84327641edd5b422252d7c93a4f7fc57f083a0d41c767561554 +F src/json.c 58c3ded3bdc94125235a805214016147ecd63f8173cff19a7084c9fb0ea0ff9c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 443a3f3a8e64d81cad8300a30e2cc57c4e39f69b5669ac8b550c590ae9f1134a -R a25493165248fa490fe7a3cb972ea0d2 +P 68d191f40e708962ec88e0c245b4496bc4a671300484b1cc0f3fc7e6d199a6e6 +R 398de78fe5dea0e1b0bc6535ce40a9fc U drh -Z 51cb7fe7cb06e4de05d17231c2159ec6 +Z c1eeb98e2ab1260bdea70d2e8aff46bd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0d822e57d7..b7c120681c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -68d191f40e708962ec88e0c245b4496bc4a671300484b1cc0f3fc7e6d199a6e6 \ No newline at end of file +285633da6d188547e52f07779e209c9e5f3dc33ce0668e14858f3337889ef4b8 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 3ae88be9d4..6c86d98f2f 100644 --- a/src/json.c +++ b/src/json.c @@ -1075,6 +1075,17 @@ static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){ return 0; } +static void jsonBlobAppendNodeType(JsonParse*,u8,u32); +static SQLITE_NOINLINE void jsonBlobExpandAndAppendNodeType( + JsonParse *pParse, + u8 eType, + u32 szPayload +){ + if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; + jsonBlobAppendNodeType(pParse, eType, szPayload); +} + + /* Append an node type byte together with the payload size. */ static void jsonBlobAppendNodeType( @@ -1082,25 +1093,31 @@ static void jsonBlobAppendNodeType( u8 eType, u32 szPayload ){ - u8 a[5]; + u8 *a; + if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ + jsonBlobExpandAndAppendNodeType(pParse,eType,szPayload); + return; + } + a = &pParse->aBlob[pParse->nBlob]; if( szPayload<=11 ){ - jsonBlobAppendOneByte(pParse, eType | (szPayload<<4)); + a[0] = eType | (szPayload<<4); + pParse->nBlob += 1; }else if( szPayload<=0xff ){ a[0] = eType | 0xc0; a[1] = szPayload & 0xff; - jsonBlobAppendNBytes(pParse, a, 2); + pParse->nBlob += 2; }else if( szPayload<=0xffff ){ a[0] = eType | 0xd0; a[1] = (szPayload >> 8) & 0xff; a[2] = szPayload & 0xff; - jsonBlobAppendNBytes(pParse, a, 3); + pParse->nBlob += 3; }else{ a[0] = eType | 0xe0; a[1] = (szPayload >> 24) & 0xff; a[2] = (szPayload >> 16) & 0xff; a[3] = (szPayload >> 8) & 0xff; a[4] = szPayload & 0xff; - jsonBlobAppendNBytes(pParse, a, 5); + pParse->nBlob += 5; } } @@ -2169,12 +2186,9 @@ static u32 jsonLookupBlobStep( u32 nIns; /* Total bytes to insert (label+value) */ JsonParse v; /* BLOB encoding of the value to be inserted */ JsonParse ix; /* Header of the label to be inserted */ - u8 aLabel[16]; /* Buffer space for ix */ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); - ix.aBlob = aLabel; - ix.nBlobAlloc = sizeof(aLabel); jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); memset(&v, 0, sizeof(v)); if( zPath[i]==0 ){ @@ -2190,9 +2204,11 @@ static u32 jsonLookupBlobStep( if( JSON_BLOB_ISERROR(rc) || v.oom ){ pParse->oom |= v.oom; jsonParseReset(&v); + jsonParseReset(&ix); return rc; } } + pParse->oom |= ix.oom; if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); @@ -2204,6 +2220,7 @@ static u32 jsonLookupBlobStep( if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); } jsonParseReset(&v); + jsonParseReset(&ix); return j; } }else if( zPath[0]=='[' ){ @@ -3323,11 +3340,11 @@ static int jsonMergePatchBlob( u32 iTEndBE; /* Original first byte past end of target, before edit */ u32 iTEnd; /* Current first byte past end of target */ u8 eTLabel; /* Node type of the target label */ - u32 iTLabel; /* Index of the label */ - u32 nTLabel; /* Header size in bytes for the target label */ + u32 iTLabel = 0; /* Index of the label */ + u32 nTLabel = 0; /* Header size in bytes for the target label */ u32 szTLabel; /* Size of the target label payload */ - u32 iTValue; /* Index of the target value */ - u32 nTValue; /* Header size of the target value */ + u32 iTValue = 0; /* Index of the target value */ + u32 nTValue = 0; /* Header size of the target value */ u32 szTValue; /* Payload size for the target value */ u32 iPCursor; /* Cursor position while scanning the patch */ From 6df61985d4930d11d6f6fe65997b54f512e9aff5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 01:38:53 +0000 Subject: [PATCH 292/347] Unroll a loop in the parser for a performance increase. FossilOrigin-Name: a6dc29e4d5e13949e0fcd9d5dde575c2670eb10a230ab9df3806fc8c3016c540 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 13 +++++++++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 776e83a5e6..54838edff6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings\sand\senhance\sperformance\sthe\sparser. -D 2023-12-02T01:06:33.225 +C Unroll\sa\sloop\sin\sthe\sparser\sfor\sa\sperformance\sincrease. +D 2023-12-02T01:38:53.624 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 58c3ded3bdc94125235a805214016147ecd63f8173cff19a7084c9fb0ea0ff9c +F src/json.c 5363d3a9e6d4f8a77b735f279aedc0139f5ba4a1c6dff5052cc8b8f31012d9ef F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 68d191f40e708962ec88e0c245b4496bc4a671300484b1cc0f3fc7e6d199a6e6 -R 398de78fe5dea0e1b0bc6535ce40a9fc +P 285633da6d188547e52f07779e209c9e5f3dc33ce0668e14858f3337889ef4b8 +R 5a9d6175e86127f56f06090dd3d036f6 U drh -Z c1eeb98e2ab1260bdea70d2e8aff46bd +Z 9e063f5d4d8d31b6e06beb4f188d0a08 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b7c120681c..beaced137d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -285633da6d188547e52f07779e209c9e5f3dc33ce0668e14858f3337889ef4b8 \ No newline at end of file +a6dc29e4d5e13949e0fcd9d5dde575c2670eb10a230ab9df3806fc8c3016c540 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6c86d98f2f..62e3d652fd 100644 --- a/src/json.c +++ b/src/json.c @@ -1391,8 +1391,16 @@ json_parse_restart: opcode = JSONB_TEXT; parse_string: cDelim = z[i]; - for(j=i+1; 1; j++){ - if( jsonIsOk[(unsigned char)z[j]] ) continue; + j = i+1; + while( 1 /*exit-by-break*/ ){ + if( jsonIsOk[(u8)z[j]] ){ + if( jsonIsOk[(u8)z[j+1]] ){ + j += 2; + continue; + }else{ + j += 1; + } + } c = z[j]; if( c==cDelim ){ break; @@ -1421,6 +1429,7 @@ json_parse_restart: pParse->iErr = j; return -1; } + j++; } jsonBlobAppendNodeType(pParse, opcode, j-1-i); jsonBlobAppendNBytes(pParse, (const u8*)&z[i+1], j-1-i); From 768b6e32f699a2e76b21f1ef512d7c1811899bac Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 12:23:34 +0000 Subject: [PATCH 293/347] Remove a NEVER that can be true if a virtual table column is declared to have a DEFAULT. See [forum:/forumpost/3d4de8917627d058|forum post 3d4de8917627d058]. FossilOrigin-Name: 8abc2ccaf8106f20243568cd7fa74174386eb85d7ea381201e97e2fd527033e0 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/build.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index d6a02fa193..e9c843fd00 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\sbasic\sbatch-mode\sSQL\srunner\sfor\sthe\sSAH\sPool\sVFS,\sfor\suse\sin\scomparing\sit\sagainst\sWebSQL.\sBring\sthe\sWebSQL\sbatch\srunner\sup\sto\sdate,\snoting\sthat\sit\scannot\srun\swithout\saddition\sof\san\s"origin\strial"\sactivation\skey\sfrom\sGoogle\sbecause\sthat's\snow\sthe\sonly\sway\sto\senable\sWebSQL\sin\sChrome\s(that\spart\sis\snot\schecked\sin\sbecause\sthat\skey\sis\sprivate).\sMinor\scode-adjacent\scleanups. -D 2023-11-30T20:34:24.440 +C Remove\sa\sNEVER\sthat\scan\sbe\strue\sif\sa\svirtual\stable\scolumn\sis\sdeclared\sto\shave\na\sDEFAULT.\s\sSee\n[forum:/forumpost/3d4de8917627d058|forum\spost\s3d4de8917627d058]. +D 2023-12-02T12:23:34.118 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -672,7 +672,7 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c f3b09c5414de3a11db73e11e1d66f4c5e53c9e89876ff3b531a887ab656ca303 x F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240 F src/btreeInt.h 3e2589726c4f105e653461814f65857465da68be1fac688de340c43b873f4062 -F src/build.c 189e4517d67f09f0a3e0d8e1faa6e2ef0c2e95f6ac82e33c912cb7efa2a359cc +F src/build.c d0bb02989e9e652ee8a012c7780e32087c0dd99643d0cf609971029f78cb738a F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b @@ -2145,8 +2145,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 6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2 -R 0685d189d0b0c5921f2e4eda3787211e -U stephan -Z 5692f67161061876d9b3bcabcb3928cc +P 883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62 +R 9588fe6fe9170e1bad62d9178cc8ac2d +U drh +Z fe60aea9a6abab8faa2bce1247eb25bf # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 98d7aac12d..494cc91006 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62 \ No newline at end of file +8abc2ccaf8106f20243568cd7fa74174386eb85d7ea381201e97e2fd527033e0 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 3d06792358..14639f096e 100644 --- a/src/build.c +++ b/src/build.c @@ -720,7 +720,7 @@ void sqlite3ColumnSetExpr( */ Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ if( pCol->iDflt==0 ) return 0; - if( NEVER(!IsOrdinaryTable(pTab)) ) return 0; + if( !IsOrdinaryTable(pTab) ) return 0; if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; From 679c90850c7df5b81aa43e5af30737748ea06954 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 13:36:52 +0000 Subject: [PATCH 294/347] Simplification and optimization of the JSON parser. FossilOrigin-Name: f5ec9485119a2a6cb33eb864c7ca9b41d4a2ed08ab6ad9a6b0dd9358ab253576 --- manifest | 12 ++++----- manifest.uuid | 2 +- src/json.c | 75 +++++++++++++++++++++++++-------------------------- 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/manifest b/manifest index 54838edff6..07d971dc38 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Unroll\sa\sloop\sin\sthe\sparser\sfor\sa\sperformance\sincrease. -D 2023-12-02T01:38:53.624 +C Simplification\sand\soptimization\sof\sthe\sJSON\sparser. +D 2023-12-02T13:36:52.119 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 5363d3a9e6d4f8a77b735f279aedc0139f5ba4a1c6dff5052cc8b8f31012d9ef +F src/json.c 73558b9dca8f326045c42350bd8274c9a9a6489cdb145b2208a8fc7e50a04247 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 285633da6d188547e52f07779e209c9e5f3dc33ce0668e14858f3337889ef4b8 -R 5a9d6175e86127f56f06090dd3d036f6 +P a6dc29e4d5e13949e0fcd9d5dde575c2670eb10a230ab9df3806fc8c3016c540 +R d65ba1fc2a2f76e7b171a7dc72bb1070 U drh -Z 9e063f5d4d8d31b6e06beb4f188d0a08 +Z b0de3fe3c23565cae5077bfe62fcd8b8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index beaced137d..9855953161 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6dc29e4d5e13949e0fcd9d5dde575c2670eb10a230ab9df3806fc8c3016c540 \ No newline at end of file +f5ec9485119a2a6cb33eb864c7ca9b41d4a2ed08ab6ad9a6b0dd9358ab253576 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 62e3d652fd..af8f461d46 100644 --- a/src/json.c +++ b/src/json.c @@ -1064,38 +1064,39 @@ static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ return 0; } -/* Append bytes. Return 1 if an error occurs. +/* Slow version of jsonBlobAppendNode() that first resizes the +** pParse->aBlob structure. */ -static int jsonBlobAppendNBytes(JsonParse *pParse, const u8 *aData, u32 N){ - if( pParse->nBlob+N > pParse->nBlobAlloc ){ - return jsonBlobExpandAndAppend(pParse, aData, N); - } - memcpy(&pParse->aBlob[pParse->nBlob], aData, N); - pParse->nBlob += N; - return 0; -} - -static void jsonBlobAppendNodeType(JsonParse*,u8,u32); -static SQLITE_NOINLINE void jsonBlobExpandAndAppendNodeType( +static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); +static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( JsonParse *pParse, u8 eType, - u32 szPayload + u32 szPayload, + const void *aPayload ){ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; - jsonBlobAppendNodeType(pParse, eType, szPayload); + jsonBlobAppendNode(pParse, eType, szPayload, aPayload); } -/* Append an node type byte together with the payload size. +/* Append an node type byte together with the payload size and +** possibly also the payload. +** +** If aPayload is not NULL, then it is a pointer to the payload which +** is also appended. If aPayload is NULL, the pParse->aBlob[] array +** is resized (if necessary) so that it is big enough to hold the +** payload, but the payload is not appended and pParse->nBlob is left +** pointing to where the first byte of payload will eventually be. */ -static void jsonBlobAppendNodeType( - JsonParse *pParse, - u8 eType, - u32 szPayload +static void jsonBlobAppendNode( + JsonParse *pParse, /* The JsonParse object under construction */ + u8 eType, /* Node type. One of JSONB_* */ + u32 szPayload, /* Number of bytes of payload */ + const void *aPayload /* The payload. Might be NULL */ ){ u8 *a; if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ - jsonBlobExpandAndAppendNodeType(pParse,eType,szPayload); + jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); return; } a = &pParse->aBlob[pParse->nBlob]; @@ -1119,6 +1120,10 @@ static void jsonBlobAppendNodeType( a[4] = szPayload & 0xff; pParse->nBlob += 5; } + if( aPayload ){ + pParse->nBlob += szPayload; + memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload); + } } /* Change the payload size for the node at index i to be szPayload. @@ -1230,7 +1235,7 @@ json_parse_restart: case '{': { /* Parse object */ iThis = pParse->nBlob; - jsonBlobAppendNodeType(pParse, JSONB_OBJECT, (pParse->nJson-i)*2); + jsonBlobAppendNode(pParse, JSONB_OBJECT, (pParse->nJson-i)*2, 0); if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; @@ -1258,8 +1263,7 @@ json_parse_restart: k++; } assert( iBlob==pParse->nBlob ); - jsonBlobAppendNodeType(pParse, op, k-j); - jsonBlobAppendNBytes(pParse, (const u8*)&z[j], k-j); + jsonBlobAppendNode(pParse, op, k-j, &z[j]); pParse->hasNonstd = 1; x = k; }else{ @@ -1331,7 +1335,7 @@ json_parse_restart: case '[': { /* Parse array */ iThis = pParse->nBlob; - jsonBlobAppendNodeType(pParse, JSONB_ARRAY, pParse->nJson - i); + jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); iStart = pParse->nBlob; if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ @@ -1431,8 +1435,7 @@ json_parse_restart: } j++; } - jsonBlobAppendNodeType(pParse, opcode, j-1-i); - jsonBlobAppendNBytes(pParse, (const u8*)&z[i+1], j-1-i); + jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]); return j+1; } case 't': { @@ -1507,11 +1510,9 @@ json_parse_restart: ){ pParse->hasNonstd = 1; if( z[i]=='-' ){ - jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 6); - jsonBlobAppendNBytes(pParse, (const u8*)"-9e999", 6); + jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); }else{ - jsonBlobAppendNodeType(pParse, JSONB_FLOAT, 5); - jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); + jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); } return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); } @@ -1592,8 +1593,7 @@ json_parse_restart: assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); assert( JSONB_INT+0x02==JSONB_FLOAT ); if( z[i]=='+' ) i++; - jsonBlobAppendNodeType(pParse, JSONB_INT+t, j-i); - jsonBlobAppendNBytes(pParse, (const u8*)&z[i], j-i); + jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]); return j; } case '}': { @@ -1660,8 +1660,7 @@ json_parse_restart: } if( sqlite3Isalnum(z[i+nn]) ) continue; if( aNanInfName[k].eType==JSONB_FLOAT ){ - jsonBlobAppendOneByte(pParse, JSONB_FLOAT | 0x50); - jsonBlobAppendNBytes(pParse, (const u8*)"9e999", 5); + jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); }else{ jsonBlobAppendOneByte(pParse, JSONB_NULL); } @@ -2198,7 +2197,7 @@ static u32 jsonLookupBlobStep( testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); - jsonBlobAppendNodeType(&ix,JSONB_TEXTRAW, nKey); + jsonBlobAppendNode(&ix,JSONB_TEXTRAW, nKey, 0); memset(&v, 0, sizeof(v)); if( zPath[i]==0 ){ v.nBlob = pParse->nIns; @@ -2563,8 +2562,7 @@ static int jsonFunctionArgToBlob( return 1; } }else{ - jsonBlobAppendNodeType(pParse, JSONB_TEXTRAW, nJson); - jsonBlobAppendNBytes(pParse, (const u8*)zJson, nJson); + jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson); } break; } @@ -2574,8 +2572,7 @@ static int jsonFunctionArgToBlob( const char *z = (const char*)sqlite3_value_text(pArg); int e = eType==SQLITE_INTEGER ? JSONB_INT : JSONB_FLOAT; if( z==0 ) return 1; - jsonBlobAppendNodeType(pParse, e, n); - jsonBlobAppendNBytes(pParse, (const u8*)z, n); + jsonBlobAppendNode(pParse, e, n, z); break; } } From f0b8b16317a0449d00d45246a2d78598e8322448 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 14:16:47 +0000 Subject: [PATCH 295/347] Performance optimization in jsonAppendString(). FossilOrigin-Name: fdf00e96239c73fb67e2acecc5b95f55a1fc51c3deed4512613c0d6070ce5805 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 36 +++++++++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 07d971dc38..71a33c080b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplification\sand\soptimization\sof\sthe\sJSON\sparser. -D 2023-12-02T13:36:52.119 +C Performance\soptimization\sin\sjsonAppendString(). +D 2023-12-02T14:16:47.613 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 73558b9dca8f326045c42350bd8274c9a9a6489cdb145b2208a8fc7e50a04247 +F src/json.c a3eabd185cfd13250503ac8a17ac3ec61c9bb88730f99134de9df6c9a3446d46 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 a6dc29e4d5e13949e0fcd9d5dde575c2670eb10a230ab9df3806fc8c3016c540 -R d65ba1fc2a2f76e7b171a7dc72bb1070 +P f5ec9485119a2a6cb33eb864c7ca9b41d4a2ed08ab6ad9a6b0dd9358ab253576 +R a5df9ac2834f12883789ae2220af8e65 U drh -Z b0de3fe3c23565cae5077bfe62fcd8b8 +Z f68f81753669c00bcaaaadfbdd6d3188 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9855953161..eb14e17380 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f5ec9485119a2a6cb33eb864c7ca9b41d4a2ed08ab6ad9a6b0dd9358ab253576 \ No newline at end of file +fdf00e96239c73fb67e2acecc5b95f55a1fc51c3deed4512613c0d6070ce5805 \ No newline at end of file diff --git a/src/json.c b/src/json.c index af8f461d46..9d6554f415 100644 --- a/src/json.c +++ b/src/json.c @@ -595,17 +595,33 @@ static void jsonAppendSeparator(JsonString *p){ ** string. */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ - u32 i; - if( zIn==0 ) return; + u32 k; + u8 c; + const u8 *z = (const u8*)zIn; + if( z==0 ) return; if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; p->zBuf[p->nUsed++] = '"'; - for(i=0; izBuf[p->nUsed++] = c; - }else if( c=='"' || c=='\\' ){ + while( 1 /*exit-by-break*/ ){ + k = 0; + while( k=N ){ + if( k>0 ){ + memcpy(&p->zBuf[p->nUsed], z, k); + p->nUsed += k; + } + break; + } + if( k>0 ){ + memcpy(&p->zBuf[p->nUsed], z, k); + p->nUsed += k; + z += k; + N -= k; + } + c = z[0]; + if( c=='"' || c=='\\' ){ json_simple_escape: - if( (p->nUsed+N+3-i > p->nAlloc) && jsonStringGrow(p,N+3-i)!=0 ) return; + if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ @@ -626,7 +642,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ c = aSpecial[c]; goto json_simple_escape; } - if( (p->nUsed+N+7+i > p->nAlloc) && jsonStringGrow(p,N+7-i)!=0 ) return; + if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = 'u'; p->zBuf[p->nUsed++] = '0'; @@ -634,6 +650,8 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; } + z++; + N--; } p->zBuf[p->nUsed++] = '"'; assert( p->nUsednAlloc ); From 82136d90f868f01c4573b52802f6e2b00bb20f45 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 14:55:46 +0000 Subject: [PATCH 296/347] Minor fix to the header comment on jsonXlateTextToBlob(). FossilOrigin-Name: c3677ba410208c07b711f5f526eb5cf039a8eee49f632c7ae04fa55cdfbb9058 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 71a33c080b..ab5b5ba123 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimization\sin\sjsonAppendString(). -D 2023-12-02T14:16:47.613 +C Minor\sfix\sto\sthe\sheader\scomment\son\sjsonXlateTextToBlob(). +D 2023-12-02T14:55:46.474 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a3eabd185cfd13250503ac8a17ac3ec61c9bb88730f99134de9df6c9a3446d46 +F src/json.c 9ff2d40ed0d0199d88fa414f519a977bee38df27907702663b1cc5bc85274b05 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 f5ec9485119a2a6cb33eb864c7ca9b41d4a2ed08ab6ad9a6b0dd9358ab253576 -R a5df9ac2834f12883789ae2220af8e65 +P fdf00e96239c73fb67e2acecc5b95f55a1fc51c3deed4512613c0d6070ce5805 +R dbfc8fdcf920d7c2aea651d65adda878 U drh -Z f68f81753669c00bcaaaadfbdd6d3188 +Z ecc9fe8f5fc6f3c09b592aceda67d13d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index eb14e17380..f505eb9a85 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fdf00e96239c73fb67e2acecc5b95f55a1fc51c3deed4512613c0d6070ce5805 \ No newline at end of file +c3677ba410208c07b711f5f526eb5cf039a8eee49f632c7ae04fa55cdfbb9058 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9d6554f415..095ae870bf 100644 --- a/src/json.c +++ b/src/json.c @@ -1235,7 +1235,7 @@ static int jsonIs4HexB(const char *z, int *pOp){ ** or one of the following special result codes: ** ** 0 End of input -** -1 Syntax error +** -1 Syntax error or OOM ** -2 '}' seen \ ** -3 ']' seen \___ For these returns, pParse->iErr is set to ** -4 ',' seen / the index in zJson[] of the seen character From 4cd397c0d9b3ded36f9838ca04b88a74f40931ce Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 15:06:43 +0000 Subject: [PATCH 297/347] Fix potential unsigned integer underflow in jsonAppendString(). FossilOrigin-Name: d2fba2cbdc3870d34228c1a9446eced884325acc183900d7dd0b96132570fb4a --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ab5b5ba123..38a7efdf50 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sfix\sto\sthe\sheader\scomment\son\sjsonXlateTextToBlob(). -D 2023-12-02T14:55:46.474 +C Fix\spotential\sunsigned\sinteger\sunderflow\sin\sjsonAppendString(). +D 2023-12-02T15:06:43.574 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9ff2d40ed0d0199d88fa414f519a977bee38df27907702663b1cc5bc85274b05 +F src/json.c c3167c9737da0f3f45538c7599bbf3f090d1ba1e444e4e743935496f37cbddcc F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 fdf00e96239c73fb67e2acecc5b95f55a1fc51c3deed4512613c0d6070ce5805 -R dbfc8fdcf920d7c2aea651d65adda878 +P c3677ba410208c07b711f5f526eb5cf039a8eee49f632c7ae04fa55cdfbb9058 +R 13d01dca5d473a81616de1c5ea4e5269 U drh -Z ecc9fe8f5fc6f3c09b592aceda67d13d +Z bee957be31c329bcce231111ed4f35d1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f505eb9a85..0f4a0c9d1d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c3677ba410208c07b711f5f526eb5cf039a8eee49f632c7ae04fa55cdfbb9058 \ No newline at end of file +d2fba2cbdc3870d34228c1a9446eced884325acc183900d7dd0b96132570fb4a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 095ae870bf..83bf3e7337 100644 --- a/src/json.c +++ b/src/json.c @@ -603,7 +603,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ p->zBuf[p->nUsed++] = '"'; while( 1 /*exit-by-break*/ ){ k = 0; - while( k=N ){ if( k>0 ){ From 2c26bde4ff7e022c4d774ce9a4c27ca56ce65279 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 15:59:48 +0000 Subject: [PATCH 298/347] Do not allow a JsonParse object to be considered "editable" after an OOM. FossilOrigin-Name: c6bacf57bd6fe0fee00c9d41163a270b60997c20659949971bbf5c6c62622bfe --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 38a7efdf50..1affea972f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\spotential\sunsigned\sinteger\sunderflow\sin\sjsonAppendString(). -D 2023-12-02T15:06:43.574 +C Do\snot\sallow\sa\sJsonParse\sobject\sto\sbe\sconsidered\s"editable"\safter\san\sOOM. +D 2023-12-02T15:59:48.114 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c c3167c9737da0f3f45538c7599bbf3f090d1ba1e444e4e743935496f37cbddcc +F src/json.c 837599bcccc7b1533273d17697b33acb747f6a742e0789a716a1d991e83b76ea F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 c3677ba410208c07b711f5f526eb5cf039a8eee49f632c7ae04fa55cdfbb9058 -R 13d01dca5d473a81616de1c5ea4e5269 +P d2fba2cbdc3870d34228c1a9446eced884325acc183900d7dd0b96132570fb4a +R 4ccb83954df0ebdab5ce51302095cafb U drh -Z bee957be31c329bcce231111ed4f35d1 +Z 5295c6ae0590ca2382d336c94f2f32b0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0f4a0c9d1d..ba27b52b90 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d2fba2cbdc3870d34228c1a9446eced884325acc183900d7dd0b96132570fb4a \ No newline at end of file +c6bacf57bd6fe0fee00c9d41163a270b60997c20659949971bbf5c6c62622bfe \ No newline at end of file diff --git a/src/json.c b/src/json.c index 83bf3e7337..334829f9cf 100644 --- a/src/json.c +++ b/src/json.c @@ -1044,6 +1044,7 @@ static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ u8 *aOld; u32 nSize; assert( !pParse->bReadOnly ); + if( pParse->oom ) return 0; if( pParse->nBlobAlloc>0 ) return 1; aOld = pParse->aBlob; nSize = pParse->nBlob + nExtra; From 05db513435d21909be60b7dcf48574fdc74b8308 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 16:11:22 +0000 Subject: [PATCH 299/347] Protect a memcpy() against OOM conditions. FossilOrigin-Name: 26144d1c25ae0435db568009ba05e485d23d146f2b1f29f3a426c87860316aed --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 14 ++++++++------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 1affea972f..4e9b31f235 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\sa\sJsonParse\sobject\sto\sbe\sconsidered\s"editable"\safter\san\sOOM. -D 2023-12-02T15:59:48.114 +C Protect\sa\smemcpy()\sagainst\sOOM\sconditions. +D 2023-12-02T16:11:22.802 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 837599bcccc7b1533273d17697b33acb747f6a742e0789a716a1d991e83b76ea +F src/json.c 21ffece5a6e846480e6f108203d5ee3838261c0d8af773986895b1fd33fea593 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 d2fba2cbdc3870d34228c1a9446eced884325acc183900d7dd0b96132570fb4a -R 4ccb83954df0ebdab5ce51302095cafb +P c6bacf57bd6fe0fee00c9d41163a270b60997c20659949971bbf5c6c62622bfe +R 0ee4315c57b01c35ef946afcac5153f9 U drh -Z 5295c6ae0590ca2382d336c94f2f32b0 +Z 15254a8c6732e3004ca75d702e3d0bea # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ba27b52b90..115ae173de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c6bacf57bd6fe0fee00c9d41163a270b60997c20659949971bbf5c6c62622bfe \ No newline at end of file +26144d1c25ae0435db568009ba05e485d23d146f2b1f29f3a426c87860316aed \ No newline at end of file diff --git a/src/json.c b/src/json.c index 334829f9cf..cdc0d60c71 100644 --- a/src/json.c +++ b/src/json.c @@ -2239,12 +2239,14 @@ static u32 jsonLookupBlobStep( if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); - memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); - k = j + ix.nBlob; - memcpy(&pParse->aBlob[k], zKey, nKey); - k += nKey; - memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); - if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + if( !pParse->oom ){ + memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); + k = j + ix.nBlob; + memcpy(&pParse->aBlob[k], zKey, nKey); + k += nKey; + memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + } } jsonParseReset(&v); jsonParseReset(&ix); From c44041e03bc4d7ad0a5edbe8277a325eaaf5f5e6 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 2 Dec 2023 17:32:16 +0000 Subject: [PATCH 300/347] Ensure that tokendata=1 queries avoid loading large doclists for queries like "common AND uncommon", just as tokendata=0 queries do. FossilOrigin-Name: 7bda09ab404a110d57449e149a3281fca8dc4cacf7bd9832ea2a1356ad20fe8e --- ext/fts5/fts5Int.h | 2 + ext/fts5/fts5_expr.c | 25 ++++--- ext/fts5/fts5_index.c | 109 ++++++++++++++++++++++++++--- ext/fts5/fts5_main.c | 10 +++ ext/fts5/test/fts5origintext2.test | 1 - ext/fts5/test/fts5origintext3.test | 13 +++- ext/fts5/test/fts5origintext4.test | 66 +++++++++++++++++ manifest | 23 +++--- manifest.uuid | 2 +- 9 files changed, 217 insertions(+), 34 deletions(-) create mode 100644 ext/fts5/test/fts5origintext4.test diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index cee9528858..911f547d17 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -397,6 +397,7 @@ struct Fts5IndexIter { #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 #define FTS5INDEX_QUERY_SKIPHASH 0x0040 #define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 +#define FTS5INDEX_QUERY_SCANONETERM 0x0100 /* ** Create/destroy an Fts5Index object. @@ -786,6 +787,7 @@ int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); int sqlite3Fts5ExprInstToken(Fts5Expr*, int, int, int, int, const char**, int*); +void sqlite3Fts5ExprClearTokens(Fts5Expr*); /******************************************* ** The fts5_expr.c API above this point is used by the other hand-written diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 89e7cbf364..95d102062d 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3050,17 +3050,6 @@ int sqlite3Fts5ExprPopulatePoslists( sCtx.aPopulator = aPopulator; sCtx.iOff = (((i64)iCol) << 32) - 1; - /* If this is a tokendata=1 table, clear out the hash tables of - ** full-terms. */ - if( pConfig->bTokendata ){ - for(i=0; inPhrase; i++){ - Fts5ExprTerm *pT; - for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ - sqlite3Fts5IndexIterClearTokendata(pT->pIter); - } - } - } - for(i=0; inPhrase; i++){ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; Fts5Colset *pColset = pNode->pNear->pColset; @@ -3225,3 +3214,17 @@ int sqlite3Fts5ExprInstToken( return sqlite3Fts5IterToken(pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut); } +/* +** Clear the token mappings for all Fts5IndexIter objects mannaged by +** the expression passed as the only argument. +*/ +void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ + int ii; + for(ii=0; iinPhrase; ii++){ + Fts5ExprTerm *pT; + for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ + sqlite3Fts5IndexIterClearTokendata(pT->pIter); + } + } +} + diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 05f4c5e6aa..94b4767677 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -366,6 +366,7 @@ struct Fts5Index { sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; + sqlite3_stmt *pIdxNextSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; @@ -2660,7 +2661,7 @@ static void fts5SegIterSeekInit( fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); } - if( p->rc==SQLITE_OK && bGe==0 ){ + if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){ pIter->flags |= FTS5_SEGITER_ONETERM; if( pIter->pLeaf ){ if( flags & FTS5INDEX_QUERY_DESC ){ @@ -2693,6 +2694,79 @@ static void fts5SegIterSeekInit( ); } + +/* +** SQL used by fts5SegIterNextInit() to find the page to open. +*/ +static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ + if( p->pIdxNextSelect==0 ){ + Fts5Config *pConfig = p->pConfig; + fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf( + "SELECT pgno FROM '%q'.'%q_idx' WHERE " + "segid=? AND term>? ORDER BY term ASC LIMIT 1", + pConfig->zDb, pConfig->zName + )); + + } + return p->pIdxNextSelect; +} + +/* +** This is similar to fts5SegIterSeekInit(), except that it initializes +** the segment iterator to point to the first term following the page +** with pToken/nToken on it. +*/ +static void fts5SegIterNextInit( + Fts5Index *p, + const char *pTerm, int nTerm, + Fts5StructureSegment *pSeg, /* Description of segment */ + Fts5SegIter *pIter /* Object to populate */ +){ + int iPg = -1; /* Page of segment to open */ + int bDlidx = 0; + sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ + + pSel = fts5IdxNextStmt(p); + if( pSel ){ + assert( p->rc==SQLITE_OK ); + sqlite3_bind_int(pSel, 1, pSeg->iSegid); + sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC); + + if( sqlite3_step(pSel)==SQLITE_ROW ){ + i64 val = sqlite3_column_int64(pSel, 0); + iPg = (int)(val>>1); + bDlidx = (val & 0x0001); + } + p->rc = sqlite3_reset(pSel); + if( p->rc ) return; + } + + memset(pIter, 0, sizeof(*pIter)); + pIter->pSeg = pSeg; + pIter->flags |= FTS5_SEGITER_ONETERM; + if( iPg>=0 ){ + pIter->iLeafPgno = iPg - 1; + fts5SegIterNextPage(p, pIter); + fts5SegIterSetNext(p, pIter); + fts5SegIterAllocTombstone(p, pIter); + } + if( pIter->pLeaf ){ + const u8 *a = pIter->pLeaf->p; + int iTermOff = 0; + + pIter->iPgidxOff = pIter->pLeaf->szLeaf; + pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); + pIter->iLeafOffset = iTermOff; + fts5SegIterLoadTerm(p, pIter, 0); + fts5SegIterLoadNPos(p, pIter); + if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); + + assert( p->rc!=SQLITE_OK || + fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0 + ); + } +} + /* ** Initialize the object pIter to point to term pTerm/nTerm within the ** in-memory hash table. If there is no such term in the hash-table, the @@ -6346,6 +6420,7 @@ int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); + sqlite3_finalize(p->pIdxNextSelect); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); @@ -6496,7 +6571,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( if( p->rc==SQLITE_OK ){ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; - int nByte = nAlloc * sizeof(Fts5Iter*); + int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); if( pNew==0 ){ @@ -6513,6 +6588,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( }else{ pRet->apIter[pRet->nIter++] = pAppend; } + assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); return pRet; } @@ -6747,6 +6823,10 @@ static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ } } +/* +** This function sets up an iterator to use for a non-prefix query on a +** tokendata=1 table. +*/ static Fts5Iter *fts5SetupTokendataIter( Fts5Index *p, /* FTS index to query */ const u8 *pToken, /* Buffer containing query term */ @@ -6756,7 +6836,7 @@ static Fts5Iter *fts5SetupTokendataIter( Fts5Iter *pRet = 0; Fts5TokenDataIter *pSet = 0; Fts5Structure *pStruct = 0; - const int flags = FTS5INDEX_QUERY_SKIPEMPTY | FTS5INDEX_QUERY_SCAN; + const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN; Fts5Buffer bSeek = {0, 0, 0}; Fts5Buffer *pSmall = 0; @@ -6787,20 +6867,32 @@ static Fts5Iter *fts5SetupTokendataIter( for(iLvl=0; iLvlnLevel; iLvl++){ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; - fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); + int bDone = 0; - pNewIter++; if( pPrevIter ){ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ - fts5SegIterSetEOF(pPrevIter); + memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); + memset(pPrevIter, 0, sizeof(Fts5SegIter)); + bDone = 1; + }else if( pPrevIter->pLeaf + && pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf + ){ + fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); + bDone = 1; } - pPrevIter++; } + + if( bDone==0 ){ + fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); + } + + pNewIter++; + if( pPrevIter ) pPrevIter++; } } fts5TokendataSetTermIfEof(pPrev, pSmall); - pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); + pNew->bSkipEmpty = 1; pNew->pColset = pColset; fts5IterSetOutputCb(&p->rc, pNew); @@ -7043,7 +7135,6 @@ int sqlite3Fts5IterToken( */ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL ); if( pIter->pTokenDataIter ){ pIter->pTokenDataIter->nMap = 0; } diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 34050474f8..e911b0c0f9 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -916,6 +916,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ ); assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); + /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, + ** clear any token mappings accumulated at the fts5_index.c level. In + ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, + ** we need to retain the mappings for the entire query. */ + if( pCsr->ePlan==FTS5_PLAN_MATCH + && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata + ){ + sqlite3Fts5ExprClearTokens(pCsr->pExpr); + } + if( pCsr->ePlan<3 ){ int bSkip = 0; if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; diff --git a/ext/fts5/test/fts5origintext2.test b/ext/fts5/test/fts5origintext2.test index 948db1c519..a27309fe0c 100644 --- a/ext/fts5/test/fts5origintext2.test +++ b/ext/fts5/test/fts5origintext2.test @@ -98,7 +98,6 @@ do_execsql_test 1.10 { INSERT INTO ft VALUES('WORLD'); } -breakpoint do_execsql_test 1.11 { SELECT rowid FROM ft('hello'); } {1 2 3} do_execsql_test 1.12 { SELECT rowid FROM ft('today'); } {4 5 6} do_execsql_test 1.13 { SELECT rowid FROM ft('world'); } {7 8 9} diff --git a/ext/fts5/test/fts5origintext3.test b/ext/fts5/test/fts5origintext3.test index 2b1e5c6387..ac00bfabc0 100644 --- a/ext/fts5/test/fts5origintext3.test +++ b/ext/fts5/test/fts5origintext3.test @@ -46,7 +46,6 @@ foreach_detail_mode $testprefix { SELECT fts5_test_poslist(ft) FROM ft('hello'); } {{0.0.0 0.0.2 0.0.4}} -breakpoint do_execsql_test 1.3 { SELECT insttoken(ft, 0, 0), @@ -63,6 +62,18 @@ breakpoint FROM ft('hello') ORDER BY rank; } {hello.Hello hello.HELLO hello} + do_execsql_test 1.5 { + CREATE VIRTUAL TABLE ft2 USING fts5( + x, tokenize="origintext unicode61", tokendata=1, detail=%DETAIL% + ); + INSERT INTO ft2(rowid, x) VALUES(1, 'ONE one two three ONE'); + INSERT INTO ft2(rowid, x) VALUES(2, 'TWO one two three TWO'); + INSERT INTO ft2(rowid, x) VALUES(3, 'THREE one two three THREE'); + } + + do_execsql_test 1.6 { + SELECT insttoken(ft2, 0, 0), rowid FROM ft2('three') ORDER BY rank; + } {three.THREE 3 three 1 three 2} } finish_test diff --git a/ext/fts5/test/fts5origintext4.test b/ext/fts5/test/fts5origintext4.test new file mode 100644 index 0000000000..8973a24b05 --- /dev/null +++ b/ext/fts5/test/fts5origintext4.test @@ -0,0 +1,66 @@ +# 2023 November 22 +# +# 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. +# +#*********************************************************************** +# +# Tests focused on phrase queries. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5origintext4 + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +sqlite3_fts5_register_origintext db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", tokendata=1 + ); +} + +do_execsql_test 1.1 { + BEGIN; + INSERT INTO ft SELECT 'the first thing'; + + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<90000 + ) + INSERT INTO ft SELECT 'The second thing' FROM s; + + INSERT INTO ft SELECT 'the first thing'; + COMMIT; + INSERT INTO ft(ft) VALUES('optimize'); +} + +foreach {tn sql expr} { + 1 { SELECT rowid FROM ft('the') } {$mem > 250000} + 2 { SELECT rowid FROM ft('first') } {$mem < 50000} + 3 { SELECT rowid FROM ft('the first') } {$mem < 50000} +} { + db close + sqlite3 db test.db + sqlite3_fts5_register_origintext db + + execsql $sql + do_test 1.2.$tn { + set mem [lindex [sqlite3_db_status db CACHE_USED 0] 1] + expr $expr + } 1 +} + +proc b {x} { string map [list "\0" "."] $x } +db func b b +# execsql_pp { SELECT segid, b(term), pgno from ft_idx } + +finish_test + diff --git a/manifest b/manifest index 92cc5ca8d2..60fd22f769 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sold\scode\sfor\stokendata=1\squeries. -D 2023-12-01T20:37:11.688 +C Ensure\sthat\stokendata=1\squeries\savoid\sloading\slarge\sdoclists\sfor\squeries\slike\s"common\sAND\suncommon",\sjust\sas\stokendata=0\squeries\sdo. +D 2023-12-02T17:32:16.568 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -90,14 +90,14 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h 5e5630fc81e212f658afaa5b2650dac939d2729d0723aef1eeaff908f1725648 -F ext/fts5/fts5Int.h 2dc73393460e5c5cab67adc7e32e1387cc225b57e05f629d490e65cddea1a8c5 +F ext/fts5/fts5Int.h 285118aa6dfccb382e84eaeb9f7bec334e4f7104efa9303240605447003445c9 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c aac8026aedf56c9a6e32b31c89f9dd7e5548378457085093307d06be14f1a176 +F ext/fts5/fts5_expr.c f83259b52b7b3e76768b835fe155cb7e345affdfafb96574372b4127d5f5496a F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 2296bcd6736eaf093212892474619dfdb7ac1e262732e2b72cb528172c0b13d6 -F ext/fts5/fts5_main.c 20596de592af135f68b9be875f0a28715f6562bbdedd215e1c89eac1b42e97f9 +F ext/fts5/fts5_index.c a02b6ff2d391dd9c2119f437eba1e8af5ac4b2f1798c7c39a93d73de95ad2337 +F ext/fts5/fts5_main.c 075995302198fe6f591fdbbedd415dfac564a9bfc20aea81e6fa0503b2d94af0 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -191,8 +191,9 @@ F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0703aff5981625b527e62 -F ext/fts5/test/fts5origintext2.test 3259b331073fec918e02fd4d14d50586f9a3531da047a2a8f4624983eb654229 -F ext/fts5/test/fts5origintext3.test cb0f5835f8dff5954ee20570b68ee520cf04a08f6f9ca967b9d01d27e532da37 +F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 +F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a +F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2148,8 +2149,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 8258967411d3ff212424b25fec79ded0d8ae83e773cd35a0bbf300c94923f25b -R d3ee6e0cbe8554b06da39b239b5142bc +P b0a489e8e1bf0290c2117ab32d78b1cc7d67bcb226b55ec044c8367ebde3815b +R aa740197e8da931b3ffaf4ddf853a1ed U dan -Z cbe01276ff2d44c618e4cc899051c453 +Z 62655ccf950056e7477fcaff4611778d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 359f2cca45..7fc4017fab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b0a489e8e1bf0290c2117ab32d78b1cc7d67bcb226b55ec044c8367ebde3815b \ No newline at end of file +7bda09ab404a110d57449e149a3281fca8dc4cacf7bd9832ea2a1356ad20fe8e \ No newline at end of file From a11aaff05ae0f9b43f4b717e0b3158c1e36e1c10 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 18:04:27 +0000 Subject: [PATCH 301/347] Take extra care to ensure that JSONB values that are in cache are actually owned by the JSON subsystem, and that ownership of such values is not handed back to the bytecode engine. FossilOrigin-Name: 1304534001e9ef66c6b12752b69d790bfa3427cc803f87cc48ca22ae12df0fdf --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 4e9b31f235..87767f5859 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Protect\sa\smemcpy()\sagainst\sOOM\sconditions. -D 2023-12-02T16:11:22.802 +C Take\sextra\scare\sto\sensure\sthat\sJSONB\svalues\sthat\sare\sin\scache\sare\sactually\nowned\sby\sthe\sJSON\ssubsystem,\sand\sthat\sownership\sof\ssuch\svalues\sis\snot\shanded\nback\sto\sthe\sbytecode\sengine. +D 2023-12-02T18:04:27.395 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 21ffece5a6e846480e6f108203d5ee3838261c0d8af773986895b1fd33fea593 +F src/json.c 4c6b5c0c731fe7a2b2d28467af747c4744370bd47b5f9d6b7531efb8617eda37 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 c6bacf57bd6fe0fee00c9d41163a270b60997c20659949971bbf5c6c62622bfe -R 0ee4315c57b01c35ef946afcac5153f9 +P 26144d1c25ae0435db568009ba05e485d23d146f2b1f29f3a426c87860316aed +R 4e2a070d847085a145ada5a2fea29659 U drh -Z 15254a8c6732e3004ca75d702e3d0bea +Z 2cced16db85959c5b0bfa29c3e3e2f71 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 115ae173de..18c274186a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -26144d1c25ae0435db568009ba05e485d23d146f2b1f29f3a426c87860316aed \ No newline at end of file +1304534001e9ef66c6b12752b69d790bfa3427cc803f87cc48ca22ae12df0fdf \ No newline at end of file diff --git a/src/json.c b/src/json.c index cdc0d60c71..586371d9d4 100644 --- a/src/json.c +++ b/src/json.c @@ -375,6 +375,7 @@ static int jsonCacheInsert( memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); p->nUsed = JSON_CACHE_SIZE-1; } + assert( pParse->nBlobAlloc>0 ); pParse->eEdit = 0; pParse->nJPRef++; pParse->bReadOnly = 1; @@ -731,7 +732,7 @@ static void jsonReturnString( sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); }else if( jsonForceRCStr(p) ){ - if( pParse && pParse->bJsonIsRCStr==0 ){ + if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ int rc; pParse->zJson = sqlite3RCStrRef(p->zBuf); pParse->nJson = p->nUsed; @@ -1751,6 +1752,8 @@ static void jsonReturnStringAsBlob(JsonString *pStr){ sqlite3_free(px.aBlob); sqlite3_result_error_nomem(pStr->pCtx); }else{ + assert( px.nBlobAlloc>0 ); + assert( !px.bReadOnly ); sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, sqlite3_free); } } @@ -2841,9 +2844,12 @@ static void jsonReturnParse( } flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( flgs & JSON_BLOB ){ - sqlite3_result_blob(ctx, p->aBlob, p->nBlob, - p->nBlobAlloc>0 ? SQLITE_DYNAMIC : SQLITE_TRANSIENT); - p->nBlobAlloc = 0; + if( p->nBlobAlloc>0 && !p->bReadOnly ){ + sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC); + p->nBlobAlloc = 0; + }else{ + sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT); + } }else{ JsonString s; jsonStringInit(&s, ctx); @@ -3063,6 +3069,8 @@ static void jsonbFunc( if( jsonConvertTextToBlob(pParse, ctx) ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ + assert( pParse->nBlobAlloc>0 ); + assert( !pParse->bReadOnly ); sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); pParse->aBlob = 0; pParse->nBlob = 0; From 94c521295aa898eb07dcfc4cf4ccdeb04ff7d735 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 2 Dec 2023 18:14:07 +0000 Subject: [PATCH 302/347] When tokendata=1 queries require multiple segment-cursors, allow those cursors to share a single array of in-memory tombstone pages. FossilOrigin-Name: e0175d07e4094db5ea4b0378a5ff480dafb6ba9da86a113fa767c4c89c3c866f --- ext/fts5/fts5_index.c | 65 ++++++++++++++++++++++++++++++++----------- manifest | 12 ++++---- manifest.uuid | 2 +- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 94b4767677..9edf184c71 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -324,6 +324,7 @@ typedef struct Fts5Structure Fts5Structure; typedef struct Fts5StructureLevel Fts5StructureLevel; typedef struct Fts5StructureSegment Fts5StructureSegment; typedef struct Fts5TokenDataIter Fts5TokenDataIter; +typedef struct Fts5TombstoneArray Fts5TombstoneArray; struct Fts5Data { u8 *p; /* Pointer to buffer containing record */ @@ -520,8 +521,7 @@ struct Fts5SegIter { Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ i64 iLeafOffset; /* Byte offset within current leaf */ - Fts5Data **apTombstone; /* Array of tombstone pages */ - int nTombstone; + Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -548,6 +548,12 @@ struct Fts5SegIter { u8 bDel; /* True if the delete flag is set */ }; +struct Fts5TombstoneArray { + int nRef; /* Number of pointers to this object */ + int nTombstone; + Fts5Data *apTombstone[1]; /* Array of tombstone pages */ +}; + /* ** Argument is a pointer to an Fts5Data structure that contains a ** leaf page. @@ -1924,11 +1930,13 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ const int nTomb = pIter->pSeg->nPgTombstone; if( nTomb>0 ){ - Fts5Data **apTomb = 0; - apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb); - if( apTomb ){ - pIter->apTombstone = apTomb; - pIter->nTombstone = nTomb; + int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); + Fts5TombstoneArray *pNew; + pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); + if( pNew ){ + pNew->nTombstone = nTomb; + pNew->nRef = 1; + pIter->pTombArray = pNew; } } } @@ -2677,7 +2685,9 @@ static void fts5SegIterSeekInit( } fts5SegIterSetNext(p, pIter); - fts5SegIterAllocTombstone(p, pIter); + if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){ + fts5SegIterAllocTombstone(p, pIter); + } /* Either: ** @@ -2852,6 +2862,19 @@ static void fts5IndexFreeArray(Fts5Data **ap, int n){ } } +static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){ + if( p ){ + p->nRef--; + if( p->nRef<=0 ){ + int ii; + for(ii=0; iinTombstone; ii++){ + fts5DataRelease(p->apTombstone[ii]); + } + sqlite3_free(p); + } + } +} + /* ** Zero the iterator passed as the only argument. */ @@ -2859,7 +2882,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){ fts5BufferFree(&pIter->term); fts5DataRelease(pIter->pLeaf); fts5DataRelease(pIter->pNextLeaf); - fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone); + fts5TombstoneArrayDelete(pIter->pTombArray); fts5DlidxIterFree(pIter->pDlidx); sqlite3_free(pIter->aRowidOffset); memset(pIter, 0, sizeof(Fts5SegIter)); @@ -3248,24 +3271,25 @@ static int fts5IndexTombstoneQuery( static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ int iFirst = pIter->aFirst[1].iFirst; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; + Fts5TombstoneArray *pArray = pSeg->pTombArray; - if( pSeg->pLeaf && pSeg->nTombstone ){ + if( pSeg->pLeaf && pArray ){ /* Figure out which page the rowid might be present on. */ - int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone; + int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; assert( iPg>=0 ); /* If tombstone hash page iPg has not yet been loaded from the ** database, load it now. */ - if( pSeg->apTombstone[iPg]==0 ){ - pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex, + if( pArray->apTombstone[iPg]==0 ){ + pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) ); - if( pSeg->apTombstone[iPg]==0 ) return 0; + if( pArray->apTombstone[iPg]==0 ) return 0; } return fts5IndexTombstoneQuery( - pSeg->apTombstone[iPg], - pSeg->nTombstone, + pArray->apTombstone[iPg], + pArray->nTombstone, pSeg->iRowid ); } @@ -6886,6 +6910,15 @@ static Fts5Iter *fts5SetupTokendataIter( fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); } + if( pPrevIter ){ + if( pPrevIter->pTombArray ){ + pNewIter->pTombArray = pPrevIter->pTombArray; + pNewIter->pTombArray->nRef++; + } + }else{ + fts5SegIterAllocTombstone(p, pNewIter); + } + pNewIter++; if( pPrevIter ) pPrevIter++; } diff --git a/manifest b/manifest index 60fd22f769..699d024ca1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sthat\stokendata=1\squeries\savoid\sloading\slarge\sdoclists\sfor\squeries\slike\s"common\sAND\suncommon",\sjust\sas\stokendata=0\squeries\sdo. -D 2023-12-02T17:32:16.568 +C When\stokendata=1\squeries\srequire\smultiple\ssegment-cursors,\sallow\sthose\scursors\sto\sshare\sa\ssingle\sarray\sof\sin-memory\stombstone\spages. +D 2023-12-02T18:14:07.393 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c f83259b52b7b3e76768b835fe155cb7e345affdfafb96574372b4127d5f5496a F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c a02b6ff2d391dd9c2119f437eba1e8af5ac4b2f1798c7c39a93d73de95ad2337 +F ext/fts5/fts5_index.c 21f8f449666ac44c12d5051e153ad84a886a729cb2f5d6ad02a113095c3f8ec6 F ext/fts5/fts5_main.c 075995302198fe6f591fdbbedd415dfac564a9bfc20aea81e6fa0503b2d94af0 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -2149,8 +2149,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 b0a489e8e1bf0290c2117ab32d78b1cc7d67bcb226b55ec044c8367ebde3815b -R aa740197e8da931b3ffaf4ddf853a1ed +P 7bda09ab404a110d57449e149a3281fca8dc4cacf7bd9832ea2a1356ad20fe8e +R 1ce6343b4aa590c869e9b9aa51095415 U dan -Z 62655ccf950056e7477fcaff4611778d +Z 8664660cde606ab1a6fdc20b18622c4a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7fc4017fab..0cbd084480 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7bda09ab404a110d57449e149a3281fca8dc4cacf7bd9832ea2a1356ad20fe8e \ No newline at end of file +e0175d07e4094db5ea4b0378a5ff480dafb6ba9da86a113fa767c4c89c3c866f \ No newline at end of file From 53c2160db04921c76e62c6eef3329cbe637b8984 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 18:17:38 +0000 Subject: [PATCH 303/347] Fix harmless compiler warnings. Refactor some identifier names for clearer presentation. FossilOrigin-Name: 7e3941502789c5afaf19b08112f464abf5e3cba7f92fc9290af2a0f96127ad9a --- manifest | 12 ++--- manifest.uuid | 2 +- src/json.c | 124 ++++++++++++++++++++++++++------------------------ 3 files changed, 71 insertions(+), 67 deletions(-) diff --git a/manifest b/manifest index 87767f5859..6f7377cfb8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Take\sextra\scare\sto\sensure\sthat\sJSONB\svalues\sthat\sare\sin\scache\sare\sactually\nowned\sby\sthe\sJSON\ssubsystem,\sand\sthat\sownership\sof\ssuch\svalues\sis\snot\shanded\nback\sto\sthe\sbytecode\sengine. -D 2023-12-02T18:04:27.395 +C Fix\sharmless\scompiler\swarnings.\s\sRefactor\ssome\sidentifier\snames\sfor\nclearer\spresentation. +D 2023-12-02T18:17:38.516 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4c6b5c0c731fe7a2b2d28467af747c4744370bd47b5f9d6b7531efb8617eda37 +F src/json.c b67328611c8ebc5aca49e2d73b1bad9962948aa5c952f3d96c44eb74ee3d0010 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 26144d1c25ae0435db568009ba05e485d23d146f2b1f29f3a426c87860316aed -R 4e2a070d847085a145ada5a2fea29659 +P 1304534001e9ef66c6b12752b69d790bfa3427cc803f87cc48ca22ae12df0fdf +R 1b492fad564bbccdff370d30a804d641 U drh -Z 2cced16db85959c5b0bfa29c3e3e2f71 +Z f1274565e5b6e1f5da018a4ab14dfe40 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 18c274186a..05b1875e6c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1304534001e9ef66c6b12752b69d790bfa3427cc803f87cc48ca22ae12df0fdf \ No newline at end of file +7e3941502789c5afaf19b08112f464abf5e3cba7f92fc9290af2a0f96127ad9a \ No newline at end of file diff --git a/src/json.c b/src/json.c index 586371d9d4..064d9457ce 100644 --- a/src/json.c +++ b/src/json.c @@ -287,7 +287,7 @@ struct JsonParse { u8 bReadOnly; /* Do not modify. */ u32 nJPRef; /* Number of references to this object */ u32 iErr; /* Error location in zJson[] */ - /* Search and edit information. See jsonLookupBlobStep() */ + /* Search and edit information. See jsonLookupStep() */ u8 eEdit; /* Edit operation to apply */ int delta; /* Size change due to the edit */ u32 nIns; /* Number of bytes to insert */ @@ -2119,20 +2119,24 @@ static void jsonBlobEdit( } /* -** Error returns from jsonLookupBlobStep() +** Error returns from jsonLookupStep() */ -#define JSON_BLOB_ERROR 0xffffffff -#define JSON_BLOB_NOTFOUND 0xfffffffe -#define JSON_BLOB_PATHERROR 0xfffffffd -#define JSON_BLOB_ISERROR(x) ((x)>=JSON_BLOB_PATHERROR) +#define JSON_LOOKUP_ERROR 0xffffffff +#define JSON_LOOKUP_NOTFOUND 0xfffffffe +#define JSON_LOOKUP_PATHERROR 0xfffffffd +#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) /* ** Search along zPath to find the Json element specified. Return an ** index into pParse->aBlob[] for the start of that element's value. ** -** Return JSON_BLOB_NOTFOUND if no such element exists. +** If the value found by this routine is the value half of label/value pair +** within an object, then set pPath->iLabel to the start of the corresponding +** label, before returning. +** +** Return one of the JSON_LOOKUP error codes if problems are seen. */ -static u32 jsonLookupBlobStep( +static u32 jsonLookupStep( JsonParse *pParse, /* The JSON to search */ u32 iRoot, /* Begin the search at this element of aBlob[] */ const char *zPath, /* The path to search */ @@ -2173,7 +2177,7 @@ static u32 jsonLookupBlobStep( if( zPath[i] ){ i++; }else{ - return JSON_BLOB_PATHERROR; + return JSON_LOOKUP_PATHERROR; } testcase( nKey==0 ); }else{ @@ -2181,37 +2185,37 @@ static u32 jsonLookupBlobStep( for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; if( nKey==0 ){ - return JSON_BLOB_PATHERROR; + return JSON_LOOKUP_PATHERROR; } } - if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_BLOB_NOTFOUND; + if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); j = iRoot + n; /* j is the index of a label */ iEnd = j+sz; while( jaBlob[j] & 0x0f; - if( xJSONB_TEXTRAW ) return JSON_BLOB_ERROR; + if( xJSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, j, &sz); - if( n==0 ) return JSON_BLOB_ERROR; + if( n==0 ) return JSON_LOOKUP_ERROR; k = j+n; /* k is the index of the label text */ - if( k+sz>=iEnd ) return JSON_BLOB_ERROR; + if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ u32 v = k+sz; /* v is the index of the value */ - if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; + if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, v, &sz); - if( n==0 || v+n+sz>iEnd ) return JSON_BLOB_ERROR; + if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; assert( j>0 ); - rc = jsonLookupBlobStep(pParse, v, &zPath[i], j); + rc = jsonLookupStep(pParse, v, &zPath[i], j); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } j = k+sz; - if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_BLOB_ERROR; + if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, j, &sz); - if( n==0 ) return JSON_BLOB_ERROR; + if( n==0 ) return JSON_LOOKUP_ERROR; j += n+sz; } - if( j>iEnd ) return JSON_BLOB_ERROR; + if( j>iEnd ) return JSON_LOOKUP_ERROR; if( pParse->eEdit>=JEDIT_INS ){ u32 nIns; /* Total bytes to insert (label+value) */ JsonParse v; /* BLOB encoding of the value to be inserted */ @@ -2230,8 +2234,8 @@ static u32 jsonLookupBlobStep( v.eEdit = pParse->eEdit; v.nIns = pParse->nIns; v.aIns = pParse->aIns; - rc = jsonLookupBlobStep(&v, 0, &zPath[i], 0); - if( JSON_BLOB_ISERROR(rc) || v.oom ){ + rc = jsonLookupStep(&v, 0, &zPath[i], 0); + if( JSON_LOOKUP_ISERROR(rc) || v.oom ){ pParse->oom |= v.oom; jsonParseReset(&v); jsonParseReset(&ix); @@ -2257,7 +2261,7 @@ static u32 jsonLookupBlobStep( } }else if( zPath[0]=='[' ){ x = pParse->aBlob[iRoot] & 0x0f; - if( x!=JSONB_ARRAY ) return JSON_BLOB_NOTFOUND; + if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); k = 0; i = 1; @@ -2276,31 +2280,31 @@ static u32 jsonLookupBlobStep( nn = nn*10 + zPath[i] - '0'; i++; }while( sqlite3Isdigit(zPath[i]) ); - if( nn>k ) return JSON_BLOB_NOTFOUND; + if( nn>k ) return JSON_LOOKUP_NOTFOUND; k -= nn; } if( zPath[i]!=']' ){ - return JSON_BLOB_PATHERROR; + return JSON_LOOKUP_PATHERROR; } }else{ - return JSON_BLOB_PATHERROR; + return JSON_LOOKUP_PATHERROR; } } j = iRoot+n; iEnd = j+sz; while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } k--; n = jsonbPayloadSize(pParse, j, &sz); - if( n==0 ) return JSON_BLOB_ERROR; + if( n==0 ) return JSON_LOOKUP_ERROR; j += n+sz; } - if( j>iEnd ) return JSON_BLOB_ERROR; - if( k>0 ) return JSON_BLOB_NOTFOUND; + if( j>iEnd ) return JSON_LOOKUP_ERROR; + if( k>0 ) return JSON_LOOKUP_NOTFOUND; if( pParse->eEdit>=JEDIT_INS ){ JsonParse v; testcase( pParse->eEdit==JEDIT_INS ); @@ -2315,8 +2319,8 @@ static u32 jsonLookupBlobStep( v.eEdit = pParse->eEdit; v.nIns = pParse->nIns; v.aIns = pParse->aIns; - rc = jsonLookupBlobStep(&v, 0, &zPath[i+1], 0); - if( JSON_BLOB_ISERROR(rc) || v.oom ){ + rc = jsonLookupStep(&v, 0, &zPath[i+1], 0); + if( JSON_LOOKUP_ISERROR(rc) || v.oom ){ pParse->oom |= v.oom; jsonParseReset(&v); return rc; @@ -2330,9 +2334,9 @@ static u32 jsonLookupBlobStep( return j; } }else{ - return JSON_BLOB_PATHERROR; + return JSON_LOOKUP_PATHERROR; } - return JSON_BLOB_NOTFOUND; + return JSON_LOOKUP_NOTFOUND; } /* @@ -2670,11 +2674,11 @@ static void jsonInsertIntoBlob( p->nIns = ax.nBlob; p->aIns = ax.aBlob; p->delta = 0; - rc = jsonLookupBlobStep(p, 0, zPath+1, 0); + rc = jsonLookupStep(p, 0, zPath+1, 0); } jsonParseReset(&ax); - if( rc==JSON_BLOB_NOTFOUND ) continue; - if( JSON_BLOB_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; + if( rc==JSON_LOOKUP_NOTFOUND ) continue; + if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; } jsonReturnParse(ctx, p); jsonParseFree(p); @@ -2682,7 +2686,7 @@ static void jsonInsertIntoBlob( jsonInsertIntoBlob_patherror: jsonParseFree(p); - if( rc==JSON_BLOB_ERROR ){ + if( rc==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); }else{ jsonBadPathError(ctx, zPath); @@ -3149,11 +3153,11 @@ static void jsonArrayLengthFunc( jsonParseFree(p); return; } - i = jsonLookupBlobStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); - if( JSON_BLOB_ISERROR(i) ){ - if( i==JSON_BLOB_NOTFOUND ){ + i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ - }else if( i==JSON_BLOB_PATHERROR ){ + }else if( i==JSON_LOOKUP_PATHERROR ){ jsonBadPathError(ctx, zPath); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); @@ -3230,7 +3234,7 @@ static void jsonExtractFunc( u32 j; if( zPath==0 ) goto json_extract_error; if( zPath[0]=='$' ){ - j = jsonLookupBlobStep(p, 0, zPath+1, 0); + j = jsonLookupStep(p, 0, zPath+1, 0); }else if( (flags & JSON_ABPATH) ){ /* The -> and ->> operators accept abbreviated PATH arguments. This ** is mostly for compatibility with PostgreSQL, but also for @@ -3253,7 +3257,7 @@ static void jsonExtractFunc( jsonAppendRaw(&jx, zPath, nPath); } jsonStringTerminate(&jx); - j = jsonLookupBlobStep(p, 0, jx.zBuf, 0); + j = jsonLookupStep(p, 0, jx.zBuf, 0); jsonStringReset(&jx); }else{ jsonBadPathError(ctx, zPath); @@ -3280,14 +3284,14 @@ static void jsonExtractFunc( jsonAppendSeparator(&jx); jsonXlateBlobToText(p, j, &jx); } - }else if( j==JSON_BLOB_NOTFOUND ){ + }else if( j==JSON_LOOKUP_NOTFOUND ){ if( argc==2 ){ goto json_extract_error; /* Return NULL if not found */ }else{ jsonAppendSeparator(&jx); jsonAppendRawNZ(&jx, "null", 4); } - }else if( j==JSON_BLOB_ERROR ){ + }else if( j==JSON_LOOKUP_ERROR ){ sqlite3_result_error(ctx, "malformed JSON", -1); goto json_extract_error; }else{ @@ -3613,8 +3617,8 @@ static void jsonRemoveFunc( ){ JsonParse *p; /* The parse */ const char *zPath = 0; /* Path of element to be removed */ - u32 i; /* Loop counter */ - int rc; /* Subroutine return code */ + int i; /* Loop counter */ + u32 rc; /* Subroutine return code */ if( argc<1 ) return; p = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); @@ -3636,9 +3640,9 @@ static void jsonRemoveFunc( } p->eEdit = JEDIT_DEL; p->delta = 0; - rc = jsonLookupBlobStep(p, 0, zPath+1, 0); - if( rc==JSON_BLOB_NOTFOUND ) continue; - if( JSON_BLOB_ISERROR(rc) ) goto json_remove_patherror; + rc = jsonLookupStep(p, 0, zPath+1, 0); + if( rc==JSON_LOOKUP_NOTFOUND ) continue; + if( JSON_LOOKUP_ISERROR(rc) ) goto json_remove_patherror; } jsonReturnParse(ctx, p); jsonParseFree(p); @@ -3738,11 +3742,11 @@ static void jsonTypeFunc( jsonBadPathError(ctx, zPath); goto json_type_done; } - i = jsonLookupBlobStep(p, 0, zPath+1, 0); - if( JSON_BLOB_ISERROR(i) ){ - if( i==JSON_BLOB_NOTFOUND ){ + i = jsonLookupStep(p, 0, zPath+1, 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ - }else if( i==JSON_BLOB_PATHERROR ){ + }else if( i==JSON_LOOKUP_PATHERROR ){ jsonBadPathError(ctx, zPath); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); @@ -4361,8 +4365,8 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ if( levelChange ){ if( p->nParent>0 ){ JsonParent *pParent = &p->aParent[p->nParent-1]; - u32 i = pParent->iValue; - p->eType = p->sParse.aBlob[i] & 0x0f; + u32 iVal = pParent->iValue; + p->eType = p->sParse.aBlob[iVal] & 0x0f; }else{ p->eType = 0; } @@ -4628,9 +4632,9 @@ static int jsonEachFilter( i = p->i = 0; p->eType = 0; }else{ - i = jsonLookupBlobStep(&p->sParse, 0, zRoot+1, 0); - if( JSON_BLOB_ISERROR(i) ){ - if( i==JSON_BLOB_NOTFOUND ){ + i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ p->i = 0; p->eType = 0; p->iEnd = 0; From c1e85742da3633cb9a154043b22ffb3c64c31bdc Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 20:25:36 +0000 Subject: [PATCH 304/347] Code and comment cleanup. Everything should work the same. FossilOrigin-Name: c640754df0d3ffdad994745f0d0e10c8f19f424b87f6a6e6e269491a0350b950 --- manifest | 12 +-- manifest.uuid | 2 +- src/json.c | 201 +++++++++++++++++++++++++++++--------------------- 3 files changed, 125 insertions(+), 90 deletions(-) diff --git a/manifest b/manifest index 6f7377cfb8..6e1a782d31 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings.\s\sRefactor\ssome\sidentifier\snames\sfor\nclearer\spresentation. -D 2023-12-02T18:17:38.516 +C Code\sand\scomment\scleanup.\s\sEverything\sshould\swork\sthe\ssame. +D 2023-12-02T20:25:36.578 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c b67328611c8ebc5aca49e2d73b1bad9962948aa5c952f3d96c44eb74ee3d0010 +F src/json.c 5ad1a1be6199359b09bb4eca690789b77375e00f17c55697b6288f1b0fbbe8b0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 1304534001e9ef66c6b12752b69d790bfa3427cc803f87cc48ca22ae12df0fdf -R 1b492fad564bbccdff370d30a804d641 +P 7e3941502789c5afaf19b08112f464abf5e3cba7f92fc9290af2a0f96127ad9a +R 9f984966a856d28c36c8f0d6d69732a4 U drh -Z f1274565e5b6e1f5da018a4ab14dfe40 +Z ea3e13091dceb571341f1dccf0e97828 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 05b1875e6c..9a68b3807a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7e3941502789c5afaf19b08112f464abf5e3cba7f92fc9290af2a0f96127ad9a \ No newline at end of file +c640754df0d3ffdad994745f0d0e10c8f19f424b87f6a6e6e269491a0350b950 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 064d9457ce..9356bfa8d5 100644 --- a/src/json.c +++ b/src/json.c @@ -133,12 +133,13 @@ #define JSONB_ARRAY 11 /* An array */ #define JSONB_OBJECT 12 /* An object */ -/* Human-readable names for the JSONB values: +/* Human-readable names for the JSONB values. The index for each +** string must correspond to the JSONB_* integer above. */ static const char * const jsonbType[] = { "null", "true", "false", "integer", "integer", "real", "real", "text", "text", "text", - "text", "array", "object" + "text", "array", "object", "", "", "", "" }; /* @@ -168,7 +169,7 @@ static const char jsonIsSpace[] = { #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) /* -** Characters that are special to JSON. Control charaters, +** Characters that are special to JSON. Control characters, ** '"' and '\\'. */ static const char jsonIsOk[256] = { @@ -204,7 +205,6 @@ typedef struct JsonCache JsonCache; typedef struct JsonString JsonString; typedef struct JsonParse JsonParse; - /* ** Magic number used for the JSON parse cache in sqlite3_get_auxdata() */ @@ -213,8 +213,14 @@ typedef struct JsonParse JsonParse; /* A cache mapping JSON text into JSONB blobs. ** -** All content, both JSON text and the JSONB blobs, is stored as RCStr -** objects. +** Each cache entry is a JsonParse object with the following restrictions: +** +** * The bReadOnly flag must be set +** +** * The aBlob[] array must be owned by the JsonParse object. In other +** words, nBlobAlloc must be non-zero. +** +** * zJson must be an RCStr. In other words bJsonIsRCStr must be true. */ struct JsonCache { sqlite3 *db; /* Database connection */ @@ -225,6 +231,10 @@ struct JsonCache { /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator ** that can be and is used to create strings other than JSON. +** +** If the generated string is longer than will fit into the zSpace[] buffer, +** then it will be an RCStr string. This aids with caching of large +** JSON strings. */ struct JsonString { sqlite3_context *pCtx; /* Function context - put error messages here */ @@ -260,18 +270,17 @@ struct JsonString { /* A parsed JSON value. Lifecycle: ** ** 1. JSON comes in and is parsed into a JSONB value in aBlob. The -** original text is stored in zJson. +** original text is stored in zJson. This step is skipped if the +** input is JSONB instead of text JSON. ** -** 2. The aBlob is searched using the JSON path notation, if needed. +** 2. The aBlob[] array is searched using the JSON path notation, if needed. ** -** 3. Zero or more changes are made to aBlob (via json_remove() or -** json_replace() or similar). +** 3. Zero or more changes are made to aBlob[] (via json_remove() or +** json_replace() or json_patch() or similar). ** -** 4. New JSON text is generated from the aBlob for output. -** -** Step 1 is omitted if the input is a BLOB in the JSONB format. Step 4 -** is omitted if the output is JSONB or some other value that is not -** JSON text. +** 4. New JSON text is generated from the aBlob[] for output. This step +** is skipped the function is one of the jsonb_* functions that returns +** JSONB instead of text JSON. */ struct JsonParse { u8 *aBlob; /* JSONB representation of JSON value */ @@ -326,7 +335,6 @@ static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); static void jsonParseFree(JsonParse*); - /************************************************************************** ** Utility routines for dealing with JsonCache objects **************************************************************************/ @@ -346,11 +354,11 @@ static void jsonCacheDeleteGeneric(void *p){ } /* -** Insert a new entry into the cache. If the cache is full, expell +** Insert a new entry into the cache. If the cache is full, expel ** the least recently used entry. Return SQLITE_OK on success or a ** result code otherwise. ** -** Both the input JSON and JSONB must be RCStr objects. +** Cache entries are stored in age order, oldest first. */ static int jsonCacheInsert( sqlite3_context *ctx, /* The SQL statement context holding the cache */ @@ -386,7 +394,14 @@ static int jsonCacheInsert( /* ** Search for a cached translation the json text supplied by pArg. Return -** the JsonParse object if found. +** the JsonParse object if found. Return NULL if not found. +** +** When a match if found, the matching entry is moved to become the +** most-recently used entry if it isn't so already. +** +** The JsonParse object returned still belongs to the Cache and might +** be deleted at any moment. If the caller whants the JsonParse to +** linger, it needs to increment the nPJRef reference counter. */ static JsonParse *jsonCacheSearch( sqlite3_context *ctx, /* The SQL statement context holding the cache */ @@ -419,6 +434,7 @@ static JsonParse *jsonCacheSearch( } if( inUsed ){ if( inUsed-1 ){ + /* Make the matching entry the most recently used entry */ JsonParse *tmp = p->a[i]; memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); p->a[p->nUsed-1] = tmp; @@ -561,7 +577,9 @@ static void jsonStringTerminate(JsonString *p){ } } -/* Try to force the string to be a zero-terminated RCStr string. +/* Try to force the string to be a zero-terminated RCStr string. In other +** words, make sure it is not still using the internal zSpace[] static +** buffer. ** ** Return true on success. Return false if an OOM prevents this ** from happening. @@ -591,9 +609,12 @@ static void jsonAppendSeparator(JsonString *p){ } /* Append the N-byte string in zIn to the end of the JsonString string -** under construction. Enclose the string in "..." and escape -** any double-quotes or backslash characters contained within the +** under construction. Enclose the string in double-quotes ("...") and +** escape any double-quotes or backslash characters contained within the ** string. +** +** This routine is a high-runner. There is a measurable performance +** increase associated with unwinding the jsonIsOk[] loop. */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ u32 k; @@ -604,8 +625,8 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ p->zBuf[p->nUsed++] = '"'; while( 1 /*exit-by-break*/ ){ k = 0; - while( k+1=N ){ if( k>0 ){ memcpy(&p->zBuf[p->nUsed], z, k); @@ -783,12 +804,8 @@ static void jsonParseReset(JsonParse *pParse){ } /* -** Free a JsonParse object that was obtained from sqlite3_malloc(). -** -** Note that destroying JsonParse might call sqlite3RCStrUnref() to -** destroy the zJson value. The RCStr object might recursively invoke -** JsonParse to destroy this pParse object again. Take care to ensure -** that this recursive destructor sequence terminates harmlessly. +** Decrement the reference count on the JsonParse object. When the +** count reaches zero, free the object. */ static void jsonParseFree(JsonParse *pParse){ if( pParse ){ @@ -1010,7 +1027,6 @@ static void jsonWrongNumArgs( ** Utility routines for dealing with the binary BLOB representation of JSON ****************************************************************************/ - /* ** Expand pParse->aBlob so that it holds at least N bytes. ** @@ -2068,7 +2084,8 @@ static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ } /* -** Edit the size of the element at iRoot by the amount in pParse->delta. +** Edit the payload size of the element at iRoot by the amount in +** pParse->delta. */ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ u32 sz = 0; @@ -2126,6 +2143,55 @@ static void jsonBlobEdit( #define JSON_LOOKUP_PATHERROR 0xfffffffd #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) +/* Forward declaration */ +static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); + + +/* This helper routine for jsonLookupStep() populates pIns with +** binary data that is to be inserted into pParse. +** +** In the common case, pIns just points to pParse->aIns and pParse->nIns. +** But if the zPath of the original edit operation includes path elements +** that go deeper, additional substructure must be created. +** +** For example: +** +** json_insert('{}', '$.a.b.c', 123); +** +** The search stops at '$.a' But additional substructure must be +** created for the ".b.c" part of the patch so that the final result +** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with +** the binary equivalent of {"b":{"c":123}} so that it can be inserted. +** +** The caller is responsible for resetting pIns when it has finished +** using the substructure. +*/ +static u32 jsonCreateEditSubstructure( + JsonParse *pParse, /* The original JSONB that is being edited */ + JsonParse *pIns, /* Populate this with the blob data to insert */ + const char *zTail /* Tail of the path that determins substructure */ +){ + static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; + int rc; + memset(pIns, 0, sizeof(*pIns)); + if( zTail[0]==0 ){ + /* No substructure. Just insert what is given in pParse. */ + pIns->aBlob = pParse->aIns; + pIns->nBlob = pParse->nIns; + rc = 0; + }else{ + /* Construct the binary substructure */ + pIns->nBlob = 1; + pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.']; + pIns->eEdit = pParse->eEdit; + pIns->nIns = pParse->nIns; + pIns->aIns = pParse->aIns; + rc = jsonLookupStep(pIns, 0, zTail, 0); + pParse->oom |= pIns->oom; + } + return rc; /* Error code only */ +} + /* ** Search along zPath to find the Json element specified. Return an ** index into pParse->aBlob[] for the start of that element's value. @@ -2135,6 +2201,13 @@ static void jsonBlobEdit( ** label, before returning. ** ** Return one of the JSON_LOOKUP error codes if problems are seen. +** +** This routine will also modify the blob. If pParse->eEdit is one of +** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be +** made to the selected value. If an edit is performed, then the return +** value does not necessarily point to the select element. If an edit +** is performed, the return value is only useful for detecting error +** conditions. */ static u32 jsonLookupStep( JsonParse *pParse, /* The JSON to search */ @@ -2145,7 +2218,6 @@ static u32 jsonLookupStep( u32 i, j, k, nKey, sz, n, iEnd, rc; const char *zKey; u8 x; - static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; if( zPath[0]==0 ){ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ @@ -2224,26 +2296,12 @@ static u32 jsonLookupStep( testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); jsonBlobAppendNode(&ix,JSONB_TEXTRAW, nKey, 0); - memset(&v, 0, sizeof(v)); - if( zPath[i]==0 ){ - v.nBlob = pParse->nIns; - v.aBlob = pParse->aIns; - }else{ - v.nBlob = 1; - v.aBlob = (u8*)&emptyObject[zPath[i]=='.']; - v.eEdit = pParse->eEdit; - v.nIns = pParse->nIns; - v.aIns = pParse->aIns; - rc = jsonLookupStep(&v, 0, &zPath[i], 0); - if( JSON_LOOKUP_ISERROR(rc) || v.oom ){ - pParse->oom |= v.oom; - jsonParseReset(&v); - jsonParseReset(&ix); - return rc; - } - } pParse->oom |= ix.oom; - if( jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) ){ + rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); + if( !JSON_LOOKUP_ISERROR(rc) + && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) + ){ + assert( !pParse->oom ); nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); if( !pParse->oom ){ @@ -2257,7 +2315,7 @@ static u32 jsonLookupStep( } jsonParseReset(&v); jsonParseReset(&ix); - return j; + return rc; } }else if( zPath[0]=='[' ){ x = pParse->aBlob[iRoot] & 0x0f; @@ -2309,29 +2367,16 @@ static u32 jsonLookupStep( JsonParse v; testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); - memset(&v, 0, sizeof(v)); - if( zPath[i+1]==0 ){ - v.aBlob = pParse->aIns; - v.nBlob = pParse->nIns; - }else{ - v.nBlob = 1; - v.aBlob = (u8*)&emptyObject[zPath[i+1]=='.']; - v.eEdit = pParse->eEdit; - v.nIns = pParse->nIns; - v.aIns = pParse->aIns; - rc = jsonLookupStep(&v, 0, &zPath[i+1], 0); - if( JSON_LOOKUP_ISERROR(rc) || v.oom ){ - pParse->oom |= v.oom; - jsonParseReset(&v); - return rc; - } - } - if( jsonBlobMakeEditable(pParse, v.nBlob) ){ + rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); + if( !JSON_LOOKUP_ISERROR(rc) + && jsonBlobMakeEditable(pParse, v.nBlob) + ){ + assert( !pParse->oom ); jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); } jsonParseReset(&v); if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); - return j; + return rc; } }else{ return JSON_LOOKUP_PATHERROR; @@ -3169,17 +3214,7 @@ static void jsonArrayLengthFunc( i = 0; } if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ - u32 n, sz = 0, iEnd; - n = jsonbPayloadSize(p, i, &sz); - if( n==0 ) eErr = 2; - iEnd = i+n+sz; - i += n; - while( eErr==0 && i Date: Sat, 2 Dec 2023 20:35:04 +0000 Subject: [PATCH 305/347] Fix various compiler warnings and other problems with the new code on this branch. FossilOrigin-Name: 3a623cfa173b4035c759cb84985d11d8727053beb383648503987d6ab15c0ef0 --- ext/fts5/fts5.h | 2 +- ext/fts5/fts5Int.h | 5 ++- ext/fts5/fts5_expr.c | 8 ++++ ext/fts5/fts5_index.c | 92 +++++++++++++++++++++++++++++++++++++------ manifest | 18 ++++----- manifest.uuid | 2 +- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 9feedbba19..63c9765eb6 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -281,7 +281,7 @@ struct Fts5PhraseIter { ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the -** "detail=none" or "detail=column" option. +** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 3 */ diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 911f547d17..4e4385ce2b 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -467,7 +467,7 @@ void sqlite3Fts5StructureRelease(void*); int sqlite3Fts5StructureTest(Fts5Index*, void*); /* -** Used by xInstToken() and xPhraseToken(). +** Used by xInstToken(): */ int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); @@ -547,8 +547,9 @@ int sqlite3Fts5IndexLoadConfig(Fts5Index *p); int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); -/* Used to populate hash tables for xInstToken in detail=none/column mode. */ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); + +/* Used to populate hash tables for xInstToken in detail=none/column mode. */ int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff ); diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 95d102062d..6f58cf8735 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -2985,6 +2985,11 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ return 0; } +/* +** pToken is a buffer nToken bytes in size that may or may not contain +** an embedded 0x00 byte. If it does, return the number of bytes in +** the buffer before the 0x00. If it does not, return nToken. +*/ static int fts5QueryTerm(const char *pToken, int nToken){ int ii; for(ii=0; iiapTombstone) for the -** iterator passed as the second argument. If an OOM error occurs, leave -** an error in the Fts5Index object. +** Allocate a tombstone hash page array object (pIter->pTombArray) for +** the iterator passed as the second argument. If an OOM error occurs, +** leave an error in the Fts5Index object. */ static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ const int nTomb = pIter->pSeg->nPgTombstone; @@ -2748,6 +2752,7 @@ static void fts5SegIterNextInit( bDlidx = (val & 0x0001); } p->rc = sqlite3_reset(pSel); + sqlite3_bind_null(pSel, 2); if( p->rc ) return; } @@ -2772,7 +2777,7 @@ static void fts5SegIterNextInit( if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); assert( p->rc!=SQLITE_OK || - fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0 + fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0 ); } } @@ -2862,6 +2867,10 @@ static void fts5IndexFreeArray(Fts5Data **ap, int n){ } } +/* +** Decrement the ref-count of the object passed as the only argument. If it +** reaches 0, free it and its contents. +*/ static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){ if( p ){ p->nRef--; @@ -3828,6 +3837,10 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ } } +/* +** All the component segment-iterators of pIter have been set up. This +** functions finishes setup for iterator pIter itself. +*/ static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){ int iIter; for(iIter=pIter->nSeg-1; iIter>0; iIter--){ @@ -6566,13 +6579,25 @@ static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ pSeg->pLeaf = 0; } -typedef struct Fts5TokenDataMap Fts5TokenDataMap; +/* +** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an +** array of these for each row it visits. Or, for an iterator used by an +** "ORDER BY rank" query, it accumulates an array of these for the entire +** query. +** +** Each instance in the array indicates the iterator (and therefore term) +** associated with position iPos of rowid iRowid. This is used by the +** xInstToken() API. +*/ struct Fts5TokenDataMap { - i64 iRowid; - i64 iPos; - int iIter; + i64 iRowid; /* Row this token is located in */ + i64 iPos; /* Position of token */ + int iIter; /* Iterator token was read from */ }; +/* +** An object used to supplement Fts5Iter for tokendata=1 iterators. +*/ struct Fts5TokenDataIter { int nIter; int nIterAlloc; @@ -6585,10 +6610,14 @@ struct Fts5TokenDataIter { Fts5Iter *apIter[1]; }; +/* +** This function appends iterator pAppend to Fts5TokenDataIter pIn and +** returns the result. +*/ static Fts5TokenDataIter *fts5AppendTokendataIter( - Fts5Index *p, - Fts5TokenDataIter *pIn, - Fts5Iter *pAppend + Fts5Index *p, /* Index object (for error code) */ + Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ + Fts5Iter *pAppend /* Append this iterator */ ){ Fts5TokenDataIter *pRet = pIn; @@ -6617,6 +6646,9 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( return pRet; } +/* +** Delete an Fts5TokenDataIter structure and its contents. +*/ static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ if( pSet ){ int ii; @@ -6629,6 +6661,13 @@ static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ } } +/* +** The iterator passed as the first argument must be a tokendata=1 iterator +** (pIter->pTokenDataIter!=0). This function is used to access the token +** instance located at offset iOff of column iCol of row iRowid. It is +** returned via output pointers *ppOut and *pnOut. This is used by the +** xInstToken() API. +*/ static int fts5TokendataIterToken( Fts5Iter *pIter, i64 iRowid, @@ -6673,6 +6712,9 @@ static int fts5TokendataIterToken( return SQLITE_OK; } +/* +** Append a mapping to the token-map belonging to object pT. +*/ static void fts5TokendataIterAppendMap( Fts5Index *p, Fts5TokenDataIter *pT, @@ -6703,6 +6745,12 @@ static void fts5TokendataIterAppendMap( } } +/* +** The iterator passed as the only argument must be a tokendata=1 iterator +** (pIter->pTokenDataIter!=0). This function sets the iterator output +** variables (pIter->base.*) according to the contents of the current +** row. +*/ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ int ii; int nHit = 0; @@ -6819,6 +6867,13 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ } } +/* +** The iterator passed as the only argument must be a tokendata=1 iterator +** (pIter->pTokenDataIter!=0). This function advances the iterator. If +** argument bFrom is false, then the iterator is advanced to the next +** entry. Or, if bFrom is true, it is advanced to the first entry with +** a rowid of iFrom or greater. +*/ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ int ii; Fts5TokenDataIter *pT = pIter->pTokenDataIter; @@ -6841,6 +6896,10 @@ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ fts5IterSetOutputsTokendata(pIter); } +/* +** If the segment-iterator passed as the first argument is at EOF, then +** set pIter->term to a copy of buffer pTerm. +*/ static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ if( pIter && pIter->aSeg[0].pLeaf==0 ){ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); @@ -7144,7 +7203,9 @@ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ } /* -** +** This is used by xInstToken() to access the token at offset iOff, column +** iCol of row iRowid. The token is returned via output variables *ppOut +** and *pnOut. */ int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, @@ -7173,6 +7234,13 @@ void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ } } +/* +** Set a token-mapping for the iterator passed as the first argument. This +** is used in detail=column or detail=none mode when a token is requested +** using the xInstToken() API. In this case the caller tokenizers the +** current row and configures the token-mapping via multiple calls to this +** function. +*/ int sqlite3Fts5IndexIterWriteTokendata( Fts5IndexIter *pIndexIter, const char *pToken, int nToken, diff --git a/manifest b/manifest index 699d024ca1..2353c55e4c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\stokendata=1\squeries\srequire\smultiple\ssegment-cursors,\sallow\sthose\scursors\sto\sshare\sa\ssingle\sarray\sof\sin-memory\stombstone\spages. -D 2023-12-02T18:14:07.393 +C Fix\svarious\scompiler\swarnings\sand\sother\sproblems\swith\sthe\snew\scode\son\sthis\sbranch. +D 2023-12-02T20:35:04.768 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -89,14 +89,14 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7 F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h 5e5630fc81e212f658afaa5b2650dac939d2729d0723aef1eeaff908f1725648 -F ext/fts5/fts5Int.h 285118aa6dfccb382e84eaeb9f7bec334e4f7104efa9303240605447003445c9 +F ext/fts5/fts5.h ff90acaa97f8e865b66d1177d1b56b8c110fd5548ab5863bab43f055a1d745fe +F ext/fts5/fts5Int.h 1fdbf3d16bdd481fe2ee99927919e4c3db835efae00f8efd7efb5e6a93277459 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c f83259b52b7b3e76768b835fe155cb7e345affdfafb96574372b4127d5f5496a +F ext/fts5/fts5_expr.c 5619c3fab45a78eb5ed3021e3b40ec3b435ef3669293e8700354aa8dd3e6c796 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 21f8f449666ac44c12d5051e153ad84a886a729cb2f5d6ad02a113095c3f8ec6 +F ext/fts5/fts5_index.c b31bf4f0fb51a15cc1aa54c2f337197740f4f8898347266781ca6970ca751302 F ext/fts5/fts5_main.c 075995302198fe6f591fdbbedd415dfac564a9bfc20aea81e6fa0503b2d94af0 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -2149,8 +2149,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 7bda09ab404a110d57449e149a3281fca8dc4cacf7bd9832ea2a1356ad20fe8e -R 1ce6343b4aa590c869e9b9aa51095415 +P e0175d07e4094db5ea4b0378a5ff480dafb6ba9da86a113fa767c4c89c3c866f +R a72f98879c56fe0b2489dc56b6289649 U dan -Z 8664660cde606ab1a6fdc20b18622c4a +Z 6a8664529c8f348c40cce12fb229aa10 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0cbd084480..b4349493de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e0175d07e4094db5ea4b0378a5ff480dafb6ba9da86a113fa767c4c89c3c866f \ No newline at end of file +3a623cfa173b4035c759cb84985d11d8727053beb383648503987d6ab15c0ef0 \ No newline at end of file From 8f8d481485b6409b739a2a5b4693a35b4aab5c77 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 20:37:45 +0000 Subject: [PATCH 306/347] Fix harmless compiler warnings reported by MSVC. FossilOrigin-Name: 419652c0c82980bd043584dcd2976f91dfff7b926b216d597698299850b855c0 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 6e1a782d31..cc84f09d52 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Code\sand\scomment\scleanup.\s\sEverything\sshould\swork\sthe\ssame. -D 2023-12-02T20:25:36.578 +C Fix\sharmless\scompiler\swarnings\sreported\sby\sMSVC. +D 2023-12-02T20:37:45.416 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 5ad1a1be6199359b09bb4eca690789b77375e00f17c55697b6288f1b0fbbe8b0 +F src/json.c fc67289aebe1ab7d3ccc31ec61b0fcea1d724f23026d072c05242dcc57049edf F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 7e3941502789c5afaf19b08112f464abf5e3cba7f92fc9290af2a0f96127ad9a -R 9f984966a856d28c36c8f0d6d69732a4 +P c640754df0d3ffdad994745f0d0e10c8f19f424b87f6a6e6e269491a0350b950 +R cbbd74dd3f256fc8164ba32c868936ce U drh -Z ea3e13091dceb571341f1dccf0e97828 +Z de70fb53fc1b64715f23def652af30f4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9a68b3807a..304fd5742b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c640754df0d3ffdad994745f0d0e10c8f19f424b87f6a6e6e269491a0350b950 \ No newline at end of file +419652c0c82980bd043584dcd2976f91dfff7b926b216d597698299850b855c0 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9356bfa8d5..067bdb4739 100644 --- a/src/json.c +++ b/src/json.c @@ -3416,10 +3416,10 @@ static int jsonMergePatchBlob( u8 eTLabel; /* Node type of the target label */ u32 iTLabel = 0; /* Index of the label */ u32 nTLabel = 0; /* Header size in bytes for the target label */ - u32 szTLabel; /* Size of the target label payload */ + u32 szTLabel = 0; /* Size of the target label payload */ u32 iTValue = 0; /* Index of the target value */ u32 nTValue = 0; /* Header size of the target value */ - u32 szTValue; /* Payload size for the target value */ + u32 szTValue = 0; /* Payload size for the target value */ u32 iPCursor; /* Cursor position while scanning the patch */ u32 iPEnd; /* First byte past the end of the patch */ From c78c3c91ae722722b0f9ba59dd850885b5ffe5a4 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 2 Dec 2023 21:39:34 +0000 Subject: [PATCH 307/347] Implement strict JSONB checking in the json_valid() function. FossilOrigin-Name: 0f26d38880fcbc207abcc94dbc170a7428bab1b4f0b7731aaf5bee0224000994 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index cc84f09d52..95a05c007f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sharmless\scompiler\swarnings\sreported\sby\sMSVC. -D 2023-12-02T20:37:45.416 +C Implement\sstrict\sJSONB\schecking\sin\sthe\sjson_valid()\sfunction. +D 2023-12-02T21:39:34.516 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fc67289aebe1ab7d3ccc31ec61b0fcea1d724f23026d072c05242dcc57049edf +F src/json.c fd321d1ab2d6f42354bd2ff7bc4974ab8dd9eee977e4e8e137cf1360873ef052 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 c640754df0d3ffdad994745f0d0e10c8f19f424b87f6a6e6e269491a0350b950 -R cbbd74dd3f256fc8164ba32c868936ce +P 419652c0c82980bd043584dcd2976f91dfff7b926b216d597698299850b855c0 +R ed8c7ad5ad50d0c7dab9c9a4109db3da U drh -Z de70fb53fc1b64715f23def652af30f4 +Z 50662e1c3dbad4c9b4aaea32a805ad55 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 304fd5742b..c4f20a42ae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -419652c0c82980bd043584dcd2976f91dfff7b926b216d597698299850b855c0 \ No newline at end of file +0f26d38880fcbc207abcc94dbc170a7428bab1b4f0b7731aaf5bee0224000994 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 067bdb4739..ae6458cd59 100644 --- a/src/json.c +++ b/src/json.c @@ -3880,8 +3880,39 @@ static void jsonValidFunc( } case SQLITE_BLOB: { if( (flags & 0x0c)!=0 && jsonFuncArgMightBeBinary(argv[0]) ){ - /* TO-DO: strict checking if flags & 0x08 */ - res = 1; + if( flags & 0x04 ){ + /* Superficial checking only - accomplisehd by the + ** jsonFuncArgMightBeBinary() call above. */ + res = 1; + }else{ + /* Strict checking. Check by translating BLOB->TEXT->BLOB. If + ** no errors occur, call that a "strict check". */ + JsonParse px; + JsonString sx; + u8 oom = 0; + memset(&px, 0, sizeof(px)); + px.aBlob = (u8*)sqlite3_value_blob(argv[0]); + px.nBlob = sqlite3_value_bytes(argv[0]); + jsonStringInit(&sx, 0); + jsonXlateBlobToText(&px, 0, &sx); + jsonParseReset(&px); + if( sx.eErr & JSTRING_OOM ) oom = 1; + if( sx.eErr==0 ){ + memset(&px, 0, sizeof(px)); + px.zJson = sx.zBuf; + px.nJson = sx.nUsed; + if( jsonXlateTextToBlob(&px, 0)==px.nJson ){ + res = 1; + } + oom |= px.oom; + jsonParseReset(&px); + } + jsonStringReset(&sx); + if( oom ){ + sqlite3_result_error_nomem(ctx); + return; + } + } } break; } From eb18ae3089ac409ed7c0bd943a993ea8b357c2f5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 00:51:30 +0000 Subject: [PATCH 308/347] Minor code changes for consistency and to simplify testing. FossilOrigin-Name: df272bd837910ad9e03e222716a1201a601399664365f1dcf73d5932372518ed --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 44 ++++++++++++++------------------------------ 3 files changed, 21 insertions(+), 37 deletions(-) diff --git a/manifest b/manifest index 95a05c007f..0746333f7a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Implement\sstrict\sJSONB\schecking\sin\sthe\sjson_valid()\sfunction. -D 2023-12-02T21:39:34.516 +C Minor\scode\schanges\sfor\sconsistency\sand\sto\ssimplify\stesting. +D 2023-12-03T00:51:30.732 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fd321d1ab2d6f42354bd2ff7bc4974ab8dd9eee977e4e8e137cf1360873ef052 +F src/json.c 9a79d32ad7df0f37aaca77a78278af177b7c898f719f7f3c1beb6626cf0cd88e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 419652c0c82980bd043584dcd2976f91dfff7b926b216d597698299850b855c0 -R ed8c7ad5ad50d0c7dab9c9a4109db3da +P 0f26d38880fcbc207abcc94dbc170a7428bab1b4f0b7731aaf5bee0224000994 +R 66de56e0a2d95958c6880cf67a24bb7b U drh -Z 50662e1c3dbad4c9b4aaea32a805ad55 +Z 5f7e6173dda49cfc3454a89f079d029d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c4f20a42ae..8eca6d0777 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f26d38880fcbc207abcc94dbc170a7428bab1b4f0b7731aaf5bee0224000994 \ No newline at end of file +df272bd837910ad9e03e222716a1201a601399664365f1dcf73d5932372518ed \ No newline at end of file diff --git a/src/json.c b/src/json.c index ae6458cd59..a35306b62e 100644 --- a/src/json.c +++ b/src/json.c @@ -534,7 +534,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ } } static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ - if( N==0 ) return; + assert( N>0 ); if( N+p->nUsed >= p->nAlloc ){ jsonStringExpandAndAppend(p,zIn,N); }else{ @@ -572,9 +572,8 @@ static void jsonAppendChar(JsonString *p, char c){ /* Make sure there is a zero terminator on p->zBuf[] */ static void jsonStringTerminate(JsonString *p){ - if( p->nUsednAlloc || jsonStringGrow(p,1) ){ - p->zBuf[p->nUsed] = 0; - } + jsonAppendChar(p, 0); + p->nUsed--; } /* Try to force the string to be a zero-terminated RCStr string. In other @@ -991,24 +990,6 @@ static const struct NanInfName { }; -/* -** Compute the text of an error in JSON path syntax. -** -** If ctx is not NULL then push the error message into ctx and return NULL. -* If ctx is NULL, then return the text of the error message. -*/ -static char *jsonPathSyntaxError(const char *zErr, sqlite3_context *ctx){ - char *zMsg = sqlite3_mprintf("JSON path error near '%q'", zErr); - if( ctx==0 ) return zMsg; - if( zMsg==0 ){ - sqlite3_result_error_nomem(ctx); - }else{ - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); - } - return 0; -} - /* ** Report the wrong number of arguments for json_insert(), json_replace() ** or json_set(). @@ -2659,15 +2640,19 @@ static int jsonFunctionArgToBlob( /* ** Generate a bad path error. +** +** If ctx is not NULL then push the error message into ctx and return NULL. +** If ctx is NULL, then return the text of the error message. */ -static void jsonBadPathError( +static char *jsonBadPathError( sqlite3_context *ctx, /* The function call containing the error */ const char *zPath /* The path with the problem */ ){ - sqlite3 *db = sqlite3_context_db_handle(ctx); - char *zMsg = sqlite3MPrintf(db, "bad JSON path: %Q", zPath); + char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + if( ctx==0 ) return zMsg; sqlite3_result_error(ctx, zMsg, -1); - sqlite3DbFree(db, zMsg); + sqlite3_free(zMsg); + return 0; } /* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent @@ -3287,7 +3272,6 @@ static void jsonExtractFunc( }else if( zPath[0]!='[' ){ jsonAppendRawNZ(&jx, ".", 1); jsonAppendRaw(&jx, zPath, nPath); - jsonAppendChar(&jx, 0); }else{ jsonAppendRaw(&jx, zPath, nPath); } @@ -3685,7 +3669,7 @@ static void jsonRemoveFunc( json_remove_patherror: jsonParseFree(p); - jsonPathSyntaxError(zPath, ctx); + jsonBadPathError(ctx, zPath); return; json_remove_return_null: @@ -4689,7 +4673,7 @@ static int jsonEachFilter( if( zRoot==0 ) return SQLITE_OK; if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } @@ -4707,7 +4691,7 @@ static int jsonEachFilter( return SQLITE_OK; } sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot, 0); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } From 78fa0186b822e137d6d6e157a28cbcad67275cdb Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 11:54:39 +0000 Subject: [PATCH 309/347] Do not let bad hexadecimal digits in malformed JSONB cause an assertion fault. FossilOrigin-Name: 8dec1ba1e5076ff596756e00c1e2ada0245f168a503dd1cadadf848331acfac3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 26 +++++++++++++++++++++----- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 0746333f7a..75a751773f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\scode\schanges\sfor\sconsistency\sand\sto\ssimplify\stesting. -D 2023-12-03T00:51:30.732 +C Do\snot\slet\sbad\shexadecimal\sdigits\sin\smalformed\sJSONB\scause\san\sassertion\sfault. +D 2023-12-03T11:54:39.886 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9a79d32ad7df0f37aaca77a78278af177b7c898f719f7f3c1beb6626cf0cd88e +F src/json.c 0bbd116e9054eae2aa8088071f163c47050388ef83ab871c6c85b57a52bef984 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 0f26d38880fcbc207abcc94dbc170a7428bab1b4f0b7731aaf5bee0224000994 -R 66de56e0a2d95958c6880cf67a24bb7b +P df272bd837910ad9e03e222716a1201a601399664365f1dcf73d5932372518ed +R 214d8647778f37447f46e39ad90ab38f U drh -Z 5f7e6173dda49cfc3454a89f079d029d +Z 2537746c5900cbcfb749d720f1ba2129 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8eca6d0777..05fe5681c0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -df272bd837910ad9e03e222716a1201a601399664365f1dcf73d5932372518ed \ No newline at end of file +8dec1ba1e5076ff596756e00c1e2ada0245f168a503dd1cadadf848331acfac3 \ No newline at end of file diff --git a/src/json.c b/src/json.c index a35306b62e..0668ff6daa 100644 --- a/src/json.c +++ b/src/json.c @@ -817,15 +817,31 @@ static void jsonParseFree(JsonParse *pParse){ } } +/* +** Translate a single byte of Hex into an integer. +** This routine only gives a correct answer if h really is a valid hexadecimal +** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not +** assert() if the digit is not hex. +*/ +static u8 jsonHexToInt(int h){ +#ifdef SQLITE_ASCII + h += 9*(1&(h>>6)); +#endif +#ifdef SQLITE_EBCDIC + h += 9*(1&~(h>>4)); +#endif + return (u8)(h & 0xf); +} + /* ** Convert a 4-byte hex string into an integer */ static u32 jsonHexToInt4(const char *z){ u32 v; - v = (sqlite3HexToInt(z[0])<<12) - + (sqlite3HexToInt(z[1])<<8) - + (sqlite3HexToInt(z[2])<<4) - + sqlite3HexToInt(z[3]); + v = (jsonHexToInt(z[0])<<12) + + (jsonHexToInt(z[1])<<8) + + (jsonHexToInt(z[2])<<4) + + jsonHexToInt(z[3]); return v; } @@ -2524,7 +2540,7 @@ static void jsonReturnFromBlob( }else if( c=='0' ){ c = 0; }else if( c=='x' ){ - c = (sqlite3HexToInt(z[iIn+1])<<4) | sqlite3HexToInt(z[iIn+2]); + c = (jsonHexToInt(z[iIn+1])<<4) | jsonHexToInt(z[iIn+2]); iIn += 2; }else if( c=='\r' && z[i+1]=='\n' ){ iIn++; From 0a18a5807a43f44a43a84f446f82201d389886a2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 19:59:45 +0000 Subject: [PATCH 310/347] Enable incorrect JSONB to be rendered into text without hitting an assertion for a bad whitespace escape in a string. FossilOrigin-Name: 4d6a9a217df6792b41766b774fb0c0553b45f9104c26a0955bf4a30862d7d7bf --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 75a751773f..7f9fac50f0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\slet\sbad\shexadecimal\sdigits\sin\smalformed\sJSONB\scause\san\sassertion\sfault. -D 2023-12-03T11:54:39.886 +C Enable\sincorrect\sJSONB\sto\sbe\srendered\sinto\stext\swithout\shitting\san\nassertion\sfor\sa\sbad\swhitespace\sescape\sin\sa\sstring. +D 2023-12-03T19:59:45.738 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 0bbd116e9054eae2aa8088071f163c47050388ef83ab871c6c85b57a52bef984 +F src/json.c a575d568a65576657aad25fb7ada56007a86faf41d967ddbc80f2109caf00b0e F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 df272bd837910ad9e03e222716a1201a601399664365f1dcf73d5932372518ed -R 214d8647778f37447f46e39ad90ab38f +P 8dec1ba1e5076ff596756e00c1e2ada0245f168a503dd1cadadf848331acfac3 +R 02988a1f4c6dc45085ceececc56fddf4 U drh -Z 2537746c5900cbcfb749d720f1ba2129 +Z c32b25a78a87ced6ccf27a2c8ddc3f70 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 05fe5681c0..d4318802f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8dec1ba1e5076ff596756e00c1e2ada0245f168a503dd1cadadf848331acfac3 \ No newline at end of file +4d6a9a217df6792b41766b774fb0c0553b45f9104c26a0955bf4a30862d7d7bf \ No newline at end of file diff --git a/src/json.c b/src/json.c index 0668ff6daa..9d29685eb0 100644 --- a/src/json.c +++ b/src/json.c @@ -2545,9 +2545,10 @@ static void jsonReturnFromBlob( }else if( c=='\r' && z[i+1]=='\n' ){ iIn++; continue; - }else if( 0xe2==(u8)c ){ - assert( 0x80==(u8)z[i+1] ); - assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); + }else if( 0xe2==(u8)c + && 0x80==(u8)z[i+1] + && (0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2]) + ){ iIn += 2; continue; }else{ From a3bf077b60d1dc08dea1014d7eb84ff57b439a05 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 20:11:35 +0000 Subject: [PATCH 311/347] Ensure that OOM conditions in the generation of the "bad JSON path" error message result in an SQLITE_NOMEM error. FossilOrigin-Name: aa0e02b5c26a2ef3d6216a0ed8bc01382be43173485f898cb63f2a8c559f2e74 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 8 ++++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 7f9fac50f0..31e68565bc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enable\sincorrect\sJSONB\sto\sbe\srendered\sinto\stext\swithout\shitting\san\nassertion\sfor\sa\sbad\swhitespace\sescape\sin\sa\sstring. -D 2023-12-03T19:59:45.738 +C Ensure\sthat\sOOM\sconditions\sin\sthe\sgeneration\sof\sthe\s"bad\sJSON\spath"\serror\nmessage\sresult\sin\san\sSQLITE_NOMEM\serror. +D 2023-12-03T20:11:35.681 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c a575d568a65576657aad25fb7ada56007a86faf41d967ddbc80f2109caf00b0e +F src/json.c 6d92343776fe32e9d703d6e13c1d1883d75e1fd17e081159dd257b21a859f5f9 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 8dec1ba1e5076ff596756e00c1e2ada0245f168a503dd1cadadf848331acfac3 -R 02988a1f4c6dc45085ceececc56fddf4 +P 4d6a9a217df6792b41766b774fb0c0553b45f9104c26a0955bf4a30862d7d7bf +R 1defa3c713a22ae4567d6d3fb033b34d U drh -Z c32b25a78a87ced6ccf27a2c8ddc3f70 +Z e3833547911441ddc3c3cd95bc32393c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d4318802f6..c9937a03fa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4d6a9a217df6792b41766b774fb0c0553b45f9104c26a0955bf4a30862d7d7bf \ No newline at end of file +aa0e02b5c26a2ef3d6216a0ed8bc01382be43173485f898cb63f2a8c559f2e74 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9d29685eb0..9fcfd3e10f 100644 --- a/src/json.c +++ b/src/json.c @@ -2667,8 +2667,12 @@ static char *jsonBadPathError( ){ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); if( ctx==0 ) return zMsg; - sqlite3_result_error(ctx, zMsg, -1); - sqlite3_free(zMsg); + if( zMsg ){ + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + }else{ + sqlite3_result_error_nomem(ctx); + } return 0; } From 16e8a5b2f35450711d9c8bac8326ca79718d86e0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 23:30:59 +0000 Subject: [PATCH 312/347] Avoid problems when the path argument to json_tree() contains embedded U+0000 characters. FossilOrigin-Name: 9f055091af01a5dddba1a7e9868ad030c8f206237e1569215cb161e53e54aa71 --- manifest | 12 +++---- manifest.uuid | 2 +- src/json.c | 94 ++++++++++++++------------------------------------- 3 files changed, 32 insertions(+), 76 deletions(-) diff --git a/manifest b/manifest index 31e68565bc..fb3e811b9c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sthat\sOOM\sconditions\sin\sthe\sgeneration\sof\sthe\s"bad\sJSON\spath"\serror\nmessage\sresult\sin\san\sSQLITE_NOMEM\serror. -D 2023-12-03T20:11:35.681 +C Avoid\sproblems\swhen\sthe\spath\sargument\sto\sjson_tree()\scontains\sembedded\sU+0000\ncharacters. +D 2023-12-03T23:30:59.212 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6d92343776fe32e9d703d6e13c1d1883d75e1fd17e081159dd257b21a859f5f9 +F src/json.c 6ac93a6cc3cbfa8be1b4058c7aa335ede339a36b65be5fdde0593bd428ee74cc F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 4d6a9a217df6792b41766b774fb0c0553b45f9104c26a0955bf4a30862d7d7bf -R 1defa3c713a22ae4567d6d3fb033b34d +P aa0e02b5c26a2ef3d6216a0ed8bc01382be43173485f898cb63f2a8c559f2e74 +R 04d47333f96a5f7bc80310aa6a2b6c34 U drh -Z e3833547911441ddc3c3cd95bc32393c +Z 051e2555795818c726068d0b4b3820ce # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c9937a03fa..a0e2cc933f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aa0e02b5c26a2ef3d6216a0ed8bc01382be43173485f898cb63f2a8c559f2e74 \ No newline at end of file +9f055091af01a5dddba1a7e9868ad030c8f206237e1569215cb161e53e54aa71 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 9fcfd3e10f..f7e1591ad2 100644 --- a/src/json.c +++ b/src/json.c @@ -3082,59 +3082,6 @@ static void jsonbTest2( ** Scalar SQL function implementations ****************************************************************************/ -/* SQL Function: jsonb(JSON) -** -** Convert the input argument into JSONB (the SQLite binary encoding of -** JSON). -** -** If the input is TEXT, or NUMERIC, try to parse it as JSON. If the fails, -** raise an error. Otherwise, return the resulting BLOB value. -** -** If the input is a BLOB, check to see if the input is a plausible -** JSONB. If it is, return it unchanged. Raise an error if it is not. -** Note that there could be internal inconsistencies in the BLOB - this -** routine does not do a full byte-for-byte validity check of the -** JSON blob. -** -** If the input is NULL, return NULL. -*/ -static void jsonbFunc( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - JsonParse *pParse; - int nJson; - const char *zJson; - JsonParse x; - UNUSED_PARAMETER(argc); - - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - /* No-op */ - }else if( jsonFuncArgMightBeBinary(argv[0]) ){ - sqlite3_result_value(ctx, argv[0]); - }else{ - zJson = (const char*)sqlite3_value_text(argv[0]); - if( zJson==0 ) return; - nJson = sqlite3_value_bytes(argv[0]); - pParse = &x; - memset(&x, 0, sizeof(x)); - x.zJson = (char*)zJson; - x.nJson = nJson; - if( jsonConvertTextToBlob(pParse, ctx) ){ - sqlite3_result_error(ctx, "malformed JSON", -1); - }else{ - assert( pParse->nBlobAlloc>0 ); - assert( !pParse->bReadOnly ); - sqlite3_result_blob(ctx, pParse->aBlob, pParse->nBlob, sqlite3_free); - pParse->aBlob = 0; - pParse->nBlob = 0; - pParse->nBlobAlloc = 0; - } - jsonParseReset(pParse); - } -} - /* ** Implementation of the json_quote(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting @@ -3271,9 +3218,10 @@ static void jsonExtractFunc( for(i=1; ipCtx = ctx; z = (const char*)sqlite3_value_text(argv[0]); - n = (u32)sqlite3_value_bytes(argv[0]); + n = z ? sqlite3Strlen30(z) : 0; jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); jsonAppendSqlValue(pStr, argv[1]); @@ -4462,15 +4410,16 @@ static int jsonEachPathLength(JsonEachCursor *p){ if( p->iRowid==0 && p->bRecursive && n>1 ){ if( p->path.zBuf[n-1]==']' ){ do{ - n--; assert( n>0 ); + n--; }while( p->path.zBuf[n]!='[' ); }else{ u32 sz = 0; jsonbPayloadSize(&p->sParse, p->i, &sz); if( p->path.zBuf[n-1]=='"' ) sz += 2; + assert( szpath.zBuf[n]!='.' ){ + while( p->path.zBuf[n]!='.' && ALWAYS(n>0) ){ n--; assert( n>0 ); } @@ -4666,11 +4615,15 @@ static int jsonEachFilter( if( idxNum==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; - if( jsonFuncArgMightBeBinary(argv[0]) ){ - p->sParse.nBlob = sqlite3_value_bytes(argv[0]); - p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); - if( p->sParse.aBlob==0 ){ - return SQLITE_NOMEM; + if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + if( jsonFuncArgMightBeBinary(argv[0]) ){ + p->sParse.nBlob = sqlite3_value_bytes(argv[0]); + p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); + if( p->sParse.aBlob==0 ){ + return SQLITE_NOMEM; + } + }else{ + goto json_each_malformed_input; } }else{ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); @@ -4683,10 +4636,7 @@ static int jsonEachFilter( if( p->sParse.oom ){ return SQLITE_NOMEM; } - sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); - jsonEachCursorReset(p); - return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + goto json_each_malformed_input; } } if( idxNum==3 ){ @@ -4698,7 +4648,7 @@ static int jsonEachFilter( jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } - p->nRoot = sqlite3_value_bytes(argv[1]); + p->nRoot = sqlite3Strlen30(zRoot); if( zRoot[1]==0 ){ i = p->i = 0; p->eType = 0; @@ -4747,6 +4697,12 @@ static int jsonEachFilter( p->aParent[0].iValue = i; } return SQLITE_OK; + +json_each_malformed_input: + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } /* The methods of the json_each virtual table */ @@ -4822,7 +4778,7 @@ void sqlite3RegisterJsonFunctions(void){ /* Number of arguments ---, | | | | ,--- Flags */ /* | | | | | | */ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), - JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonbFunc), + JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), From 9af45dc4826dd6acb24b11759346bf43a3cf4503 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sun, 3 Dec 2023 23:38:24 +0000 Subject: [PATCH 313/347] Remove dead code. Improved reporting of errors in JSON inputs. FossilOrigin-Name: 2eaa738e6b5c1b67b3e57c868d9c3a30eea38a0b3b8b02482f06d57a45b10921 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 +--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index fb3e811b9c..6b01ad342f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sproblems\swhen\sthe\spath\sargument\sto\sjson_tree()\scontains\sembedded\sU+0000\ncharacters. -D 2023-12-03T23:30:59.212 +C Remove\sdead\scode.\s\sImproved\sreporting\sof\serrors\sin\sJSON\sinputs. +D 2023-12-03T23:38:24.696 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 6ac93a6cc3cbfa8be1b4058c7aa335ede339a36b65be5fdde0593bd428ee74cc +F src/json.c 2f824bf3cb7f6018476fef922afd13aaa5fbe063ded53cf556d9542c5e81a95c F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 aa0e02b5c26a2ef3d6216a0ed8bc01382be43173485f898cb63f2a8c559f2e74 -R 04d47333f96a5f7bc80310aa6a2b6c34 +P 9f055091af01a5dddba1a7e9868ad030c8f206237e1569215cb161e53e54aa71 +R 37598996ac6084b4bcb164d59f053e89 U drh -Z 051e2555795818c726068d0b4b3820ce +Z 62d813bb1b1f385603ccfef947a3746b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a0e2cc933f..dc5d8dab10 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9f055091af01a5dddba1a7e9868ad030c8f206237e1569215cb161e53e54aa71 \ No newline at end of file +2eaa738e6b5c1b67b3e57c868d9c3a30eea38a0b3b8b02482f06d57a45b10921 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f7e1591ad2..1e88b51743 100644 --- a/src/json.c +++ b/src/json.c @@ -1729,6 +1729,7 @@ static int jsonConvertTextToBlob( if( zJson[i] ){ i += json5Whitespace(&zJson[i]); if( zJson[i] ){ + if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1); jsonParseReset(pParse); return 1; } @@ -2801,9 +2802,6 @@ rebuild_from_cache: p->hasNonstd = pFromCache->hasNonstd; jsonParseFree(pFromCache); return p; - }else{ - jsonParseFree(pFromCache); - pFromCache = 0; } if( eType==SQLITE_BLOB ){ u32 n, sz = 0; From b7fd951be4b5ca926c8daa787d5a627c3772df3e Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 00:31:58 +0000 Subject: [PATCH 314/347] Back off on the use of strlen() for situations where sqlite3_value_bytes() will work as well, for performance. FossilOrigin-Name: 79fb54fbb8b9c30f47cdbd437d24a21542716241e822749e5e28c9fbc449bfa8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 6b01ad342f..a102fa7874 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sdead\scode.\s\sImproved\sreporting\sof\serrors\sin\sJSON\sinputs. -D 2023-12-03T23:38:24.696 +C Back\soff\son\sthe\suse\sof\sstrlen()\sfor\ssituations\swhere\ssqlite3_value_bytes()\nwill\swork\sas\swell,\sfor\sperformance. +D 2023-12-04T00:31:58.791 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 2f824bf3cb7f6018476fef922afd13aaa5fbe063ded53cf556d9542c5e81a95c +F src/json.c 4cf9581f5d58cac15136c40dc359910eda0bac0508173ac1e2c2d988c7b9f310 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 9f055091af01a5dddba1a7e9868ad030c8f206237e1569215cb161e53e54aa71 -R 37598996ac6084b4bcb164d59f053e89 +P 2eaa738e6b5c1b67b3e57c868d9c3a30eea38a0b3b8b02482f06d57a45b10921 +R 38b2727d0c66c3371d75d1e0b8d86ea5 U drh -Z 62d813bb1b1f385603ccfef947a3746b +Z 2acd0d3ecb5d5f83c97261113a2445b2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dc5d8dab10..965abfdee1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2eaa738e6b5c1b67b3e57c868d9c3a30eea38a0b3b8b02482f06d57a45b10921 \ No newline at end of file +79fb54fbb8b9c30f47cdbd437d24a21542716241e822749e5e28c9fbc449bfa8 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 1e88b51743..f5eef834d0 100644 --- a/src/json.c +++ b/src/json.c @@ -3579,7 +3579,7 @@ static void jsonObjectFunc( } jsonAppendSeparator(&jx); z = (const char*)sqlite3_value_text(argv[i]); - n = z!=0 ? sqlite3Strlen30(z) : 0; + n = sqlite3_value_bytes(argv[i]); jsonAppendString(&jx, z, n); jsonAppendChar(&jx, ':'); jsonAppendSqlValue(&jx, argv[i+1]); @@ -4099,7 +4099,7 @@ static void jsonObjectStep( } pStr->pCtx = ctx; z = (const char*)sqlite3_value_text(argv[0]); - n = z ? sqlite3Strlen30(z) : 0; + n = sqlite3Strlen30(z); jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); jsonAppendSqlValue(pStr, argv[1]); From 2ff73a5f3e6eef0c1cc5423f7d7b41db1f435378 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 01:14:23 +0000 Subject: [PATCH 315/347] Better pre-scan size estimations for objects in the JSON parser resulting in fewer reallocations and memmove operations. FossilOrigin-Name: 526b27f90897f5e35dfff7257daf6c4ce4798d649b09b8aecfb02df0449e3c51 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index a102fa7874..f5a4a19dce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\soff\son\sthe\suse\sof\sstrlen()\sfor\ssituations\swhere\ssqlite3_value_bytes()\nwill\swork\sas\swell,\sfor\sperformance. -D 2023-12-04T00:31:58.791 +C Better\spre-scan\ssize\sestimations\sfor\sobjects\sin\sthe\sJSON\sparser\sresulting\nin\sfewer\sreallocations\sand\smemmove\soperations. +D 2023-12-04T01:14:23.183 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4cf9581f5d58cac15136c40dc359910eda0bac0508173ac1e2c2d988c7b9f310 +F src/json.c fe5c8d7494ed56126553dbcb5c970dcbbadddfd56871e08b905b8d7fb9903417 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 2eaa738e6b5c1b67b3e57c868d9c3a30eea38a0b3b8b02482f06d57a45b10921 -R 38b2727d0c66c3371d75d1e0b8d86ea5 +P 79fb54fbb8b9c30f47cdbd437d24a21542716241e822749e5e28c9fbc449bfa8 +R dfc9eac13900dae64608fce7f4f645e4 U drh -Z 2acd0d3ecb5d5f83c97261113a2445b2 +Z 0536405657c5f5cc41f38cd4401ae262 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 965abfdee1..deb93d36ac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -79fb54fbb8b9c30f47cdbd437d24a21542716241e822749e5e28c9fbc449bfa8 \ No newline at end of file +526b27f90897f5e35dfff7257daf6c4ce4798d649b09b8aecfb02df0449e3c51 \ No newline at end of file diff --git a/src/json.c b/src/json.c index f5eef834d0..598b290510 100644 --- a/src/json.c +++ b/src/json.c @@ -1268,7 +1268,7 @@ json_parse_restart: case '{': { /* Parse object */ iThis = pParse->nBlob; - jsonBlobAppendNode(pParse, JSONB_OBJECT, (pParse->nJson-i)*2, 0); + jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0); if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; From dc138cb186b24784128f5fc0452c22de0b3b5c9d Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 13:12:45 +0000 Subject: [PATCH 316/347] Repair issues and inefficiencies found during testing. FossilOrigin-Name: ae973cb1515f9d76409c92a2ca2ffd6b71f32b0b490a4886770e7c1b90f12611 --- manifest | 12 ++--- manifest.uuid | 2 +- src/json.c | 121 +++++++++++++------------------------------------- 3 files changed, 39 insertions(+), 96 deletions(-) diff --git a/manifest b/manifest index f5a4a19dce..9ee6f8d2ea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Better\spre-scan\ssize\sestimations\sfor\sobjects\sin\sthe\sJSON\sparser\sresulting\nin\sfewer\sreallocations\sand\smemmove\soperations. -D 2023-12-04T01:14:23.183 +C Repair\sissues\sand\sinefficiencies\sfound\sduring\stesting. +D 2023-12-04T13:12:45.155 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fe5c8d7494ed56126553dbcb5c970dcbbadddfd56871e08b905b8d7fb9903417 +F src/json.c fdab711d10347af4e70bbdc4dc3aca516a4ef0e6c4388cc70335ab9201bdf7eb F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 79fb54fbb8b9c30f47cdbd437d24a21542716241e822749e5e28c9fbc449bfa8 -R dfc9eac13900dae64608fce7f4f645e4 +P 526b27f90897f5e35dfff7257daf6c4ce4798d649b09b8aecfb02df0449e3c51 +R a8fc2b32fcf7c8e7f15da5575d63ccf0 U drh -Z 0536405657c5f5cc41f38cd4401ae262 +Z 9f46540ea8d4faeba010e6034ffe6d00 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index deb93d36ac..b7d0da89ae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -526b27f90897f5e35dfff7257daf6c4ce4798d649b09b8aecfb02df0449e3c51 \ No newline at end of file +ae973cb1515f9d76409c92a2ca2ffd6b71f32b0b490a4886770e7c1b90f12611 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 598b290510..6df07b0e66 100644 --- a/src/json.c +++ b/src/json.c @@ -570,32 +570,16 @@ static void jsonAppendChar(JsonString *p, char c){ } /* Make sure there is a zero terminator on p->zBuf[] -*/ -static void jsonStringTerminate(JsonString *p){ - jsonAppendChar(p, 0); - p->nUsed--; -} - -/* Try to force the string to be a zero-terminated RCStr string. In other -** words, make sure it is not still using the internal zSpace[] static -** buffer. ** ** Return true on success. Return false if an OOM prevents this ** from happening. */ -static int jsonForceRCStr(JsonString *p){ +static int jsonStringTerminate(JsonString *p){ jsonAppendChar(p, 0); - if( p->eErr ) return 0; p->nUsed--; - if( p->bStatic==0 ) return 1; - p->nAlloc = 0; - p->nUsed++; - jsonStringGrow(p, p->nUsed); - p->nUsed--; - return p->bStatic==0; + return p->eErr==0; } - /* Append a comma separator to the output buffer, if the previous ** character is not '[' or '{'. */ @@ -751,7 +735,7 @@ static void jsonReturnString( }else if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); - }else if( jsonForceRCStr(p) ){ + }else if( jsonStringTerminate(p) ){ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ int rc; pParse->zJson = sqlite3RCStrRef(p->zBuf); @@ -1071,30 +1055,27 @@ static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ return 1; } - -/* Expand pParse->aBlob and append N bytes. -** -** Return the number of errors. +/* Expand pParse->aBlob and append one bytes. */ -static SQLITE_NOINLINE int jsonBlobExpandAndAppend( +static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte( JsonParse *pParse, - const u8 *aData, - u32 N + u8 c ){ - if( jsonBlobExpand(pParse, pParse->nBlob+N) ) return 1; - memcpy(&pParse->aBlob[pParse->nBlob], aData, N); - pParse->nBlob += N; - return 0; + jsonBlobExpand(pParse, pParse->nBlob+1); + if( pParse->oom==0 ){ + assert( pParse->nBlob+1<=pParse->nBlobAlloc ); + pParse->aBlob[pParse->nBlob++] = c; + } } -/* Append a single character. Return 1 if an error occurs. +/* Append a single character. */ -static int jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ +static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ if( pParse->nBlob >= pParse->nBlobAlloc ){ - return jsonBlobExpandAndAppend(pParse, &c, 1); + jsonBlobExpandAndAppendOneByte(pParse, c); + }else{ + pParse->aBlob[pParse->nBlob++] = c; } - pParse->aBlob[pParse->nBlob++] = c; - return 0; } /* Slow version of jsonBlobAppendNode() that first resizes the @@ -1937,11 +1918,12 @@ static u32 jsonXlateBlobToText( sz2--; continue; } - if( sz2<2 ){ - if( sz2>0 ) pOut->eErr |= JSTRING_MALFORMED; - if( sz2==0 ) break; - } assert( zIn[0]=='\\' ); + assert( sz2>=1 ); + if( sz2<2 ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } switch( (u8)zIn[1] ){ case '\'': jsonAppendChar(pOut, '\''); @@ -1950,9 +1932,9 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, "\\u0009", 6); break; case 'x': - if( sz2<2 ){ + if( sz2<4 ){ pOut->eErr |= JSTRING_MALFORMED; - sz2 = 0; + sz2 = 2; break; } jsonAppendRawNZ(pOut, "\\u00", 4); @@ -1980,7 +1962,7 @@ static u32 jsonXlateBlobToText( || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) ){ pOut->eErr |= JSTRING_MALFORMED; - k = sz2; + sz2 = 2; break; } zIn += 2; @@ -1990,11 +1972,7 @@ static u32 jsonXlateBlobToText( jsonAppendRawNZ(pOut, zIn, 2); break; } - if( sz2<2 ){ - sz2 = 0; - pOut->eErr |= JSTRING_MALFORMED; - break; - } + assert( sz2>=2 ); zIn += 2; sz2 -= 2; } @@ -2051,11 +2029,11 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ const u8 *aBlob; int nBlob; JsonParse s; - if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; + assert( sqlite3_value_type(pJson)==SQLITE_BLOB ); aBlob = sqlite3_value_blob(pJson); nBlob = sqlite3_value_bytes(pJson); if( nBlob<1 ) return 0; - if( aBlob==0 || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; + if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; memset(&s, 0, sizeof(s)); s.aBlob = (u8*)aBlob; s.nBlob = nBlob; @@ -2302,13 +2280,13 @@ static u32 jsonLookupStep( assert( !pParse->oom ); nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); - if( !pParse->oom ){ + if( ALWAYS(!pParse->oom) ){ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); k += nKey; memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); - if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot); } } jsonParseReset(&v); @@ -2394,7 +2372,7 @@ static void jsonReturnTextJsonFromBlob( JsonParse x; JsonString s; - if( aBlob==0 ) return; + if( NEVER(aBlob==0) ) return; memset(&x, 0, sizeof(x)); x.aBlob = (u8*)aBlob; x.nBlob = nBlob; @@ -2423,7 +2401,7 @@ static void jsonReturnFromBlob( sqlite3 *db = sqlite3_context_db_handle(pCtx); n = jsonbPayloadSize(pParse, i, &sz); - if( n==0 ) return; + if( NEVER(n==0) ) return; switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { sqlite3_result_null(pCtx); @@ -2605,7 +2583,7 @@ static int jsonFunctionArgToBlob( static u8 aNull[] = { 0x00 }; memset(pParse, 0, sizeof(pParse[0])); switch( eType ){ - case SQLITE_NULL: { + default: { pParse->aBlob = aNull; pParse->nBlob = 1; return 0; @@ -3041,39 +3019,6 @@ static void jsonParseFunc( jsonShowParse(p); jsonParseFree(p); } - -/* -** The json_test1(JSON) function return true (1) if the input is JSON -** text generated by another json function. It returns (0) if the input -** is not known to be JSON. -*/ -static void jsonTest1Func( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - UNUSED_PARAMETER(argc); - sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); -} - -/* SQL Function: jsonb_test2(BLOB_JSON) -** -** Render BLOB_JSON back into text. -** Development testing only. -*/ -static void jsonbTest2( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - const u8 *aBlob; - int nBlob; - UNUSED_PARAMETER(argc); - - aBlob = (const u8*)sqlite3_value_blob(argv[0]); - nBlob = sqlite3_value_bytes(argv[0]); - jsonReturnTextJsonFromBlob(ctx, aBlob, nBlob); -} #endif /* SQLITE_DEBUG */ /**************************************************************************** @@ -4805,8 +4750,6 @@ void sqlite3RegisterJsonFunctions(void){ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), #if SQLITE_DEBUG JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), - JFUNCTION(json_test1, 1,1,0, 1,0,0, jsonTest1Func), - JFUNCTION(jsonb_test2, 1,1,0, 0,1,0, jsonbTest2), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, From 43b4864a98da1537203eab92e5112d88451884f4 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 15:08:21 +0000 Subject: [PATCH 317/347] Add tests for using tokendata=1 and contentless_delete=1 together. FossilOrigin-Name: a2506b8c9718054912270055638204753c4156bbc115e55194e6df9d7e76cb10 --- ext/fts5/test/fts5origintext5.test | 135 +++++++++++++++++++++++++++++ manifest | 11 +-- manifest.uuid | 2 +- 3 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 ext/fts5/test/fts5origintext5.test diff --git a/ext/fts5/test/fts5origintext5.test b/ext/fts5/test/fts5origintext5.test new file mode 100644 index 0000000000..a48dfbb3ab --- /dev/null +++ b/ext/fts5/test/fts5origintext5.test @@ -0,0 +1,135 @@ +# 2023 Dec 04 +# +# 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. +# +#*********************************************************************** +# +# Tests for tables that use both tokendata=1 and contentless_delete=1. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5origintext + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +# Return a random integer between 0 and n-1. +# +proc random {n} { expr {abs(int(rand()*$n))} } + +# Select an element of the list passed as the only argument at random and +# return it. +# +proc select_one {list} { + set n [llength $list] + lindex $list [random $n] +} + +# Given a term that consists entirely of alphabet characters, return all +# permutations of the term using upper and lower case characters. e.g. +# +# "abc" -> {CBA cBA CbA cbA CBa cBa Cba cba} +# +proc casify {term {lRet {{}}}} { + if {$term==""} { return $lRet } + set t [string range $term 1 end] + set f1 [string toupper [string range $term 0 0]] + set f2 [string tolower [string range $term 0 0]] + set ret [list] + foreach x $lRet { + lappend ret "$x$f1" + lappend ret "$x$f2" + } + return [casify $t $ret] +} + +proc vocab {} { + list abc def ghi jkl mno pqr stu vwx yza +} + +# Return a random 3 letter term. +# +proc term {} { + if {[info exists ::expanded_vocab]==0} { + foreach v [vocab] { lappend ::expanded_vocab {*}[casify $v] } + } + + select_one $::expanded_vocab +} + +# Return a document - between 3 and 6 terms. +# +proc document {} { + set nTerm [expr [random 3] + 3] + set doc "" + for {set ii 0} {$ii < $nTerm} {incr ii} { + lappend doc [term] + } + set doc +} +db func document document + +#------------------------------------------------------------------------- + +set NDOC 200 +set NLOOP 100 + + +sqlite3_fts5_register_origintext db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, tokenize="origintext unicode61", contentless_delete=1, content=, + tokendata=1 + ); + + CREATE TABLE ctrl(id INTEGER PRIMARY KEY, x TEXT); + INSERT INTO ft(ft, rank) VALUES('pgsz', 64); +} +do_test 1.1 { + for {set ii 0} {$ii < $NDOC} {incr ii} { + set doc [document] + execsql { + INSERT INTO ft(rowid, x) VALUES($ii, $doc); + INSERT INTO ctrl(id, x) VALUES($ii, $doc); + } + } +} {} + +proc do_all_vocab_test {tn} { + foreach ::v [vocab] { + set answer [execsql {SELECT id FROM ctrl WHERE x LIKE '%' || $::v || '%'}] + do_execsql_test $tn.$::v { + SELECT rowid FROM ft($::v) + } $answer + } +} + +do_all_vocab_test 1.2 + +for {set ii 0} {$ii < $NLOOP} {incr ii} { + set lRowid [execsql { SELECT id FROM ctrl WHERE random() % 2 }] + foreach r $lRowid { + execsql { DELETE FROM ft WHERE rowid = $r } + execsql { DELETE FROM ctrl WHERE rowid = $r } + + set doc [document] + execsql { INSERT INTO ft(rowid, x) VALUES($r, $doc) } + execsql { INSERT INTO ctrl(id, x) VALUES($r, $doc) } + } + do_all_vocab_test 1.3.$ii +} + + + + + +finish_test + diff --git a/manifest b/manifest index 2353c55e4c..61b1c976fd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\svarious\scompiler\swarnings\sand\sother\sproblems\swith\sthe\snew\scode\son\sthis\sbranch. -D 2023-12-02T20:35:04.768 +C Add\stests\sfor\susing\stokendata=1\sand\scontentless_delete=1\stogether. +D 2023-12-04T15:08:21.125 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -194,6 +194,7 @@ F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0 F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 +F ext/fts5/test/fts5origintext5.test 5b9fa1b7d2f8c5f933076000c30aea5b104c00c3f1b767334b87b76d46492e59 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2149,8 +2150,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 e0175d07e4094db5ea4b0378a5ff480dafb6ba9da86a113fa767c4c89c3c866f -R a72f98879c56fe0b2489dc56b6289649 +P 3a623cfa173b4035c759cb84985d11d8727053beb383648503987d6ab15c0ef0 +R 977c39d056cdc1226d7f3117e124e785 U dan -Z 6a8664529c8f348c40cce12fb229aa10 +Z 8168409535c63203870031d6c1a58cc5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b4349493de..0187da7a6e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3a623cfa173b4035c759cb84985d11d8727053beb383648503987d6ab15c0ef0 \ No newline at end of file +a2506b8c9718054912270055638204753c4156bbc115e55194e6df9d7e76cb10 \ No newline at end of file From 732fb64ad38beb5f79742b25d524916d8dfae2ac Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 15:22:42 +0000 Subject: [PATCH 318/347] Two new NEVER macros. FossilOrigin-Name: 52632c92cb06faf0e804654b3490fd6c199521107bd30c8fcbc3a2a5a488098f --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 9ee6f8d2ea..17837b4093 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Repair\sissues\sand\sinefficiencies\sfound\sduring\stesting. -D 2023-12-04T13:12:45.155 +C Two\snew\sNEVER\smacros. +D 2023-12-04T15:22:42.529 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c fdab711d10347af4e70bbdc4dc3aca516a4ef0e6c4388cc70335ab9201bdf7eb +F src/json.c f9628f4dc6cc2ddf26b5edb1c275e80db886634003cead23ec8c7c8db8b4fd04 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 526b27f90897f5e35dfff7257daf6c4ce4798d649b09b8aecfb02df0449e3c51 -R a8fc2b32fcf7c8e7f15da5575d63ccf0 +P ae973cb1515f9d76409c92a2ca2ffd6b71f32b0b490a4886770e7c1b90f12611 +R 34754ed91bc896045fb5083333f2e977 U drh -Z 9f46540ea8d4faeba010e6034ffe6d00 +Z 8fd37e932957842ac050d54bc05491f1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b7d0da89ae..7bf52474de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae973cb1515f9d76409c92a2ca2ffd6b71f32b0b490a4886770e7c1b90f12611 \ No newline at end of file +52632c92cb06faf0e804654b3490fd6c199521107bd30c8fcbc3a2a5a488098f \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6df07b0e66..feab5f4dfe 100644 --- a/src/json.c +++ b/src/json.c @@ -2788,7 +2788,7 @@ rebuild_from_cache: if( p->nBlob==0 ){ goto json_pfa_malformed; } - if( p->aBlob==0 ){ + if( NEVER(p->aBlob==0) ){ goto json_pfa_oom; } if( (p->aBlob[0] & 0x0f)>JSONB_OBJECT ){ @@ -2810,7 +2810,7 @@ rebuild_from_cache: p->zJson = (char*)sqlite3_value_text(pArg); p->nJson = sqlite3_value_bytes(pArg); if( p->nJson==0 ) goto json_pfa_malformed; - if( p->zJson==0 ) goto json_pfa_oom; + if( NEVER(p->zJson==0) ) goto json_pfa_oom; if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ if( flgs & JSON_KEEPERROR ){ p->nErr = 1; From 99c41692f1297f2072345f895207ce5ecc881e3b Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 16:01:39 +0000 Subject: [PATCH 319/347] Remove reachable ALWAYS and NEVER macros. FossilOrigin-Name: f601de3eeabd85993c1f5ee96b62de6fdabbeae2fe8950e00d08feb48d42c498 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 17837b4093..71f63d320e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Two\snew\sNEVER\smacros. -D 2023-12-04T15:22:42.529 +C Remove\sreachable\sALWAYS\sand\sNEVER\smacros. +D 2023-12-04T16:01:39.513 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c f9628f4dc6cc2ddf26b5edb1c275e80db886634003cead23ec8c7c8db8b4fd04 +F src/json.c 486643d34e35a9d33c88ba13810119eb08edf8f7585378379b2880ab2f20884f F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 ae973cb1515f9d76409c92a2ca2ffd6b71f32b0b490a4886770e7c1b90f12611 -R 34754ed91bc896045fb5083333f2e977 +P 52632c92cb06faf0e804654b3490fd6c199521107bd30c8fcbc3a2a5a488098f +R e34d44ceb9e1b582fec5d492940f4291 U drh -Z 8fd37e932957842ac050d54bc05491f1 +Z 6c24af6356a01e718b1f63d8aebe720b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7bf52474de..feb883af6c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -52632c92cb06faf0e804654b3490fd6c199521107bd30c8fcbc3a2a5a488098f \ No newline at end of file +f601de3eeabd85993c1f5ee96b62de6fdabbeae2fe8950e00d08feb48d42c498 \ No newline at end of file diff --git a/src/json.c b/src/json.c index feab5f4dfe..10470e62df 100644 --- a/src/json.c +++ b/src/json.c @@ -2280,7 +2280,7 @@ static u32 jsonLookupStep( assert( !pParse->oom ); nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); - if( ALWAYS(!pParse->oom) ){ + if( !pParse->oom ){ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); @@ -2401,7 +2401,7 @@ static void jsonReturnFromBlob( sqlite3 *db = sqlite3_context_db_handle(pCtx); n = jsonbPayloadSize(pParse, i, &sz); - if( NEVER(n==0) ) return; + if( n==0 ) return; switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { sqlite3_result_null(pCtx); From 9d373ca1c5eee2771e97af122f23264ba2f92576 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 17:05:37 +0000 Subject: [PATCH 320/347] Fix bug in xInstToken() causing the wrong token to be returned. FossilOrigin-Name: da78d07e77cbc783fbc725758911c230fd6a1c1885d9576125de955dcc2bd37f --- ext/fts5/fts5_index.c | 10 +++++-- ext/fts5/test/fts5origintext5.test | 47 ++++++++++++++++++++++++++---- manifest | 14 ++++----- manifest.uuid | 2 +- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index df08ca2127..095b945838 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -6607,6 +6607,7 @@ struct Fts5TokenDataIter { Fts5TokenDataMap *aMap; Fts5PoslistReader *aPoslistReader; + int *aPoslistToIter; Fts5Iter *apIter[1]; }; @@ -6794,16 +6795,19 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ /* Allocate array of iterators if they are not already allocated. */ if( pT->aPoslistReader==0 ){ - pT->aPoslistReader = sqlite3Fts5MallocZero( - &pIter->pIndex->rc, sizeof(Fts5PoslistReader) * pT->nIter + int nByte = pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)); + pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( + &pIter->pIndex->rc, nByte ); if( pT->aPoslistReader==0 ) return; + pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; } /* Populate an iterator for each poslist that will be merged */ for(ii=0; iinIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( iRowid==p->base.iRowid ){ + pT->aPoslistToIter[nReader] = ii; sqlite3Fts5PoslistReaderInit( p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] ); @@ -6855,7 +6859,7 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ if( eDetail==FTS5_DETAIL_FULL ){ pT->aMap[pT->nMap].iPos = iMinPos; - pT->aMap[pT->nMap].iIter = iMin; + pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; pT->aMap[pT->nMap].iRowid = iRowid; pT->nMap++; } diff --git a/ext/fts5/test/fts5origintext5.test b/ext/fts5/test/fts5origintext5.test index a48dfbb3ab..9421758f88 100644 --- a/ext/fts5/test/fts5origintext5.test +++ b/ext/fts5/test/fts5origintext5.test @@ -65,10 +65,10 @@ proc term {} { select_one $::expanded_vocab } -# Return a document - between 3 and 6 terms. +# Return a document - between 3 and 10 terms. # proc document {} { - set nTerm [expr [random 3] + 3] + set nTerm [expr [random 3] + 7] set doc "" for {set ii 0} {$ii < $nTerm} {incr ii} { lappend doc [term] @@ -82,11 +82,39 @@ db func document document set NDOC 200 set NLOOP 100 - sqlite3_fts5_register_origintext db + +proc tokens {cmd} { + for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { + set txt [$cmd xInstToken $iTok 0] + if {$txt==""} break + set txt [string map [list "\0" "."] $txt] + lappend ret $txt + } + set ret +} +sqlite3_fts5_create_function db tokens tokens + +proc ctrl_tokens {doc term} { + set ret [list] + set term [string tolower $term] + foreach a $doc { + if {[string tolower $a]==$term} { + if {$a==$term} { + lappend ret $a + } else { + lappend ret [string tolower $a].$a + } + } + } + set ret +} +db func ctrl_tokens ctrl_tokens + + do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( - x, tokenize="origintext unicode61", contentless_delete=1, content=, + x, tokenize="origintext unicode61", tokendata=1 ); @@ -105,13 +133,20 @@ do_test 1.1 { proc do_all_vocab_test {tn} { foreach ::v [vocab] { - set answer [execsql {SELECT id FROM ctrl WHERE x LIKE '%' || $::v || '%'}] + set answer [execsql { + SELECT id, ctrl_tokens(x, $::v) FROM ctrl WHERE x LIKE '%' || $::v || '%' + }] do_execsql_test $tn.$::v { - SELECT rowid FROM ft($::v) + SELECT rowid, tokens(ft) FROM ft($::v) } $answer } } +#execsql_pp { SELECT * FROM ctrl } +#execsql_pp { SELECT * FROM ft } +#fts5_aux_test_functions db +#execsql_pp { SELECT rowid, tokens(ft), fts5_test_poslist(ft) FROM ft('ghi'); } + do_all_vocab_test 1.2 for {set ii 0} {$ii < $NLOOP} {incr ii} { diff --git a/manifest b/manifest index 61b1c976fd..4ecc82d692 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests\sfor\susing\stokendata=1\sand\scontentless_delete=1\stogether. -D 2023-12-04T15:08:21.125 +C Fix\sbug\sin\sxInstToken()\scausing\sthe\swrong\stoken\sto\sbe\sreturned. +D 2023-12-04T17:05:37.721 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c 5619c3fab45a78eb5ed3021e3b40ec3b435ef3669293e8700354aa8dd3e6c796 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c b31bf4f0fb51a15cc1aa54c2f337197740f4f8898347266781ca6970ca751302 +F ext/fts5/fts5_index.c 072666814be04485445c07e0e75362d13245cef6c66e07aa2060c532190d0c10 F ext/fts5/fts5_main.c 075995302198fe6f591fdbbedd415dfac564a9bfc20aea81e6fa0503b2d94af0 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -194,7 +194,7 @@ F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0 F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 -F ext/fts5/test/fts5origintext5.test 5b9fa1b7d2f8c5f933076000c30aea5b104c00c3f1b767334b87b76d46492e59 +F ext/fts5/test/fts5origintext5.test f4377b67debb10e3731030ce51245b9843ffb31f85725615b3b8820bd5912702 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2150,8 +2150,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 3a623cfa173b4035c759cb84985d11d8727053beb383648503987d6ab15c0ef0 -R 977c39d056cdc1226d7f3117e124e785 +P a2506b8c9718054912270055638204753c4156bbc115e55194e6df9d7e76cb10 +R 135aeb3f5184e1f0131c446ddc8eab37 U dan -Z 8168409535c63203870031d6c1a58cc5 +Z 33215e49f8892bbcd31004428ffced69 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0187da7a6e..60beff1eed 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a2506b8c9718054912270055638204753c4156bbc115e55194e6df9d7e76cb10 \ No newline at end of file +da78d07e77cbc783fbc725758911c230fd6a1c1885d9576125de955dcc2bd37f \ No newline at end of file From 9c794b9bff393410d9efbd4d43cb5540ed534531 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 17:40:28 +0000 Subject: [PATCH 321/347] Continuing simplifications and code cleanup. FossilOrigin-Name: ddf92b5059a9106753fd18b82ba8daa269a62af947561c460790107b83416f0b --- manifest | 12 +++--- manifest.uuid | 2 +- src/json.c | 109 +++++++++++++++++++++----------------------------- 3 files changed, 53 insertions(+), 70 deletions(-) diff --git a/manifest b/manifest index 71f63d320e..7f065bdc52 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sreachable\sALWAYS\sand\sNEVER\smacros. -D 2023-12-04T16:01:39.513 +C Continuing\ssimplifications\sand\scode\scleanup. +D 2023-12-04T17:40:28.698 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 486643d34e35a9d33c88ba13810119eb08edf8f7585378379b2880ab2f20884f +F src/json.c 0e9fc8f2f2fecb1b4e7904ead93aa98313483ca4fea1d564e252e445f717b226 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 52632c92cb06faf0e804654b3490fd6c199521107bd30c8fcbc3a2a5a488098f -R e34d44ceb9e1b582fec5d492940f4291 +P f601de3eeabd85993c1f5ee96b62de6fdabbeae2fe8950e00d08feb48d42c498 +R c9ebf0dbf5357376ee89626a26211c09 U drh -Z 6c24af6356a01e718b1f63d8aebe720b +Z 2f59464d9d28542ea091e094068881ef # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index feb883af6c..b3d16e9d7b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f601de3eeabd85993c1f5ee96b62de6fdabbeae2fe8950e00d08feb48d42c498 \ No newline at end of file +ddf92b5059a9106753fd18b82ba8daa269a62af947561c460790107b83416f0b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 10470e62df..dc884b9fd3 100644 --- a/src/json.c +++ b/src/json.c @@ -2029,7 +2029,7 @@ static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ const u8 *aBlob; int nBlob; JsonParse s; - assert( sqlite3_value_type(pJson)==SQLITE_BLOB ); + if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; aBlob = sqlite3_value_blob(pJson); nBlob = sqlite3_value_bytes(pJson); if( nBlob<1 ) return 0; @@ -3555,36 +3555,39 @@ static void jsonRemoveFunc( p = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); if( p==0 ) return; for(i=1; ieEdit = JEDIT_DEL; p->delta = 0; rc = jsonLookupStep(p, 0, zPath+1, 0); - if( rc==JSON_LOOKUP_NOTFOUND ) continue; - if( JSON_LOOKUP_ISERROR(rc) ) goto json_remove_patherror; + if( JSON_LOOKUP_ISERROR(rc) ){ + if( rc==JSON_LOOKUP_NOTFOUND ){ + continue; /* No-op */ + }else if( rc==JSON_LOOKUP_PATHERROR ){ + jsonBadPathError(ctx, zPath); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + goto json_remove_done; + } } jsonReturnParse(ctx, p); jsonParseFree(p); return; json_remove_patherror: - jsonParseFree(p); jsonBadPathError(ctx, zPath); - return; -json_remove_return_null: +json_remove_done: jsonParseFree(p); return; } @@ -3657,18 +3660,8 @@ static void jsonTypeFunc( p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==2 ){ - if( sqlite3_value_type(argv[1])==SQLITE_NULL ){ - goto json_type_done; - } - if( sqlite3_value_bytes(argv[1])==0 ){ - jsonBadPathError(ctx, ""); - goto json_type_done; - } zPath = (const char*)sqlite3_value_text(argv[1]); - if( zPath==0 ){ - sqlite3_result_error_nomem(ctx); - goto json_type_done; - } + if( zPath==0 ) goto json_type_done; if( zPath[0]!='$' ){ jsonBadPathError(ctx, zPath); goto json_type_done; @@ -3841,17 +3834,14 @@ static void jsonValidFunc( ** ** If the argument is NULL, return NULL ** -** If the argument is TEXT then try to interpret that text as JSON and +** If the argument is BLOB, do a fast validity check and return non-zero +** if the check fails. The returned value does not indicate where in the +** BLOB the error occurs. +** +** Otherwise interpret the argument is TEXT (even if it is numeric) and ** return the 1-based character position for where the parser first recognized ** that the input was not valid JSON, or return 0 if the input text looks ** ok. JSON-5 extensions are accepted. -** -** If the argument is a BLOB then try to interpret the blob as a JSONB -** and return the 1-based byte offset of the first position that is -** misformatted. Return 0 if the input BLOB seems to be well-formed. -** -** Numeric inputs are converted into text, which is usually valid -** JSON-5, so they should return 0. */ static void jsonErrorFunc( sqlite3_context *ctx, @@ -3863,46 +3853,42 @@ static void jsonErrorFunc( assert( argc==1 ); UNUSED_PARAMETER(argc); - switch( sqlite3_value_type(argv[0]) ){ - case SQLITE_NULL: { - return; + memset(&s, 0, sizeof(s)); + if( jsonFuncArgMightBeBinary(argv[0]) ){ + JsonString out; + jsonStringInit(&out, 0); + s.aBlob = (u8*)sqlite3_value_blob(argv[0]); + s.nBlob = sqlite3_value_bytes(argv[0]); + jsonXlateBlobToText(&s, 0, &out); + if( out.eErr ){ + iErrPos = (out.eErr & JSTRING_MALFORMED)!=0 ? 1 : -1; } - case SQLITE_BLOB: { - if( !jsonFuncArgMightBeBinary(argv[0]) ) iErrPos = 1; - break; - } - default: { - memset(&s, 0, sizeof(s)); - s.zJson = (char*)sqlite3_value_text(argv[0]); - s.nJson = sqlite3_value_bytes(argv[0]); - if( s.nJson==0 ){ - iErrPos = 1; - break; - } - if( s.zJson==0 ){ - sqlite3_result_error_nomem(ctx); - return; - } - if( jsonConvertTextToBlob(&s,0) ){ - u32 k; - if( s.oom ){ - sqlite3_result_error_nomem(ctx); - jsonParseReset(&s); - return; - } + jsonStringReset(&out); + }else{ + s.zJson = (char*)sqlite3_value_text(argv[0]); + if( s.zJson==0 ) return; /* NULL input or OOM */ + s.nJson = sqlite3_value_bytes(argv[0]); + if( jsonConvertTextToBlob(&s,0) ){ + if( s.oom ){ + iErrPos = -1; + }else{ /* Convert byte-offset s.iErr into a character offset */ - for(k=0; ksParse.nBlob = sqlite3_value_bytes(argv[0]); p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); - if( p->sParse.aBlob==0 ){ - return SQLITE_NOMEM; - } }else{ goto json_each_malformed_input; } From 3dfc063705277f68540fc8aa3d55967481ebf042 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 17:45:33 +0000 Subject: [PATCH 322/347] Fix a problem with the xInstCount() API and "ORDER BY rank" queries. FossilOrigin-Name: 317a50563d9e8586fda136e513727241b414e7267d50a06571c8ebd0eae710bc --- ext/fts5/fts5Int.h | 2 +- ext/fts5/fts5_expr.c | 2 +- ext/fts5/fts5_main.c | 3 ++- ext/fts5/test/fts5origintext5.test | 17 ++++++++++++++--- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 4e4385ce2b..9beb26e056 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -787,7 +787,7 @@ int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); -int sqlite3Fts5ExprInstToken(Fts5Expr*, int, int, int, int, const char**, int*); +int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); void sqlite3Fts5ExprClearTokens(Fts5Expr*); /******************************************* diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 6f58cf8735..02d7e78cbe 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3199,6 +3199,7 @@ int sqlite3Fts5ExprQueryToken( */ int sqlite3Fts5ExprInstToken( Fts5Expr *pExpr, + i64 iRowid, int iPhrase, int iCol, int iOff, @@ -3208,7 +3209,6 @@ int sqlite3Fts5ExprInstToken( ){ Fts5ExprPhrase *pPhrase = 0; Fts5IndexIter *pIter = 0; - i64 iRowid = pExpr->pRoot->iRowid; if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ return SQLITE_RANGE; diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index e911b0c0f9..c183f3c579 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -2364,9 +2364,10 @@ static int fts5ApiInstToken( int iPhrase = pCsr->aInst[iIdx*3]; int iCol = pCsr->aInst[iIdx*3 + 1]; int iOff = pCsr->aInst[iIdx*3 + 2]; + i64 iRowid = fts5CursorRowid(pCsr); rc = sqlite3Fts5ExprInstToken( - pCsr->pExpr, iPhrase, iCol, iOff, iToken, ppOut, pnOut + pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut ); } } diff --git a/ext/fts5/test/fts5origintext5.test b/ext/fts5/test/fts5origintext5.test index 9421758f88..b425c582e8 100644 --- a/ext/fts5/test/fts5origintext5.test +++ b/ext/fts5/test/fts5origintext5.test @@ -79,15 +79,17 @@ db func document document #------------------------------------------------------------------------- +expr srand(6) + set NDOC 200 set NLOOP 100 sqlite3_fts5_register_origintext db proc tokens {cmd} { + set ret [list] for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { set txt [$cmd xInstToken $iTok 0] - if {$txt==""} break set txt [string map [list "\0" "."] $txt] lappend ret $txt } @@ -95,6 +97,11 @@ proc tokens {cmd} { } sqlite3_fts5_create_function db tokens tokens +proc rankfunc {cmd} { + $cmd xRowid +} +sqlite3_fts5_create_function db rankfunc rankfunc + proc ctrl_tokens {doc term} { set ret [list] set term [string tolower $term] @@ -114,12 +121,13 @@ db func ctrl_tokens ctrl_tokens do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( - x, tokenize="origintext unicode61", + x, tokenize="origintext unicode61", content=, contentless_delete=1, tokendata=1 ); CREATE TABLE ctrl(id INTEGER PRIMARY KEY, x TEXT); INSERT INTO ft(ft, rank) VALUES('pgsz', 64); + INSERT INTO ft(ft, rank) VALUES('rank', 'rankfunc()'); } do_test 1.1 { for {set ii 0} {$ii < $NDOC} {incr ii} { @@ -136,9 +144,12 @@ proc do_all_vocab_test {tn} { set answer [execsql { SELECT id, ctrl_tokens(x, $::v) FROM ctrl WHERE x LIKE '%' || $::v || '%' }] - do_execsql_test $tn.$::v { + do_execsql_test $tn.$::v.1 { SELECT rowid, tokens(ft) FROM ft($::v) } $answer + do_execsql_test $tn.$::v.2 { + SELECT rowid, tokens(ft) FROM ft($::v) ORDER BY rank + } $answer } } diff --git a/manifest b/manifest index 4ecc82d692..00c355a167 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sbug\sin\sxInstToken()\scausing\sthe\swrong\stoken\sto\sbe\sreturned. -D 2023-12-04T17:05:37.721 +C Fix\sa\sproblem\swith\sthe\sxInstCount()\sAPI\sand\s"ORDER\sBY\srank"\squeries. +D 2023-12-04T17:45:33.714 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -90,14 +90,14 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h ff90acaa97f8e865b66d1177d1b56b8c110fd5548ab5863bab43f055a1d745fe -F ext/fts5/fts5Int.h 1fdbf3d16bdd481fe2ee99927919e4c3db835efae00f8efd7efb5e6a93277459 +F ext/fts5/fts5Int.h defa43c0932265138ee910ca416e6baccf8b774e0f3d610e74be1ab2880e9834 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c 5619c3fab45a78eb5ed3021e3b40ec3b435ef3669293e8700354aa8dd3e6c796 +F ext/fts5/fts5_expr.c aec893108d90cc8eae6801283d38d4e705d8c22aba12717fcc56e1910b8e3b32 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 F ext/fts5/fts5_index.c 072666814be04485445c07e0e75362d13245cef6c66e07aa2060c532190d0c10 -F ext/fts5/fts5_main.c 075995302198fe6f591fdbbedd415dfac564a9bfc20aea81e6fa0503b2d94af0 +F ext/fts5/fts5_main.c 1fbadf63a7381fd68753efdccce19683f587668489b1c5a9c7bf53d6e9b64872 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -194,7 +194,7 @@ F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0 F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 -F ext/fts5/test/fts5origintext5.test f4377b67debb10e3731030ce51245b9843ffb31f85725615b3b8820bd5912702 +F ext/fts5/test/fts5origintext5.test f9dfb005248d764fdc52859308875f680b523a897258a2cc40100f9e2356b5a9 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2150,8 +2150,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 a2506b8c9718054912270055638204753c4156bbc115e55194e6df9d7e76cb10 -R 135aeb3f5184e1f0131c446ddc8eab37 +P da78d07e77cbc783fbc725758911c230fd6a1c1885d9576125de955dcc2bd37f +R 17d195e142931273edf7c88c21ff2164 U dan -Z 33215e49f8892bbcd31004428ffced69 +Z 7652ccb47cdd7a8e28db861627789490 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 60beff1eed..f1f57381d1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -da78d07e77cbc783fbc725758911c230fd6a1c1885d9576125de955dcc2bd37f \ No newline at end of file +317a50563d9e8586fda136e513727241b414e7267d50a06571c8ebd0eae710bc \ No newline at end of file From 910c77b049f6d64866bc8d0a0745f8ed446e0dfa Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 17:58:56 +0000 Subject: [PATCH 323/347] Fix memory leak in new code on this branch. FossilOrigin-Name: ebc160b9a05568df66f86e30804399ee29d34b44a60c57e062f98cb92826353f --- ext/fts5/fts5_index.c | 1 - manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 095b945838..a24308534c 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -2763,7 +2763,6 @@ static void fts5SegIterNextInit( pIter->iLeafPgno = iPg - 1; fts5SegIterNextPage(p, pIter); fts5SegIterSetNext(p, pIter); - fts5SegIterAllocTombstone(p, pIter); } if( pIter->pLeaf ){ const u8 *a = pIter->pLeaf->p; diff --git a/manifest b/manifest index 00c355a167..355143083f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sthe\sxInstCount()\sAPI\sand\s"ORDER\sBY\srank"\squeries. -D 2023-12-04T17:45:33.714 +C Fix\smemory\sleak\sin\snew\scode\son\sthis\sbranch. +D 2023-12-04T17:58:56.324 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c aec893108d90cc8eae6801283d38d4e705d8c22aba12717fcc56e1910b8e3b32 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 072666814be04485445c07e0e75362d13245cef6c66e07aa2060c532190d0c10 +F ext/fts5/fts5_index.c 7111fed6c01048e227cc8c681306fa2db1e5ad29429b1373e665b8e95a7868ba F ext/fts5/fts5_main.c 1fbadf63a7381fd68753efdccce19683f587668489b1c5a9c7bf53d6e9b64872 F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -2150,8 +2150,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 da78d07e77cbc783fbc725758911c230fd6a1c1885d9576125de955dcc2bd37f -R 17d195e142931273edf7c88c21ff2164 +P 317a50563d9e8586fda136e513727241b414e7267d50a06571c8ebd0eae710bc +R 4cc8f92a68d4e11e53934c982deb6dc3 U dan -Z 7652ccb47cdd7a8e28db861627789490 +Z c8f15e3d33aff662e610fc8982efce75 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f1f57381d1..bb8887198f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -317a50563d9e8586fda136e513727241b414e7267d50a06571c8ebd0eae710bc \ No newline at end of file +ebc160b9a05568df66f86e30804399ee29d34b44a60c57e062f98cb92826353f \ No newline at end of file From 54318b382a982fa08dfe3d034156afa20550aea9 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 18:45:14 +0000 Subject: [PATCH 324/347] Fixes for xInstToken() with tokendata=0 tables. And with prefix queries. FossilOrigin-Name: 78fbb71598b1ca756acc078253880a1d0f7983a5a26b9efc683e6488122505a1 --- ext/fts5/fts5_expr.c | 18 ++++++++++---- ext/fts5/fts5_main.c | 1 - ext/fts5/test/fts5origintext.test | 39 +++++++++++++++++++++++++++++++ manifest | 16 ++++++------- manifest.uuid | 2 +- 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 02d7e78cbe..c5de6eba6c 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -3208,7 +3208,8 @@ int sqlite3Fts5ExprInstToken( int *pnOut ){ Fts5ExprPhrase *pPhrase = 0; - Fts5IndexIter *pIter = 0; + Fts5ExprTerm *pTerm = 0; + int rc = SQLITE_OK; if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ return SQLITE_RANGE; @@ -3217,9 +3218,18 @@ int sqlite3Fts5ExprInstToken( if( iToken<0 || iToken>=pPhrase->nTerm ){ return SQLITE_RANGE; } - pIter = pPhrase->aTerm[iToken].pIter; - - return sqlite3Fts5IterToken(pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut); + pTerm = &pPhrase->aTerm[iToken]; + if( pTerm->bPrefix==0 ){ + if( pExpr->pConfig->bTokendata ){ + rc = sqlite3Fts5IterToken( + pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut + ); + }else{ + *ppOut = pTerm->pTerm; + *pnOut = pTerm->nFullTerm; + } + } + return rc; } /* diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index c183f3c579..d35e998da0 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -2365,7 +2365,6 @@ static int fts5ApiInstToken( int iCol = pCsr->aInst[iIdx*3 + 1]; int iOff = pCsr->aInst[iIdx*3 + 2]; i64 iRowid = fts5CursorRowid(pCsr); - rc = sqlite3Fts5ExprInstToken( pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut ); diff --git a/ext/fts5/test/fts5origintext.test b/ext/fts5/test/fts5origintext.test index 8273b3ca4d..9752f35d34 100644 --- a/ext/fts5/test/fts5origintext.test +++ b/ext/fts5/test/fts5origintext.test @@ -252,6 +252,45 @@ do_execsql_test 5.3 { {0.0.0 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5} } +#------------------------------------------------------------------------- +# Test the xInstToken() API with: +# +# * a non tokendata=1 table. +# * prefix queries. +# +reset_db +sqlite3_fts5_register_origintext db +do_execsql_test 6.0 { + CREATE VIRTUAL TABLE ft USING fts5( + x, y, tokenize='origintext unicode61', detail=%DETAIL% + ); + + INSERT INTO ft VALUES('One Two', 'Three two'); + INSERT INTO ft VALUES('three Three', 'one One'); +} +proc tokens {cmd} { + set ret [list] + for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { + set txt [$cmd xInstToken $iTok 0] + set txt [string map [list "\0" "."] $txt] + lappend ret $txt + } + set ret +} +sqlite3_fts5_create_function db tokens tokens + +do_execsql_test 6.1 { + SELECT rowid, tokens(ft) FROM ft('One'); +} {1 one.One 2 one.One} + +do_execsql_test 6.2 { + SELECT rowid, tokens(ft) FROM ft('on*'); +} {1 {{}} 2 {{} {}}} + +do_execsql_test 6.3 { + SELECT rowid, tokens(ft) FROM ft('Three*'); +} {1 {{}} 2 {{}}} + } finish_test diff --git a/manifest b/manifest index 355143083f..52485ee8e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\smemory\sleak\sin\snew\scode\son\sthis\sbranch. -D 2023-12-04T17:58:56.324 +C Fixes\sfor\sxInstToken()\swith\stokendata=0\stables.\sAnd\swith\sprefix\squeries. +D 2023-12-04T18:45:14.192 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,10 +94,10 @@ F ext/fts5/fts5Int.h defa43c0932265138ee910ca416e6baccf8b774e0f3d610e74be1ab2880 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c aec893108d90cc8eae6801283d38d4e705d8c22aba12717fcc56e1910b8e3b32 +F ext/fts5/fts5_expr.c 920516be4aac44eccdd9e747fe26f367442848b9a58971e4b36edb0c8284b6b8 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 F ext/fts5/fts5_index.c 7111fed6c01048e227cc8c681306fa2db1e5ad29429b1373e665b8e95a7868ba -F ext/fts5/fts5_main.c 1fbadf63a7381fd68753efdccce19683f587668489b1c5a9c7bf53d6e9b64872 +F ext/fts5/fts5_main.c fb7ec495d663f40d18e420e1986316591041a70e1e4b4696ab2a7384e4c7fd7a F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee @@ -190,7 +190,7 @@ F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618 F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca -F ext/fts5/test/fts5origintext.test 6574e8d2121460cda72866afe3e582693d9992f150b0703aff5981625b527e62 +F ext/fts5/test/fts5origintext.test d2796fa08ee7aecfabdc0c45bb8a2fb16a00ea8757e63fbc153b718dbe430a39 F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 @@ -2150,8 +2150,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 317a50563d9e8586fda136e513727241b414e7267d50a06571c8ebd0eae710bc -R 4cc8f92a68d4e11e53934c982deb6dc3 +P ebc160b9a05568df66f86e30804399ee29d34b44a60c57e062f98cb92826353f +R 4a97cb4432f9a2f4a872da3bc4853a9c U dan -Z c8f15e3d33aff662e610fc8982efce75 +Z d5e27dba49298422b322a3619fa6b5f7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bb8887198f..667968c045 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ebc160b9a05568df66f86e30804399ee29d34b44a60c57e062f98cb92826353f \ No newline at end of file +78fbb71598b1ca756acc078253880a1d0f7983a5a26b9efc683e6488122505a1 \ No newline at end of file From 3fedb7e59e6a5208f006cad5e02be79abdd2e759 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 18:53:10 +0000 Subject: [PATCH 325/347] Fix errors in rendering JSON5 escape sequences embedded in JSONB. FossilOrigin-Name: f1a51ae3863557526a51c6e98e71fcdf4f1ed14a36212b3c90f7408f926345e4 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 12 ++++++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 7f065bdc52..7e8f87147a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Continuing\ssimplifications\sand\scode\scleanup. -D 2023-12-04T17:40:28.698 +C Fix\serrors\sin\srendering\sJSON5\sescape\ssequences\sembedded\sin\sJSONB. +D 2023-12-04T18:53:10.232 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 0e9fc8f2f2fecb1b4e7904ead93aa98313483ca4fea1d564e252e445f717b226 +F src/json.c b3a194fc1db52f1b169d9779a0d9155b4c123e8492a921114a5381700ea6c81d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 f601de3eeabd85993c1f5ee96b62de6fdabbeae2fe8950e00d08feb48d42c498 -R c9ebf0dbf5357376ee89626a26211c09 +P ddf92b5059a9106753fd18b82ba8daa269a62af947561c460790107b83416f0b +R 2f4c11a457fe80bb549ac0019420b08f U drh -Z 2f59464d9d28542ea091e094068881ef +Z 9def50f1dac0c1957715ee3f6f843ba7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b3d16e9d7b..030ca873ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ddf92b5059a9106753fd18b82ba8daa269a62af947561c460790107b83416f0b \ No newline at end of file +f1a51ae3863557526a51c6e98e71fcdf4f1ed14a36212b3c90f7408f926345e4 \ No newline at end of file diff --git a/src/json.c b/src/json.c index dc884b9fd3..aefd47498f 100644 --- a/src/json.c +++ b/src/json.c @@ -2401,7 +2401,10 @@ static void jsonReturnFromBlob( sqlite3 *db = sqlite3_context_db_handle(pCtx); n = jsonbPayloadSize(pParse, i, &sz); - if( n==0 ) return; + if( n==0 ){ + sqlite3_result_error(pCtx, "malformed JSON", -1); + return; + } switch( pParse->aBlob[i] & 0x0f ){ case JSONB_NULL: { sqlite3_result_null(pCtx); @@ -2483,7 +2486,7 @@ static void jsonReturnFromBlob( }else{ u32 vlo; if( (v&0xfc00)==0xd800 - && i Date: Mon, 4 Dec 2023 19:14:13 +0000 Subject: [PATCH 326/347] Do not make the input JSONB editable in json_remove() if there are no PATH argument. FossilOrigin-Name: 66594544f3ba9977475a3e3f74404eb2b2fb845053b28bd24c2b52c7df94e9d7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 7e8f87147a..e729bdb871 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\serrors\sin\srendering\sJSON5\sescape\ssequences\sembedded\sin\sJSONB. -D 2023-12-04T18:53:10.232 +C Do\snot\smake\sthe\sinput\sJSONB\seditable\sin\sjson_remove()\sif\sthere\sare\sno\sPATH\nargument. +D 2023-12-04T19:14:13.899 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c b3a194fc1db52f1b169d9779a0d9155b4c123e8492a921114a5381700ea6c81d +F src/json.c ab1eb1ab2ecedf193e0e1dc2ae62668e74ffd8668c39bf2014d419d175f6aae6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 ddf92b5059a9106753fd18b82ba8daa269a62af947561c460790107b83416f0b -R 2f4c11a457fe80bb549ac0019420b08f +P f1a51ae3863557526a51c6e98e71fcdf4f1ed14a36212b3c90f7408f926345e4 +R fedbc77310dd69998d8c7dc42f0e4ac1 U drh -Z 9def50f1dac0c1957715ee3f6f843ba7 +Z e47b61cc62bf0572c657f0a0469cf19a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 030ca873ab..4c9ef95e4c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f1a51ae3863557526a51c6e98e71fcdf4f1ed14a36212b3c90f7408f926345e4 \ No newline at end of file +66594544f3ba9977475a3e3f74404eb2b2fb845053b28bd24c2b52c7df94e9d7 \ No newline at end of file diff --git a/src/json.c b/src/json.c index aefd47498f..97c65df0bc 100644 --- a/src/json.c +++ b/src/json.c @@ -2802,7 +2802,6 @@ rebuild_from_cache: if( n==0 || sz+n!=p->nBlob || ((p->aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0) - || sz+n!=p->nBlob ){ goto json_pfa_malformed; } @@ -3556,7 +3555,7 @@ static void jsonRemoveFunc( u32 rc; /* Subroutine return code */ if( argc<1 ) return; - p = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); + p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0); if( p==0 ) return; for(i=1; i Date: Mon, 4 Dec 2023 19:32:17 +0000 Subject: [PATCH 327/347] Fixes to error handling in json_array_length(). FossilOrigin-Name: aa85df2d26b74c171c55bde19ef17c4f11f40b8af7181bbf7162f87cdea7e88b --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 6 +----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index e729bdb871..c1b6b255f5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\smake\sthe\sinput\sJSONB\seditable\sin\sjson_remove()\sif\sthere\sare\sno\sPATH\nargument. -D 2023-12-04T19:14:13.899 +C Fixes\sto\serror\shandling\sin\sjson_array_length(). +D 2023-12-04T19:32:17.504 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c ab1eb1ab2ecedf193e0e1dc2ae62668e74ffd8668c39bf2014d419d175f6aae6 +F src/json.c 326c7149206251639111f4fe2275f66d50345da0725dec4c617d583a280e969d F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 f1a51ae3863557526a51c6e98e71fcdf4f1ed14a36212b3c90f7408f926345e4 -R fedbc77310dd69998d8c7dc42f0e4ac1 +P 66594544f3ba9977475a3e3f74404eb2b2fb845053b28bd24c2b52c7df94e9d7 +R 07c551e152c91bb13fe1d576cd87540a U drh -Z e47b61cc62bf0572c657f0a0469cf19a +Z 0dc2cc057fc8990b916cb29e965f29a3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4c9ef95e4c..2b59f8602a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -66594544f3ba9977475a3e3f74404eb2b2fb845053b28bd24c2b52c7df94e9d7 \ No newline at end of file +aa85df2d26b74c171c55bde19ef17c4f11f40b8af7181bbf7162f87cdea7e88b \ No newline at end of file diff --git a/src/json.c b/src/json.c index 97c65df0bc..6b72d3fdea 100644 --- a/src/json.c +++ b/src/json.c @@ -3115,11 +3115,7 @@ static void jsonArrayLengthFunc( if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ cnt = jsonbArrayCount(p, i); } - if( eErr ){ - if( eErr==2 ) sqlite3_result_error(ctx, "malformed JSON", -1); - }else{ - sqlite3_result_int64(ctx, cnt); - } + if( !eErr ) sqlite3_result_int64(ctx, cnt); jsonParseFree(p); } From 49bfbc1ef37784f9f205f06c272716b058d5be3e Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Dec 2023 19:48:08 +0000 Subject: [PATCH 328/347] Add further tests for xInstToken(). FossilOrigin-Name: 8582707f16133f003a6687f68cbea03d4eb6c2a0e2e07746b7cace0c44e84fa4 --- ext/fts5/test/fts5origintext5.test | 96 +++++++++++++++++++++++------- manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/ext/fts5/test/fts5origintext5.test b/ext/fts5/test/fts5origintext5.test index b425c582e8..89a5d8f39c 100644 --- a/ext/fts5/test/fts5origintext5.test +++ b/ext/fts5/test/fts5origintext5.test @@ -82,7 +82,7 @@ db func document document expr srand(6) set NDOC 200 -set NLOOP 100 +set NLOOP 50 sqlite3_fts5_register_origintext db @@ -102,15 +102,17 @@ proc rankfunc {cmd} { } sqlite3_fts5_create_function db rankfunc rankfunc -proc ctrl_tokens {doc term} { +proc ctrl_tokens {term args} { set ret [list] set term [string tolower $term] - foreach a $doc { - if {[string tolower $a]==$term} { - if {$a==$term} { - lappend ret $a - } else { - lappend ret [string tolower $a].$a + foreach doc $args { + foreach a $doc { + if {[string tolower $a]==$term} { + if {$a==$term} { + lappend ret $a + } else { + lappend ret [string tolower $a].$a + } } } } @@ -118,6 +120,20 @@ proc ctrl_tokens {doc term} { } db func ctrl_tokens ctrl_tokens +proc do_all_vocab_test {tn} { + foreach ::v [vocab] { + set answer [execsql { + SELECT id, ctrl_tokens($::v, x) FROM ctrl WHERE x LIKE '%' || $::v || '%' + }] + do_execsql_test $tn.$::v.1 { + SELECT rowid, tokens(ft) FROM ft($::v) + } $answer + do_execsql_test $tn.$::v.2 { + SELECT rowid, tokens(ft) FROM ft($::v) ORDER BY rank + } $answer + } +} + do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( @@ -139,20 +155,6 @@ do_test 1.1 { } } {} -proc do_all_vocab_test {tn} { - foreach ::v [vocab] { - set answer [execsql { - SELECT id, ctrl_tokens(x, $::v) FROM ctrl WHERE x LIKE '%' || $::v || '%' - }] - do_execsql_test $tn.$::v.1 { - SELECT rowid, tokens(ft) FROM ft($::v) - } $answer - do_execsql_test $tn.$::v.2 { - SELECT rowid, tokens(ft) FROM ft($::v) ORDER BY rank - } $answer - } -} - #execsql_pp { SELECT * FROM ctrl } #execsql_pp { SELECT * FROM ft } #fts5_aux_test_functions db @@ -173,9 +175,59 @@ for {set ii 0} {$ii < $NLOOP} {incr ii} { do_all_vocab_test 1.3.$ii } +#------------------------------------------------------------------------- +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE ft2 USING fts5( + x, y, tokenize="origintext unicode61", content=, contentless_delete=1, + tokendata=1 + ); + CREATE TABLE ctrl2(id INTEGER PRIMARY KEY, x TEXT, y TEXT); + INSERT INTO ft2(ft2, rank) VALUES('pgsz', 64); + INSERT INTO ft2(ft2, rank) VALUES('rank', 'rankfunc()'); +} +do_test 2.1 { + for {set ii 0} {$ii < $NDOC} {incr ii} { + set doc1 [document] + set doc2 [document] + execsql { + INSERT INTO ft2(rowid, x, y) VALUES($ii, $doc, $doc2); + INSERT INTO ctrl2(id, x, y) VALUES($ii, $doc, $doc2); + } + } +} {} +proc do_all_vocab_test2 {tn} { + foreach ::v [vocab] { + set answer [execsql { + SELECT id, ctrl_tokens($::v, x, y) FROM ctrl2 + WHERE x LIKE '%' || $::v || '%' OR y LIKE '%' || $::v || '%'; + }] + do_execsql_test $tn.$::v.1 { + SELECT rowid, tokens(ft2) FROM ft2($::v) + } $answer + do_execsql_test $tn.$::v.2 { + SELECT rowid, tokens(ft2) FROM ft2($::v) ORDER BY rank + } $answer + } +} + +do_all_vocab_test2 2.2 + +for {set ii 0} {$ii < $NLOOP} {incr ii} { + set lRowid [execsql { SELECT id FROM ctrl2 WHERE random() % 2 }] + foreach r $lRowid { + execsql { DELETE FROM ft2 WHERE rowid = $r } + execsql { DELETE FROM ctrl2 WHERE rowid = $r } + + set doc1 [document] + set doc2 [document] + execsql { INSERT INTO ft2(rowid, x, y) VALUES($r, $doc, $doc1) } + execsql { INSERT INTO ctrl2(id, x, y) VALUES($r, $doc, $doc2) } + } + do_all_vocab_test 2.3.$ii +} finish_test diff --git a/manifest b/manifest index 52485ee8e6..11d57c4176 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sfor\sxInstToken()\swith\stokendata=0\stables.\sAnd\swith\sprefix\squeries. -D 2023-12-04T18:45:14.192 +C Add\sfurther\stests\sfor\sxInstToken(). +D 2023-12-04T19:48:08.530 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -194,7 +194,7 @@ F ext/fts5/test/fts5origintext.test d2796fa08ee7aecfabdc0c45bb8a2fb16a00ea8757e6 F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 -F ext/fts5/test/fts5origintext5.test f9dfb005248d764fdc52859308875f680b523a897258a2cc40100f9e2356b5a9 +F ext/fts5/test/fts5origintext5.test 067bfb3008323585df640ab29e8ef7c4ca6dec62c597be07a9f896d88f98cd10 F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2150,8 +2150,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 ebc160b9a05568df66f86e30804399ee29d34b44a60c57e062f98cb92826353f -R 4a97cb4432f9a2f4a872da3bc4853a9c +P 78fbb71598b1ca756acc078253880a1d0f7983a5a26b9efc683e6488122505a1 +R ced1cc2c59284a9ef3026043e080f745 U dan -Z d5e27dba49298422b322a3619fa6b5f7 +Z e2cce7acff968ef3c9472723192ae452 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 667968c045..780ed88175 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -78fbb71598b1ca756acc078253880a1d0f7983a5a26b9efc683e6488122505a1 \ No newline at end of file +8582707f16133f003a6687f68cbea03d4eb6c2a0e2e07746b7cace0c44e84fa4 \ No newline at end of file From 3cdb079476530f3ef394bbe2c8c0ce3120f012b2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 4 Dec 2023 23:12:57 +0000 Subject: [PATCH 329/347] Rename the internal routine jsonMergePatchBlob() to just jsonMergePatch(). FossilOrigin-Name: ebf667b616235bb64b83832008342ba5e7b10b2c170d7cebc431f040fef7ecfb --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index c1b6b255f5..db03cd3807 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sto\serror\shandling\sin\sjson_array_length(). -D 2023-12-04T19:32:17.504 +C Rename\sthe\sinternal\sroutine\sjsonMergePatchBlob()\sto\sjust\sjsonMergePatch(). +D 2023-12-04T23:12:57.639 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 326c7149206251639111f4fe2275f66d50345da0725dec4c617d583a280e969d +F src/json.c d2385c49df219c1d10cb0a3a817df5aa7bd2ef86772f24f3763e2341b0875b70 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 66594544f3ba9977475a3e3f74404eb2b2fb845053b28bd24c2b52c7df94e9d7 -R 07c551e152c91bb13fe1d576cd87540a +P aa85df2d26b74c171c55bde19ef17c4f11f40b8af7181bbf7162f87cdea7e88b +R 6429070b5e7eb1a8581d28813921879f U drh -Z 0dc2cc057fc8990b916cb29e965f29a3 +Z f2f7e0af6ffb5ce2a2c95b6ed2412ab3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2b59f8602a..7301a291e0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aa85df2d26b74c171c55bde19ef17c4f11f40b8af7181bbf7162f87cdea7e88b \ No newline at end of file +ebf667b616235bb64b83832008342ba5e7b10b2c170d7cebc431f040fef7ecfb \ No newline at end of file diff --git a/src/json.c b/src/json.c index 6b72d3fdea..e0651130ff 100644 --- a/src/json.c +++ b/src/json.c @@ -3243,7 +3243,7 @@ json_extract_error: } /* -** Return codes for jsonMergePatchBlob() +** Return codes for jsonMergePatch() */ #define JSON_MERGE_OK 0 /* Success */ #define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ @@ -3296,7 +3296,7 @@ json_extract_error: ** | ** ^---- Line numbers referenced in comments in the implementation */ -static int jsonMergePatchBlob( +static int jsonMergePatch( JsonParse *pTarget, /* The JSON parser that contains the TARGET */ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ const JsonParse *pPatch, /* The PATCH */ @@ -3427,7 +3427,7 @@ static int jsonMergePatchBlob( /* Algorithm line 12 */ int rc, savedDelta = pTarget->delta; pTarget->delta = 0; - rc = jsonMergePatchBlob(pTarget, iTValue, pPatch, iPValue); + rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); if( rc ) return rc; pTarget->delta += savedDelta; } @@ -3448,7 +3448,7 @@ static int jsonMergePatchBlob( pTarget->aBlob[iTEnd+szNew] = 0x00; savedDelta = pTarget->delta; pTarget->delta = 0; - rc = jsonMergePatchBlob(pTarget, iTEnd+szNew,pPatch,iPValue); + rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); if( rc ) return rc; pTarget->delta += savedDelta; } @@ -3479,7 +3479,7 @@ static void jsonPatchFunc( if( pTarget==0 ) return; pPatch = jsonParseFuncArg(ctx, argv[1], 0); if( pPatch ){ - rc = jsonMergePatchBlob(pTarget, 0, pPatch, 0); + rc = jsonMergePatch(pTarget, 0, pPatch, 0); if( rc==JSON_MERGE_OK ){ jsonReturnParse(ctx, pTarget); }else if( rc==JSON_MERGE_OOM ){ From ae2e9728020ecc71d8a47bddb0c0555fdca56a81 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 00:17:17 +0000 Subject: [PATCH 330/347] Fix OOM and corrupt JSONB handling in json_patch(). FossilOrigin-Name: 1910feb0b7d5cc2b810c3322f6cca281d8730182d30d162bd7bb56800979ea91 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index db03cd3807..c0fde4f425 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rename\sthe\sinternal\sroutine\sjsonMergePatchBlob()\sto\sjust\sjsonMergePatch(). -D 2023-12-04T23:12:57.639 +C Fix\sOOM\sand\scorrupt\sJSONB\shandling\sin\sjson_patch(). +D 2023-12-05T00:17:17.234 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c d2385c49df219c1d10cb0a3a817df5aa7bd2ef86772f24f3763e2341b0875b70 +F src/json.c 4dadaf584446be580a7a0054d25c4fc3ae17065952d5d1ffdb7a05a1cc102519 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 aa85df2d26b74c171c55bde19ef17c4f11f40b8af7181bbf7162f87cdea7e88b -R 6429070b5e7eb1a8581d28813921879f +P ebf667b616235bb64b83832008342ba5e7b10b2c170d7cebc431f040fef7ecfb +R 63dbe3f87f86d8011f70d9756cf66af6 U drh -Z f2f7e0af6ffb5ce2a2c95b6ed2412ab3 +Z f62ad889060f6abf937d9ec41a83acdd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7301a291e0..5223dccf73 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ebf667b616235bb64b83832008342ba5e7b10b2c170d7cebc431f040fef7ecfb \ No newline at end of file +1910feb0b7d5cc2b810c3322f6cca281d8730182d30d162bd7bb56800979ea91 \ No newline at end of file diff --git a/src/json.c b/src/json.c index e0651130ff..57082cf3f8 100644 --- a/src/json.c +++ b/src/json.c @@ -3348,11 +3348,11 @@ static int jsonMergePatch( pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; } n = jsonbPayloadSize(pPatch, iPatch, &sz); - if( n==0 ) return JSON_MERGE_BADPATCH; + if( NEVER(n==0) ) return JSON_MERGE_BADPATCH; iPCursor = iPatch+n; iPEnd = iPCursor+sz; n = jsonbPayloadSize(pTarget, iTarget, &sz); - if( n==0 ) return JSON_MERGE_BADTARGET; + if( NEVER(n==0) ) return JSON_MERGE_BADTARGET; iTStart = iTarget+n; iTEndBE = iTStart+sz; @@ -3365,7 +3365,7 @@ static int jsonMergePatch( nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); if( nPLabel==0 ) return JSON_MERGE_BADPATCH; iPValue = iPCursor + nPLabel + szPLabel; - if( iPCursor>=iPEnd ) return JSON_MERGE_BADPATCH; + if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH; nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); if( nPValue==0 ) return JSON_MERGE_BADPATCH; iPCursor = iPValue + nPValue + szPValue; @@ -3397,13 +3397,12 @@ static int jsonMergePatch( }else{ /* Should rarely happen */ JsonString s1, s2; - int isEqual; + int isEqual, isOom; jsonStringInit(&s1, 0); jsonXlateBlobToText(pTarget, iTLabel, &s1); - if( s1.eErr ) return JSON_MERGE_OOM; jsonStringInit(&s2, 0); jsonXlateBlobToText(pPatch, iPLabel, &s2); - if( s2.eErr ) return JSON_MERGE_OOM; + isOom = s1.eErr || s2.eErr; if( s1.nUsed==s2.nUsed && memcmp(s1.zBuf, s2.zBuf, s1.nUsed)==0 ){ isEqual = 1; }else{ @@ -3411,6 +3410,7 @@ static int jsonMergePatch( } jsonStringReset(&s1); jsonStringReset(&s2); + if( isOom ) return JSON_MERGE_OOM; if( isEqual ) break; } iTCursor = iTValue + nTValue + szTValue; @@ -3420,9 +3420,9 @@ static int jsonMergePatch( /* A match was found. Algorithm line 08 */ if( x==0 ){ /* Patch value is NULL. Algorithm line 09 */ - jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, - 0, 0); - if( pTarget->oom ) return JSON_MERGE_OOM; + jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0); + /* vvvvvv----- No OOM on a delete-only edit */ + if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM; }else{ /* Algorithm line 12 */ int rc, savedDelta = pTarget->delta; From fa43e21711422f4d5f1e82b6eda8a96f474c5a99 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 01:44:15 +0000 Subject: [PATCH 331/347] Use an assert() to fix a harmless static analyzer warning. FossilOrigin-Name: a249ca657e624028bc6b3d2c2bcedd7162d118addb7d62ce519920cecebf1860 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index c0fde4f425..62d3de3ec6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sOOM\sand\scorrupt\sJSONB\shandling\sin\sjson_patch(). -D 2023-12-05T00:17:17.234 +C Use\san\sassert()\sto\sfix\sa\sharmless\sstatic\sanalyzer\swarning. +D 2023-12-05T01:44:15.015 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 4dadaf584446be580a7a0054d25c4fc3ae17065952d5d1ffdb7a05a1cc102519 +F src/json.c 3635645a2a07070a38dade2ba34daa3f3633fd134a800cb05db236d68bc1b5c3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 ebf667b616235bb64b83832008342ba5e7b10b2c170d7cebc431f040fef7ecfb -R 63dbe3f87f86d8011f70d9756cf66af6 +P 1910feb0b7d5cc2b810c3322f6cca281d8730182d30d162bd7bb56800979ea91 +R a9ebb3d8db9982a53cbb9404704cc04b U drh -Z f62ad889060f6abf937d9ec41a83acdd +Z 17d27d3e2a2e6e4799038f7f740bf1ce # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5223dccf73..0dc59fba28 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1910feb0b7d5cc2b810c3322f6cca281d8730182d30d162bd7bb56800979ea91 \ No newline at end of file +a249ca657e624028bc6b3d2c2bcedd7162d118addb7d62ce519920cecebf1860 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 57082cf3f8..7bb409a8eb 100644 --- a/src/json.c +++ b/src/json.c @@ -3873,6 +3873,7 @@ static void jsonErrorFunc( }else{ /* Convert byte-offset s.iErr into a character offset */ u32 k; + assert( s.zJson!=0 ); /* Because s.oom is false */ for(k=0; k Date: Tue, 5 Dec 2023 12:20:58 +0000 Subject: [PATCH 332/347] Clean up the JSONB performance test script. FossilOrigin-Name: 905301075a7fc1010ee7e754867b1b698c9b8576d50e98125def32a5dfb7ee9d --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/json/json-q1-b.txt | 5 ++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 62d3de3ec6..ba676b6944 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\san\sassert()\sto\sfix\sa\sharmless\sstatic\sanalyzer\swarning. -D 2023-12-05T01:44:15.015 +C Clean\sup\sthe\sJSONB\sperformance\stest\sscript. +D 2023-12-05T12:20:58.241 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1322,7 +1322,7 @@ F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd7 F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35e30ee2 F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f -F test/json/json-q1-b.txt 606818a5fba6d9e418c9f4ea7d8418af026775042dad81439b72447a147a462c +F test/json/json-q1-b.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7 F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307 F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x F test/json101.test 70587d7d35ef9e2126364ba70f0c951f70827cfbd28649d779ff3df7e8f87547 @@ -2145,8 +2145,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 1910feb0b7d5cc2b810c3322f6cca281d8730182d30d162bd7bb56800979ea91 -R a9ebb3d8db9982a53cbb9404704cc04b +P a249ca657e624028bc6b3d2c2bcedd7162d118addb7d62ce519920cecebf1860 +R 458c3105955a68681dcfc6965be984aa U drh -Z 17d27d3e2a2e6e4799038f7f740bf1ce +Z 523674accd38155d2ed6d77fe8db57c8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0dc59fba28..cae3c03bea 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a249ca657e624028bc6b3d2c2bcedd7162d118addb7d62ce519920cecebf1860 \ No newline at end of file +905301075a7fc1010ee7e754867b1b698c9b8576d50e98125def32a5dfb7ee9d \ No newline at end of file diff --git a/test/json/json-q1-b.txt b/test/json/json-q1-b.txt index bd32ec7201..e78c63670a 100644 --- a/test/json/json-q1-b.txt +++ b/test/json/json-q1-b.txt @@ -1,6 +1,6 @@ .mode qbox .timer on -.param set $label '$.q87' +.param set $label 'q87' SELECT rowid, x->>$label FROM data1 WHERE x->>$label IS NOT NULL; CREATE TEMP TABLE t2(x JSON TEXT); @@ -14,8 +14,7 @@ WITH RECURSIVE ), c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<5) INSERT INTO t2(x) - SELECT jsonb(json_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6)) - FROM array1, c2; + SELECT jsonb_object('a',n,'b',n*2,'c',y,'d',3,'e',5,'f',6) FROM array1, c2; CREATE INDEX t2x1 ON t2(x->>'a'); CREATE INDEX t2x2 ON t2(x->>'b'); CREATE INDEX t2x3 ON t2(x->>'e'); From 590aaff9928d7904e16f9868f1822beaeab6d909 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 12:22:05 +0000 Subject: [PATCH 333/347] Small performance gain by unwinding the string literal delimiter search loop in the JSON parser by one more level. FossilOrigin-Name: 4c587feac153e8ebe526559ec3d254f545f81e8d1ed3126f91a5ff25ec4aa72e --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 10 ++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index ba676b6944..483ed62511 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clean\sup\sthe\sJSONB\sperformance\stest\sscript. -D 2023-12-05T12:20:58.241 +C Small\sperformance\sgain\sby\sunwinding\sthe\sstring\sliteral\sdelimiter\ssearch\nloop\sin\sthe\sJSON\sparser\sby\sone\smore\slevel. +D 2023-12-05T12:22:05.245 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 3635645a2a07070a38dade2ba34daa3f3633fd134a800cb05db236d68bc1b5c3 +F src/json.c 53d9e4ea2e1b33ea0f0dc629d08605748056333c57ff03ea8b6cfdcc585a4dc0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 a249ca657e624028bc6b3d2c2bcedd7162d118addb7d62ce519920cecebf1860 -R 458c3105955a68681dcfc6965be984aa +P 905301075a7fc1010ee7e754867b1b698c9b8576d50e98125def32a5dfb7ee9d +R ec589ba3fd8295a6ff09aeae12dc3bd7 U drh -Z 523674accd38155d2ed6d77fe8db57c8 +Z 7a001bd7445e746508caf3ed9f69266e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cae3c03bea..5ad309bbc2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -905301075a7fc1010ee7e754867b1b698c9b8576d50e98125def32a5dfb7ee9d \ No newline at end of file +4c587feac153e8ebe526559ec3d254f545f81e8d1ed3126f91a5ff25ec4aa72e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 7bb409a8eb..ff488de4ec 100644 --- a/src/json.c +++ b/src/json.c @@ -1412,11 +1412,13 @@ json_parse_restart: j = i+1; while( 1 /*exit-by-break*/ ){ if( jsonIsOk[(u8)z[j]] ){ - if( jsonIsOk[(u8)z[j+1]] ){ - j += 2; - continue; - }else{ + if( !jsonIsOk[(u8)z[j+1]] ){ j += 1; + }else if( !jsonIsOk[(u8)z[j+2]] ){ + j += 2; + }else{ + j += 3; + continue; } } c = z[j]; From 8eac91fab71bec3638d468330446635ca5640486 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 12:52:13 +0000 Subject: [PATCH 334/347] Use strspn() to accelerate whitespace bypass in the JSON parser. FossilOrigin-Name: 843197df08352bdff4b87be91d160e574572aded0d0c66142fd960000c0b4701 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 15 ++++++++++----- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 483ed62511..e5224d07aa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Small\sperformance\sgain\sby\sunwinding\sthe\sstring\sliteral\sdelimiter\ssearch\nloop\sin\sthe\sJSON\sparser\sby\sone\smore\slevel. -D 2023-12-05T12:22:05.245 +C Use\sstrspn()\sto\saccelerate\swhitespace\sbypass\sin\sthe\sJSON\sparser. +D 2023-12-05T12:52:13.143 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 53d9e4ea2e1b33ea0f0dc629d08605748056333c57ff03ea8b6cfdcc585a4dc0 +F src/json.c 752aabf2c961d5d79143ae4a2b41d1bf60d4ec15110b0b75c5c3de5fe62d4dd1 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 905301075a7fc1010ee7e754867b1b698c9b8576d50e98125def32a5dfb7ee9d -R ec589ba3fd8295a6ff09aeae12dc3bd7 +P 4c587feac153e8ebe526559ec3d254f545f81e8d1ed3126f91a5ff25ec4aa72e +R 7ec679fd53493789f3af3780d93617a7 U drh -Z 7a001bd7445e746508caf3ed9f69266e +Z be9b20ef175c9b8d4ac4f39def382e58 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5ad309bbc2..3eb7382c63 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4c587feac153e8ebe526559ec3d254f545f81e8d1ed3126f91a5ff25ec4aa72e \ No newline at end of file +843197df08352bdff4b87be91d160e574572aded0d0c66142fd960000c0b4701 \ No newline at end of file diff --git a/src/json.c b/src/json.c index ff488de4ec..2fc6c8b74f 100644 --- a/src/json.c +++ b/src/json.c @@ -168,6 +168,12 @@ static const char jsonIsSpace[] = { }; #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) +/* +** The set of all space characters recognized by jsonIsspace(). +** Useful as an argument to strspn(). +*/ +static const char jsonSpaces[] = "\011\012\015\040"; + /* ** Characters that are special to JSON. Control characters, ** '"' and '\\'. @@ -1296,6 +1302,7 @@ json_parse_restart: j++; }else{ if( jsonIsspace(z[j]) ){ + /* strspn() is not helpful here */ do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==':' ){ j++; @@ -1322,7 +1329,7 @@ json_parse_restart: break; }else{ if( jsonIsspace(z[j]) ){ - do{ j++; }while( jsonIsspace(z[j]) ); + j += 1 + strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ @@ -1374,7 +1381,7 @@ json_parse_restart: break; }else{ if( jsonIsspace(z[j]) ){ - do{ j++; }while( jsonIsspace(z[j]) ); + j += 1 + strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]==']' ){ @@ -1635,9 +1642,7 @@ json_parse_restart: case 0x0a: case 0x0d: case 0x20: { - do{ - i++; - }while( jsonIsspace(z[i]) ); + i += 1 + strspn(&z[i+1], jsonSpaces); goto json_parse_restart; } case 0x0b: From a0de45459e8dcd8594a95684768f14c680570e04 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 18:28:15 +0000 Subject: [PATCH 335/347] Miscellaneous comment cleanup and typo fixes. FossilOrigin-Name: 59446dc0bd0091572122a3c8b4653d7a2dc867d16c4a5919f79b81bc3a673ce3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 26 +++++++++++++------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index e5224d07aa..ff6dfffb99 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\sstrspn()\sto\saccelerate\swhitespace\sbypass\sin\sthe\sJSON\sparser. -D 2023-12-05T12:52:13.143 +C Miscellaneous\scomment\scleanup\sand\stypo\sfixes. +D 2023-12-05T18:28:15.619 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 752aabf2c961d5d79143ae4a2b41d1bf60d4ec15110b0b75c5c3de5fe62d4dd1 +F src/json.c 00480474f134f18d77275de3fe1220b198059af39d41b8549f14af991ee50c64 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 4c587feac153e8ebe526559ec3d254f545f81e8d1ed3126f91a5ff25ec4aa72e -R 7ec679fd53493789f3af3780d93617a7 +P 843197df08352bdff4b87be91d160e574572aded0d0c66142fd960000c0b4701 +R 51649fd2031c328be393a9b165de9b28 U drh -Z be9b20ef175c9b8d4ac4f39def382e58 +Z 92ba2a23f6f1deba28c66b8e97e6bff1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3eb7382c63..bdb4e91e40 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -843197df08352bdff4b87be91d160e574572aded0d0c66142fd960000c0b4701 \ No newline at end of file +59446dc0bd0091572122a3c8b4653d7a2dc867d16c4a5919f79b81bc3a673ce3 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 2fc6c8b74f..15f99d49b2 100644 --- a/src/json.c +++ b/src/json.c @@ -10,7 +10,7 @@ ** ****************************************************************************** ** -** This SQLite JSON functions. +** SQLite JSON functions. ** ** This file began as an extension in ext/misc/json1.c in 2015. That ** extension proved so useful that it has now been moved into the core. @@ -170,7 +170,7 @@ static const char jsonIsSpace[] = { /* ** The set of all space characters recognized by jsonIsspace(). -** Useful as an argument to strspn(). +** Useful as the second argument to strspn(). */ static const char jsonSpaces[] = "\011\012\015\040"; @@ -198,14 +198,6 @@ static const char jsonIsOk[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; -/* Put code used only for testing inside the JSON_VVA() macro. -*/ -#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) -# define JSON_VVA(X) -#else -# define JSON_VVA(X) X -#endif - /* Objects */ typedef struct JsonCache JsonCache; typedef struct JsonString JsonString; @@ -263,8 +255,8 @@ struct JsonString { #define JSON_SUBTYPE 74 /* Ascii for "J" */ /* -** Bit values for the flags passed into jsonExtractFunc() or -** jsonSetFunc() via the user-data value. +** Bit values for the flags passed into various SQL function implementations +** via the sqlite3_user_data() value. */ #define JSON_JSON 0x01 /* Result is always JSON */ #define JSON_SQL 0x02 /* Result is always SQL */ @@ -323,7 +315,11 @@ struct JsonParse { ** descent parser. A depth of 1000 is far deeper than any sane JSON ** should go. Historical note: This limit was 2000 prior to version 3.42.0 */ -#define JSON_MAX_DEPTH 1000 +#ifndef SQLITE_JSON_MAX_DEPTH +# define JSON_MAX_DEPTH 1000 +#else +# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH +#endif /* ** Allowed values for the flgs argument to jsonParseFuncArg(); @@ -807,6 +803,10 @@ static void jsonParseFree(JsonParse *pParse){ } } +/************************************************************************** +** Utility routines for the JSON text parser +**************************************************************************/ + /* ** Translate a single byte of Hex into an integer. ** This routine only gives a correct answer if h really is a valid hexadecimal From fb923fc4ccb79fe27d6daa9477ac2ef55b1da9bc Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 5 Dec 2023 18:36:23 +0000 Subject: [PATCH 336/347] Further tests for the new code on this branch. FossilOrigin-Name: 59d008b6c23ab900377bc696ee19381feb7614bac80546eae361e401c3620c4e --- ext/fts5/fts5_expr.c | 6 +- ext/fts5/fts5_index.c | 113 ++++++++++++----------------- ext/fts5/test/fts5faultH.test | 93 ++++++++++++++++++++++++ ext/fts5/test/fts5origintext2.test | 39 ++++++++++ ext/fts5/test/fts5origintext3.test | 21 ++++++ ext/fts5/test/fts5origintext5.test | 44 ++++++++++- manifest | 21 +++--- manifest.uuid | 2 +- 8 files changed, 257 insertions(+), 82 deletions(-) create mode 100644 ext/fts5/test/fts5faultH.test diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index c5de6eba6c..bc7c9741ee 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -1768,7 +1768,9 @@ static int fts5ParseTokenize( memset(pTerm, 0, sizeof(Fts5ExprTerm)); pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); pTerm->nFullTerm = pTerm->nQueryTerm = nToken; - if( pCtx->pConfig->bTokendata ) pTerm->nQueryTerm = strlen(pTerm->pTerm); + if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){ + pTerm->nQueryTerm = strlen(pTerm->pTerm); + } } } @@ -3027,7 +3029,7 @@ static int fts5ExprPopulatePoslistsCb( int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff ); - if( rc==SQLITE_OK && pExpr->pConfig->bTokendata ){ + if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ int iCol = p->iOff>>32; int iTokOff = p->iOff & 0x7FFFFFFF; rc = sqlite3Fts5IndexIterWriteTokendata( diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index a24308534c..9a2cc026b9 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -6661,57 +6661,6 @@ static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ } } -/* -** The iterator passed as the first argument must be a tokendata=1 iterator -** (pIter->pTokenDataIter!=0). This function is used to access the token -** instance located at offset iOff of column iCol of row iRowid. It is -** returned via output pointers *ppOut and *pnOut. This is used by the -** xInstToken() API. -*/ -static int fts5TokendataIterToken( - Fts5Iter *pIter, - i64 iRowid, - int iCol, int iOff, - const char **ppOut, int *pnOut -){ - Fts5TokenDataIter *pT = pIter->pTokenDataIter; - Fts5TokenDataMap *aMap = pT->aMap; - i64 iPos = (((i64)iCol)<<32) + iOff; - - int i1 = 0; - int i2 = pT->nMap; - int iTest = 0; - - while( i2>i1 ){ - iTest = (i1 + i2) / 2; - - if( aMap[iTest].iRowidiRowid ){ - i2 = iTest; - }else{ - if( aMap[iTest].iPosiPos ){ - i2 = iTest; - }else{ - break; - } - } - } - - if( i2>i1 ){ - Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; - *ppOut = (const char*)pMap->aSeg[0].term.p+1; - *pnOut = pMap->aSeg[0].term.n-1; - } - - return SQLITE_OK; -} - /* ** Append a mapping to the token-map belonging to object pT. */ @@ -6960,9 +6909,7 @@ static Fts5Iter *fts5SetupTokendataIter( memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); memset(pPrevIter, 0, sizeof(Fts5SegIter)); bDone = 1; - }else if( pPrevIter->pLeaf - && pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf - ){ + }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); bDone = 1; } @@ -7071,7 +7018,7 @@ int sqlite3Fts5IndexQuery( if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ - int bTokendata = (flags&FTS5INDEX_QUERY_NOTOKENDATA)?0:pConfig->bTokendata; + int bTokendata = pConfig->bTokendata; if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* Figure out which index to search and set iIdx accordingly. If this @@ -7085,6 +7032,7 @@ int sqlite3Fts5IndexQuery( ** for internal sanity checking by the integrity-check in debug ** mode only. */ #ifdef SQLITE_DEBUG + if( flags & FTS5INDEX_QUERY_NOTOKENDATA ) bTokendata = 0; if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){ assert( flags & FTS5INDEX_QUERY_PREFIX ); iIdx = 1+pConfig->nPrefix; @@ -7208,7 +7156,8 @@ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ /* ** This is used by xInstToken() to access the token at offset iOff, column ** iCol of row iRowid. The token is returned via output variables *ppOut -** and *pnOut. +** and *pnOut. The iterator passed as the first argument must be a tokendata=1 +** iterator (pIter->pTokenDataIter!=0). */ int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, @@ -7218,9 +7167,39 @@ int sqlite3Fts5IterToken( const char **ppOut, int *pnOut ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + Fts5TokenDataMap *aMap = pT->aMap; + i64 iPos = (((i64)iCol)<<32) + iOff; - if( pIter->pTokenDataIter ){ - return fts5TokendataIterToken(pIter, iRowid, iCol, iOff, ppOut, pnOut); + int i1 = 0; + int i2 = pT->nMap; + int iTest = 0; + + while( i2>i1 ){ + iTest = (i1 + i2) / 2; + + if( aMap[iTest].iRowidiRowid ){ + i2 = iTest; + }else{ + if( aMap[iTest].iPosiPos ){ + i2 = iTest; + }else{ + break; + } + } + } + + if( i2>i1 ){ + Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; + *ppOut = (const char*)pMap->aSeg[0].term.p+1; + *pnOut = pMap->aSeg[0].term.n-1; } return SQLITE_OK; @@ -7252,17 +7231,17 @@ int sqlite3Fts5IndexIterWriteTokendata( Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5TokenDataIter *pT = pIter->pTokenDataIter; Fts5Index *p = pIter->pIndex; + int ii; assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); - if( pT ){ - int ii; - for(ii=0; iinIter; ii++){ - Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; - if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; - } - if( iinIter ){ - fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); - } + assert( pIter->pTokenDataIter ); + + for(ii=0; iinIter; ii++){ + Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; + if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; + } + if( iinIter ){ + fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); } return fts5IndexReturn(p); } diff --git a/ext/fts5/test/fts5faultH.test b/ext/fts5/test/fts5faultH.test new file mode 100644 index 0000000000..9dd4cac0d6 --- /dev/null +++ b/ext/fts5/test/fts5faultH.test @@ -0,0 +1,93 @@ +# 2010 June 15 +# +# 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. +# +#*********************************************************************** +# + +source [file join [file dirname [info script]] fts5_common.tcl] +source $testdir/malloc_common.tcl +set testprefix fts5faultG + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +set ::testprefix fts5faultH + +sqlite3_fts5_register_origintext db + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING fts5( + x, tokenize="origintext unicode61", tokendata=1 + ); + + BEGIN; + INSERT INTO t1 VALUES('oNe tWo thRee'); + INSERT INTO t1 VALUES('One Two Three'); + INSERT INTO t1 VALUES('onE twO threE'); + COMMIT; + BEGIN; + INSERT INTO t1 VALUES('one two three'); + INSERT INTO t1 VALUES('one two three'); + INSERT INTO t1 VALUES('one two three'); + COMMIT; +} + +do_faultsim_test 1 -faults oom* -prep { +} -body { + execsql { + SELECT rowid FROM t1('three'); + } +} -test { + faultsim_integrity_check + faultsim_test_result {0 {1 2 3 4 5 6}} +} + + +reset_db +sqlite3_fts5_register_origintext db +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE t1 USING fts5( + x, tokenize="origintext unicode61", tokendata=1 + ); + INSERT INTO t1(t1, rank) VALUES('pgsz', 64); + + BEGIN; + INSERT INTO t1(rowid, x) VALUES(10, 'aaa bbb BBB'); + INSERT INTO t1(rowid, x) VALUES(12, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(13, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(14, 'bbb BBB bbb'); + INSERT INTO t1(rowid, x) VALUES(15, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(16, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(17, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(18, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(19, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(20, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(21, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(22, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(23, 'bbb bbb bbb'); + INSERT INTO t1(rowid, x) VALUES(24, 'aaa bbb BBB'); + COMMIT; +} + +do_faultsim_test 2 -faults oom* -prep { +} -body { + execsql { + SELECT rowid FROM t1('BBB AND AAA'); + } +} -test { + faultsim_integrity_check + faultsim_test_result {0 {10 24}} +} + + + +finish_test diff --git a/ext/fts5/test/fts5origintext2.test b/ext/fts5/test/fts5origintext2.test index a27309fe0c..a8c5d4eb50 100644 --- a/ext/fts5/test/fts5origintext2.test +++ b/ext/fts5/test/fts5origintext2.test @@ -103,5 +103,44 @@ do_execsql_test 1.12 { SELECT rowid FROM ft('today'); } {4 5 6} do_execsql_test 1.13 { SELECT rowid FROM ft('world'); } {7 8 9} do_execsql_test 1.14 { SELECT rowid FROM ft('hello') ORDER BY rank; } {1 2 3} +#------------------------------------------------------------------------ +reset_db +sqlite3_fts5_register_origintext db +proc tokens {cmd} { + set ret [list] + for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} { + set txt [$cmd xInstToken $iTok 0] + set txt [string map [list "\0" "."] $txt] + lappend ret $txt + } + set ret +} +sqlite3_fts5_create_function db tokens tokens + +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE x1 USING fts5( + v, tokenize="origintext unicode61", tokendata=1, detail=none + ); + + INSERT INTO x1 VALUES('xxx Xxx XXX yyy YYY yyy'); + INSERT INTO x1 VALUES('xxx yyy xxx yyy yyy yyy'); +} + +do_execsql_test 2.1 { + SELECT tokens(x1) FROM x1('xxx'); +} { + {xxx xxx.Xxx xxx.XXX} {xxx xxx} +} + +do_execsql_test 2.2 { + UPDATE x1_content SET c0 = 'xxx xxX xxx yyy yyy yyy' WHERE id=1; +} + +do_execsql_test 2.3 { + SELECT tokens(x1) FROM x1('xxx'); +} { + {xxx {} xxx} {xxx xxx} +} + finish_test diff --git a/ext/fts5/test/fts5origintext3.test b/ext/fts5/test/fts5origintext3.test index ac00bfabc0..834844595d 100644 --- a/ext/fts5/test/fts5origintext3.test +++ b/ext/fts5/test/fts5origintext3.test @@ -74,6 +74,27 @@ foreach_detail_mode $testprefix { do_execsql_test 1.6 { SELECT insttoken(ft2, 0, 0), rowid FROM ft2('three') ORDER BY rank; } {three.THREE 3 three 1 three 2} + + do_execsql_test 1.7 { + INSERT INTO ft2(rowid, x) VALUES(10, 'aaa bbb BBB'); + INSERT INTO ft2(rowid, x) VALUES(12, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(13, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(14, 'bbb BBB bbb'); + INSERT INTO ft2(rowid, x) VALUES(15, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(16, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(17, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(18, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(19, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(20, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(21, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(22, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(23, 'bbb bbb bbb'); + INSERT INTO ft2(rowid, x) VALUES(24, 'aaa bbb BBB'); + } + + do_execsql_test 1.8 { SELECT rowid FROM ft2('aaa AND bbb'); } {10 24} + do_execsql_test 1.9 { SELECT rowid FROM ft2('bbb AND aaa'); } {10 24} + } finish_test diff --git a/ext/fts5/test/fts5origintext5.test b/ext/fts5/test/fts5origintext5.test index 89a5d8f39c..03d5bee215 100644 --- a/ext/fts5/test/fts5origintext5.test +++ b/ext/fts5/test/fts5origintext5.test @@ -121,7 +121,7 @@ proc ctrl_tokens {term args} { db func ctrl_tokens ctrl_tokens proc do_all_vocab_test {tn} { - foreach ::v [vocab] { + foreach ::v [concat [vocab] nnn] { set answer [execsql { SELECT id, ctrl_tokens($::v, x) FROM ctrl WHERE x LIKE '%' || $::v || '%' }] @@ -134,7 +134,6 @@ proc do_all_vocab_test {tn} { } } - do_execsql_test 1.0 { CREATE VIRTUAL TABLE ft USING fts5( x, tokenize="origintext unicode61", content=, contentless_delete=1, @@ -229,5 +228,46 @@ for {set ii 0} {$ii < $NLOOP} {incr ii} { do_all_vocab_test 2.3.$ii } +#------------------------------------------------------------------------- + +unset -nocomplain ::expanded_vocab +proc vocab {} { + list abcde fghij klmno +} + +proc do_all_vocab_test3 {tn} { + foreach ::v [concat [vocab] nnn] { + set answer [execsql { + SELECT rowid, ctrl_tokens($::v, w) FROM ctrl3 WHERE w LIKE '%' || $::v || '%' + }] + do_execsql_test $tn.$::v.1 { + SELECT rowid, tokens(ft3) FROM ft3($::v) + } $answer + do_execsql_test $tn.$::v.2 { + SELECT rowid, tokens(ft3) FROM ft3($::v) ORDER BY rank + } $answer + } +} + +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE ft3 USING fts5( + w, tokenize="origintext unicode61", content=, contentless_delete=1, + tokendata=1 + ); + INSERT INTO ft3(ft3, rank) VALUES('rank', 'rankfunc()'); + CREATE TABLE ctrl3(w); +} + +do_execsql_test 3.1 { + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<2 + ) + INSERT INTO ctrl3 SELECT document() FROM s; + INSERT INTO ft3(rowid, w) SELECT rowid, w FROM ctrl3; +} + +do_all_vocab_test3 3.2 + + finish_test diff --git a/manifest b/manifest index 11d57c4176..91a29a053c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sfurther\stests\sfor\sxInstToken(). -D 2023-12-04T19:48:08.530 +C Further\stests\sfor\sthe\snew\scode\son\sthis\sbranch. +D 2023-12-05T18:36:23.618 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -94,9 +94,9 @@ F ext/fts5/fts5Int.h defa43c0932265138ee910ca416e6baccf8b774e0f3d610e74be1ab2880 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf -F ext/fts5/fts5_expr.c 920516be4aac44eccdd9e747fe26f367442848b9a58971e4b36edb0c8284b6b8 +F ext/fts5/fts5_expr.c b1ec526371b9ffde82341423a5b9753c42cbea629a41b69f26fa377d13b95a8e F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c 7111fed6c01048e227cc8c681306fa2db1e5ad29429b1373e665b8e95a7868ba +F ext/fts5/fts5_index.c be39b44ff8773cff56bcbc01f74701a83e068c20d773cafd01e8bb2fa0fc1bc5 F ext/fts5/fts5_main.c fb7ec495d663f40d18e420e1986316591041a70e1e4b4696ab2a7384e4c7fd7a F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -170,6 +170,7 @@ F ext/fts5/test/fts5faultD.test e7ed7895abfe6bc98a5e853826f6b74956e7ba7f594f1860 F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e4710c77eb8ce7075 F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1 F ext/fts5/test/fts5faultG.test d2e5a4d9a34e08dcaadcaeafef74d10cbc2abdd11aa2659a18af0294bf2812d3 +F ext/fts5/test/fts5faultH.test d845f45dac3e1a3f20c7e0a2be95280c95d3204c06802f86ab2c110e52ed3d14 F ext/fts5/test/fts5first.test 3fcf2365c00a15fc9704233674789a3b95131d12de18a9b996159f6909dc8079 F ext/fts5/test/fts5full.test e1701a112354e0ff9a1fdffb0c940c576530c33732ee20ac5e8361777070d717 F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e @@ -191,10 +192,10 @@ F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696 F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca F ext/fts5/test/fts5origintext.test d2796fa08ee7aecfabdc0c45bb8a2fb16a00ea8757e63fbc153b718dbe430a39 -F ext/fts5/test/fts5origintext2.test 43b07dd62d087743322b0003a27c8efdbda6c8659a27fde71f32ead27b5a0969 -F ext/fts5/test/fts5origintext3.test e0d47c187e7c279d25aa27aa3de8dd0d26b050a74db90670c9b20d0ecfcfb52a +F ext/fts5/test/fts5origintext2.test f3b9436de540828d01f0672df855b09ebc0863e126d5b56234701d71dfa73634 +F ext/fts5/test/fts5origintext3.test 0d25933506600452a5ab3873cbb418ed5f2de2446c3672b9997b1ea104b0e7f0 F ext/fts5/test/fts5origintext4.test 296b1b1e6630d492b99db0769e8127087548f0e939376047716a68b77ca3c871 -F ext/fts5/test/fts5origintext5.test 067bfb3008323585df640ab29e8ef7c4ca6dec62c597be07a9f896d88f98cd10 +F ext/fts5/test/fts5origintext5.test a037bdf7235a22033c4663837bdb12d9738245464a3ac2f60c71fc40d07ede7d F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -2150,8 +2151,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 78fbb71598b1ca756acc078253880a1d0f7983a5a26b9efc683e6488122505a1 -R ced1cc2c59284a9ef3026043e080f745 +P 8582707f16133f003a6687f68cbea03d4eb6c2a0e2e07746b7cace0c44e84fa4 +R c18539c880ee946e73119264163283e7 U dan -Z e2cce7acff968ef3c9472723192ae452 +Z 86bd4754d1465fbe5cee9e784de6e900 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 780ed88175..c1e3ee2ba4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8582707f16133f003a6687f68cbea03d4eb6c2a0e2e07746b7cace0c44e84fa4 \ No newline at end of file +59d008b6c23ab900377bc696ee19381feb7614bac80546eae361e401c3620c4e \ No newline at end of file From 5afd67b3c38d9af1fe7ad4a04567e5a8aaf5746f Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 5 Dec 2023 19:24:07 +0000 Subject: [PATCH 337/347] Use extra assert() statement to silence harmless static analyzer warnings. FossilOrigin-Name: 174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/json.c | 5 ++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index ff6dfffb99..4206539101 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Miscellaneous\scomment\scleanup\sand\stypo\sfixes. -D 2023-12-05T18:28:15.619 +C Use\sextra\sassert()\sstatement\sto\ssilence\sharmless\sstatic\sanalyzer\swarnings. +D 2023-12-05T19:24:07.192 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -688,7 +688,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 00480474f134f18d77275de3fe1220b198059af39d41b8549f14af991ee50c64 +F src/json.c 0a6095d10a8c8251e1838a1f12abc89125b0b05f82497ad12896a7f714e397ce F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2145,8 +2145,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 843197df08352bdff4b87be91d160e574572aded0d0c66142fd960000c0b4701 -R 51649fd2031c328be393a9b165de9b28 +P 59446dc0bd0091572122a3c8b4653d7a2dc867d16c4a5919f79b81bc3a673ce3 +R 901a6c57f6544c02dc1f928ffbc0f678 U drh -Z 92ba2a23f6f1deba28c66b8e97e6bff1 +Z d3a76b74f5e90cbdae4d0a0216cf9ffb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bdb4e91e40..4658b8623b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -59446dc0bd0091572122a3c8b4653d7a2dc867d16c4a5919f79b81bc3a673ce3 \ No newline at end of file +174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 15f99d49b2..8165eb4f31 100644 --- a/src/json.c +++ b/src/json.c @@ -1119,6 +1119,7 @@ static void jsonBlobAppendNode( jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); return; } + assert( pParse->aBlob!=0 ); a = &pParse->aBlob[pParse->nBlob]; if( szPayload<=11 ){ a[0] = eType | (szPayload<<4); @@ -2288,6 +2289,8 @@ static u32 jsonLookupStep( nIns = ix.nBlob + nKey + v.nBlob; jsonBlobEdit(pParse, j, 0, 0, nIns); if( !pParse->oom ){ + assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */ + assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); k = j + ix.nBlob; memcpy(&pParse->aBlob[k], zKey, nKey); @@ -4406,7 +4409,7 @@ static int jsonEachColumn( } case JEACH_TYPE: { u32 i = jsonSkipLabel(p); - u8 eType = eType = p->sParse.aBlob[i] & 0x0f; + u8 eType = p->sParse.aBlob[i] & 0x0f; sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); break; } From 1f2d7c43120fe831fd63f7fedcc61b15a17fccee Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 6 Dec 2023 12:30:28 +0000 Subject: [PATCH 338/347] README.md typo fix reported in the forum and update all links from http: to https:. FossilOrigin-Name: 5c48acdbb44185b352b54911a57a6986d6c7e624bdeba2af48b985d29f0292bf --- README.md | 20 ++++++++++---------- manifest | 15 +++++++-------- manifest.uuid | 2 +- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index ebb917e218..0975a32d9a 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ extension and only later escaped to the wild as an independent library.) Test scripts and programs are found in the **test/** subdirectory. Additional test code is found in other source repositories. -See [How SQLite Is Tested](http://www.sqlite.org/testing.html) for +See [How SQLite Is Tested](https://www.sqlite.org/testing.html) for additional information. The **ext/** subdirectory contains code for extensions. The @@ -183,7 +183,7 @@ manually-edited files and automatically-generated files. The SQLite interface is defined by the **sqlite3.h** header file, which is generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The -[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. +[Tcl script](https://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. The manifest.uuid file contains the SHA3 hash of the particular check-in and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file contains the current SQLite version number. The sqlite3.h header is really @@ -250,14 +250,14 @@ individual source file exceeds 32K lines in length. ## How It All Fits Together SQLite is modular in design. -See the [architectural description](http://www.sqlite.org/arch.html) +See the [architectural description](https://www.sqlite.org/arch.html) for details. Other documents that are useful in (helping to understand how SQLite works include the -[file format](http://www.sqlite.org/fileformat2.html) description, -the [virtual machine](http://www.sqlite.org/opcode.html) that runs +[file format](https://www.sqlite.org/fileformat2.html) description, +the [virtual machine](https://www.sqlite.org/opcode.html) that runs prepared statements, the description of -[how transactions work](http://www.sqlite.org/atomiccommit.html), and -the [overview of the query planner](http://www.sqlite.org/optoverview.html). +[how transactions work](https://www.sqlite.org/atomiccommit.html), and +the [overview of the query planner](https://www.sqlite.org/optoverview.html). Years of effort have gone into optimizing SQLite, both for small size and high performance. And optimizations tend to result in @@ -353,7 +353,7 @@ hidden by also modifying the makefiles. ## Contacts -The main SQLite website is [http:/sqlite.org/](http://sqlite.org/) +The main SQLite website is [https://sqlite.org/](https://sqlite.org/) with geographically distributed backups at -[http://www2.sqlite.org/](http://www2.sqlite.org) and -[http://www3.sqlite.org/](http://www3.sqlite.org). +[https://www2.sqlite.org/](https://www2.sqlite.org) and +[https://www3.sqlite.org/](https://www3.sqlite.org). diff --git a/manifest b/manifest index c0dab2ba23..6d47dd3e83 100644 --- a/manifest +++ b/manifest @@ -1,12 +1,12 @@ -C Rework\sthe\sJSON\sfunctions\sso\sthat\sthey\suse\sthe\sJSONB\sformat\sinternally.\nThe\soriginal\sJsonNode\sparse\stree\sdesign\sis\sremoved.\s\sAll\sJSON\sfunctions\nthat\saccept\stext\sJSON\salso\saccept\sJSONB.\s\sNew\sfunctions\sgenerate\sJSONB. -D 2023-12-05T19:45:09.048 +C README.md\stypo\sfix\sreported\sin\sthe\sforum\sand\supdate\sall\slinks\sfrom\shttp:\sto\shttps:. +D 2023-12-06T12:30:28.174 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F Makefile.in 890caf094636c308bc981032baf8f9208bf307755f9197ae4218a9fbcec2e449 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc 59bb36dba001f0b38212be0794fb838f25371008b180929bcf08aa799694c168 -F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 +F README.md 6358805260a03ebead84e168bbf3740ddf3f683b477e478567186aa7afb490d3 F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 @@ -2147,9 +2147,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 8abc2ccaf8106f20243568cd7fa74174386eb85d7ea381201e97e2fd527033e0 174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 -R 2fc453038906bf8ee420a799d6f3c8a1 -T +closed 174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 -U drh -Z fc61504cc821223c214ac8778fb2caf0 +P 7f0c79b94e8f55e5013e52ba64ba8b32dad1dc4e2224d2099733cbc561de1810 +R e4c4253b529b92ef6212320f71e1570e +U stephan +Z 02e09a8447ded58f933fad2e169fffdc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 634d6e05e0..f935efea29 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7f0c79b94e8f55e5013e52ba64ba8b32dad1dc4e2224d2099733cbc561de1810 \ No newline at end of file +5c48acdbb44185b352b54911a57a6986d6c7e624bdeba2af48b985d29f0292bf \ No newline at end of file From 91ec00c25acd25bd9a6bc2345436370d23723653 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 14:50:48 +0000 Subject: [PATCH 339/347] Increased rigor in comparisons between object labels in JSON. FossilOrigin-Name: 2bc86d145fccc07107b7753cb1a69122676d4096fe59c454497bd81a6142d45e --- manifest | 16 +-- manifest.uuid | 2 +- src/json.c | 311 +++++++++++++++++++++++++++++++++++--------------- 3 files changed, 228 insertions(+), 101 deletions(-) diff --git a/manifest b/manifest index c0dab2ba23..918632105e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rework\sthe\sJSON\sfunctions\sso\sthat\sthey\suse\sthe\sJSONB\sformat\sinternally.\nThe\soriginal\sJsonNode\sparse\stree\sdesign\sis\sremoved.\s\sAll\sJSON\sfunctions\nthat\saccept\stext\sJSON\salso\saccept\sJSONB.\s\sNew\sfunctions\sgenerate\sJSONB. -D 2023-12-05T19:45:09.048 +C Increased\srigor\sin\scomparisons\sbetween\sobject\slabels\sin\sJSON. +D 2023-12-06T14:50:48.135 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -690,7 +690,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 0a6095d10a8c8251e1838a1f12abc89125b0b05f82497ad12896a7f714e397ce +F src/json.c 15efd213cc95bde5b714ec068fdb1f6817b1ed9a253644833fed5f196957445a F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2147,9 +2147,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8abc2ccaf8106f20243568cd7fa74174386eb85d7ea381201e97e2fd527033e0 174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 -R 2fc453038906bf8ee420a799d6f3c8a1 -T +closed 174c2b2eef5fecd96a5fc89b81032fe81f7801f12097cea10e7e7f0a02114813 +P 7f0c79b94e8f55e5013e52ba64ba8b32dad1dc4e2224d2099733cbc561de1810 +R 2684d5ad233d4af8d84cd782c9755832 +T *branch * json-label-compare +T *sym-json-label-compare * +T -sym-trunk * U drh -Z fc61504cc821223c214ac8778fb2caf0 +Z 93dccc3939eaec1e85cde17c2d8ca8af # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 634d6e05e0..6aa957e936 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7f0c79b94e8f55e5013e52ba64ba8b32dad1dc4e2224d2099733cbc561de1810 \ No newline at end of file +2bc86d145fccc07107b7753cb1a69122676d4096fe59c454497bd81a6142d45e \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8165eb4f31..37a0ebfe1a 100644 --- a/src/json.c +++ b/src/json.c @@ -2119,6 +2119,188 @@ static void jsonBlobEdit( if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); } +/* +** Return the number of escaped newlines to be ignored. +** An escaped newline is a one of the following byte sequences: +** +** 0x5c 0x0a +** 0x5c 0x0d +** 0x5c 0x0d 0x0a +** 0x5c 0xe2 0x80 0xa8 +** 0x5c 0xe2 0x80 0xa9 +*/ +static u32 jsonBytesToBypass(const char *z, u32 n){ + u32 i = 0; + while( i+10 ); + assert( z[0]=='\\' ); + if( n<2 ){ + *piOut = 0xFFFD; + return n; + } + switch( (u8)z[1] ){ + case 'u': { + u32 v, vlo; + if( n<6 ){ + *piOut = 0xFFFD; + return n; + } + v = jsonHexToInt4(&z[2]); + if( (v & 0xfc00)==0xd800 + && n>=12 + && z[6]=='\\' + && z[7]=='u' + && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00 + ){ + *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000; + return 12; + }else{ + *piOut = v; + return 6; + } + } + case 'b': { *piOut = '\b'; return 2; } + case 'f': { *piOut = '\f'; return 2; } + case 'n': { *piOut = '\n'; return 2; } + case 'r': { *piOut = '\r'; return 2; } + case 't': { *piOut = '\t'; return 2; } + case 'v': { *piOut = '\v'; return 2; } + case '0': { *piOut = 0; return 2; } + case '\'': + case '"': + case '/': + case '\\':{ *piOut = z[1]; return 2; } + case 'x': { + if( n<4 ){ + *piOut = 0xFFFD; + return n; + } + *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]); + return 4; + } + case 0xe2: + case '\r': + case '\n': { + u32 nSkip = jsonBytesToBypass(z, n); + if( nSkip==0 ){ + *piOut = 0xFFFD; + return n; + }else if( nSkip==n ){ + *piOut = 0; + return n; + }else if( z[nSkip]=='\\' ){ + return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut); + }else{ + *piOut = z[nSkip]; + return nSkip+1; + } + } + default: { + *piOut = 0xFFFD; + return 2; + } + } +} + + +/* +** Compare two object labels. Return 1 if they are equal and +** 0 if they differ. +** +** In this version, we know that one or the other or both of the +** two comparands contains an escape sequence. +*/ +static SQLITE_NOINLINE int jsonLabelCompareEscaped( + const char *zLeft, /* The left label */ + u32 nLeft, /* Size of the left label in bytes */ + int rawLeft, /* True if zLeft contains no escapes */ + const char *zRight, /* The right label */ + u32 nRight, /* Size of the right label in bytes */ + int rawRight /* True if zRight is escape-free */ +){ + u32 cLeft, cRight; + assert( rawLeft==0 || rawRight==0 ); + while( nLeft>0 && nRight>0 ){ + if( rawLeft || zLeft[0]!='\\' ){ + cLeft = ((u8*)zLeft)[0]; + zLeft++; + nLeft--; + }else{ + u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft); + zLeft += n; + assert( n<=nLeft ); + nLeft -= n; + } + if( rawRight || zRight[0]!='\\' ){ + cRight = ((u8*)zRight)[0]; + zRight++; + nRight--; + }else{ + u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight); + zRight += n; + assert( n<=nRight ); + nRight -= n; + } + if( cLeft!=cRight ) return 0; + } + return nLeft==0 && nRight==0; +} + +/* +** Compare two object labels. Return 1 if they are equal and +** 0 if they differ. Return -1 if an OOM occurs. +*/ +static int jsonLabelCompare( + const char *zLeft, /* The left label */ + u32 nLeft, /* Size of the left label in bytes */ + int rawLeft, /* True if zLeft contains no escapes */ + const char *zRight, /* The right label */ + u32 nRight, /* Size of the right label in bytes */ + int rawRight /* True if zRight is escape-free */ +){ + if( rawLeft && rawRight ){ + /* Simpliest case: Neither label contains escapes. A simple + ** memcmp() is sufficient. */ + if( nLeft!=nRight ) return 0; + return memcmp(zLeft, zRight, nLeft)==0; + }else{ + return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft, + zRight, nRight, rawRight); + } +} + /* ** Error returns from jsonLookupStep() */ @@ -2224,6 +2406,7 @@ static u32 jsonLookupStep( return iRoot; } if( zPath[0]=='.' ){ + int rawKey = 1; x = pParse->aBlob[iRoot]; zPath++; if( zPath[0]=='"' ){ @@ -2236,6 +2419,7 @@ static u32 jsonLookupStep( return JSON_LOOKUP_PATHERROR; } testcase( nKey==0 ); + rawKey = memchr(zKey, '\\', nKey)==0; }else{ zKey = zPath; for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} @@ -2249,13 +2433,17 @@ static u32 jsonLookupStep( j = iRoot + n; /* j is the index of a label */ iEnd = j+sz; while( jaBlob[j] & 0x0f; if( xJSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_LOOKUP_ERROR; k = j+n; /* k is the index of the label text */ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; - if( sz==nKey && memcmp(&pParse->aBlob[k], zKey, nKey)==0 ){ + zLabel = (const char*)&pParse->aBlob[k]; + rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW; + if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){ u32 v = k+sz; /* v is the index of the value */ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; n = jsonbPayloadSize(pParse, v, &sz); @@ -2279,7 +2467,7 @@ static u32 jsonLookupStep( testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); memset(&ix, 0, sizeof(ix)); - jsonBlobAppendNode(&ix,JSONB_TEXTRAW, nKey, 0); + jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); pParse->oom |= ix.oom; rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); if( !JSON_LOOKUP_ISERROR(rc) @@ -2483,72 +2671,27 @@ static void jsonReturnFromBlob( for(iIn=iOut=0; iIn>6)); - zOut[iOut++] = 0x80 | (v&0x3f); - }else{ - u32 vlo; - if( (v&0xfc00)==0xd800 - && iIn>18); - zOut[iOut++] = 0x80 | ((v>>12)&0x3f); - zOut[iOut++] = 0x80 | ((v>>6)&0x3f); - zOut[iOut++] = 0x80 | (v&0x3f); - }else{ - zOut[iOut++] = 0xe0 | (v>>12); - zOut[iOut++] = 0x80 | ((v>>6)&0x3f); - zOut[iOut++] = 0x80 | (v&0x3f); - } - } - continue; - }else if( c=='b' ){ - c = '\b'; - }else if( c=='f' ){ - c = '\f'; - }else if( c=='n' ){ - c = '\n'; - }else if( c=='r' ){ - c = '\r'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='v' ){ - c = '\v'; - }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ - /* pass through unchanged */ - }else if( c=='0' ){ - c = 0; - }else if( c=='x' ){ - c = (jsonHexToInt(z[iIn+1])<<4) | jsonHexToInt(z[iIn+2]); - iIn += 2; - }else if( c=='\r' && z[i+1]=='\n' ){ - iIn++; - continue; - }else if( 0xe2==(u8)c - && iIn>6)); + zOut[iOut++] = 0x80 | (v&0x3f); + }else if( v<0x10000 ){ + zOut[iOut++] = 0xe0 | (v>>12); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); }else{ - continue; + zOut[iOut++] = 0xf0 | (v>>18); + zOut[iOut++] = 0x80 | ((v>>12)&0x3f); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); } - } /* end if( c=='\\' ) */ - zOut[iOut++] = c; + iIn += szEscape - 1; + }else{ + zOut[iOut++] = c; + } } /* end for() */ zOut[iOut] = 0; sqlite3_result_text(pCtx, zOut, iOut, sqlite3_free); @@ -3384,6 +3527,7 @@ static int jsonMergePatch( iTCursor = iTStart; iTEnd = iTEndBE + pTarget->delta; while( iTCursoraBlob[iTCursor] & 0x0f; if( eTLabelJSONB_TEXTRAW ){ @@ -3396,33 +3540,14 @@ static int jsonMergePatch( nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); if( nTValue==0 ) return JSON_MERGE_BADTARGET; if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; - if( eTLabel==ePLabel ){ - /* Common case */ - if( szTLabel==szPLabel - && memcmp(&pTarget->aBlob[iTLabel+nTLabel], - &pPatch->aBlob[iPLabel+nPLabel], szTLabel)==0 - ){ - break; /* Labels match. */ - } - }else{ - /* Should rarely happen */ - JsonString s1, s2; - int isEqual, isOom; - jsonStringInit(&s1, 0); - jsonXlateBlobToText(pTarget, iTLabel, &s1); - jsonStringInit(&s2, 0); - jsonXlateBlobToText(pPatch, iPLabel, &s2); - isOom = s1.eErr || s2.eErr; - if( s1.nUsed==s2.nUsed && memcmp(s1.zBuf, s2.zBuf, s1.nUsed)==0 ){ - isEqual = 1; - }else{ - isEqual = 0; - } - jsonStringReset(&s1); - jsonStringReset(&s2); - if( isOom ) return JSON_MERGE_OOM; - if( isEqual ) break; - } + isEqual = jsonLabelCompare( + (const char*)&pPatch->aBlob[iPLabel+nPLabel], + szPLabel, + (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW), + (const char*)&pTarget->aBlob[iTLabel+nTLabel], + szTLabel, + (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW)); + if( isEqual ) break; iTCursor = iTValue + nTValue + szTValue; } x = pPatch->aBlob[iPValue] & 0x0f; From 6a8581d828e2c6b940cdf19868c309dde4322ea9 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 15:35:38 +0000 Subject: [PATCH 340/347] The rule for the RHS of the ->> and -> operators when the RHS does not begin with $ is that it must be (1) all digits, or (2) all alphanumerics, or (3) contained within [..] or else it will become a quoted label. FossilOrigin-Name: 0e059a546ec11fa5c6d007bd65c249ee2422f1facbdb2792c53e0bc0ccc97e14 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/json.c | 24 +++++++++++++++++++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/manifest b/manifest index 918632105e..173207b80f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Increased\srigor\sin\scomparisons\sbetween\sobject\slabels\sin\sJSON. -D 2023-12-06T14:50:48.135 +C The\srule\sfor\sthe\sRHS\sof\sthe\s->>\sand\s->\soperators\swhen\sthe\sRHS\sdoes\snot\sbegin\nwith\s$\sis\sthat\sit\smust\sbe\s(1)\sall\sdigits,\sor\s(2)\sall\salphanumerics,\sor\n(3)\scontained\swithin\s[..]\sor\selse\sit\swill\sbecome\sa\squoted\slabel. +D 2023-12-06T15:35:38.860 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -690,7 +690,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 15efd213cc95bde5b714ec068fdb1f6817b1ed9a253644833fed5f196957445a +F src/json.c 9d0b8fa594a931d3f84056020c6581bb631cee02ea5f0530a4fd5876a2cb6560 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2147,11 +2147,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 7f0c79b94e8f55e5013e52ba64ba8b32dad1dc4e2224d2099733cbc561de1810 -R 2684d5ad233d4af8d84cd782c9755832 -T *branch * json-label-compare -T *sym-json-label-compare * -T -sym-trunk * +P 2bc86d145fccc07107b7753cb1a69122676d4096fe59c454497bd81a6142d45e +R 5951a4c9f5e0f75219c7e2d971403d09 U drh -Z 93dccc3939eaec1e85cde17c2d8ca8af +Z 93fae7007061a25e3b1f2103eb12e9f0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6aa957e936..121f08ea73 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2bc86d145fccc07107b7753cb1a69122676d4096fe59c454497bd81a6142d45e \ No newline at end of file +0e059a546ec11fa5c6d007bd65c249ee2422f1facbdb2792c53e0bc0ccc97e14 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 37a0ebfe1a..44abe2d164 100644 --- a/src/json.c +++ b/src/json.c @@ -3272,6 +3272,20 @@ static void jsonArrayLengthFunc( jsonParseFree(p); } +/* True if the string is all digits */ +static int jsonAllDigits(const char *z, int n){ + int i; + for(i=0; i"(JSON,PATH) @@ -3329,15 +3343,19 @@ static void jsonExtractFunc( ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); - if( sqlite3Isdigit(zPath[0]) ){ + if( jsonAllDigits(zPath, nPath) ){ jsonAppendRawNZ(&jx, "[", 1); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "]", 2); - }else if( zPath[0]!='[' ){ + }else if( jsonAllAlphanum(zPath, nPath) ){ jsonAppendRawNZ(&jx, ".", 1); jsonAppendRaw(&jx, zPath, nPath); - }else{ + }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){ jsonAppendRaw(&jx, zPath, nPath); + }else{ + jsonAppendRawNZ(&jx, ".\"", 2); + jsonAppendRaw(&jx, zPath, nPath); + jsonAppendRawNZ(&jx, "\"", 1); } jsonStringTerminate(&jx); j = jsonLookupStep(p, 0, jx.zBuf, 0); From 8dfbf4addce7f8bf4d4fa3fd96a41aea11d9b41d Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 15:50:13 +0000 Subject: [PATCH 341/347] Test cases for object label matching with escape sequences. FossilOrigin-Name: c6f2aa38e95b7888650cfa7bb773b18a28e01d883033ac77be6d504ffe417d18 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/json502.test | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 173207b80f..0ec0180763 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\srule\sfor\sthe\sRHS\sof\sthe\s->>\sand\s->\soperators\swhen\sthe\sRHS\sdoes\snot\sbegin\nwith\s$\sis\sthat\sit\smust\sbe\s(1)\sall\sdigits,\sor\s(2)\sall\salphanumerics,\sor\n(3)\scontained\swithin\s[..]\sor\selse\sit\swill\sbecome\sa\squoted\slabel. -D 2023-12-06T15:35:38.860 +C Test\scases\sfor\sobject\slabel\smatching\swith\sescape\ssequences. +D 2023-12-06T15:50:13.951 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1333,7 +1333,7 @@ F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a888011 F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test e64a8d73677fbae67886642cd5076e2ef3efe89f8483b87595cf9c030216c9bd F test/json501.test ab168a12eb6eb14d479f8c1cdae3ac062fd5a4679f17f976e96f1af518408330 -F test/json502.test 98c38e3c4573841028a1381dfb81d4c3f9b105d39668167da10d055e503f6d0b +F test/json502.test 73dd17721c0b4a1320b163c3a1092d714aae5d100faf46f305515e1b3591c3a6 F test/jsonb01.test cace70765b36a36aec9a85a41ea65667d3bbf647d4400ddc3ac76f8fe7d94f90 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c 6e0228409ea7ca0497dad503fbd109badb5e59545d131014b6aaac68b56f484a @@ -2147,8 +2147,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 2bc86d145fccc07107b7753cb1a69122676d4096fe59c454497bd81a6142d45e -R 5951a4c9f5e0f75219c7e2d971403d09 +P 0e059a546ec11fa5c6d007bd65c249ee2422f1facbdb2792c53e0bc0ccc97e14 +R 4550d328e20b914ec87aa1544ecc75dd U drh -Z 93fae7007061a25e3b1f2103eb12e9f0 +Z 5fceb780d654fe6791112d65204ddb27 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 121f08ea73..6aa28612b9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0e059a546ec11fa5c6d007bd65c249ee2422f1facbdb2792c53e0bc0ccc97e14 \ No newline at end of file +c6f2aa38e95b7888650cfa7bb773b18a28e01d883033ac77be6d504ffe417d18 \ No newline at end of file diff --git a/test/json502.test b/test/json502.test index 595bf63318..ed61260e3b 100644 --- a/test/json502.test +++ b/test/json502.test @@ -36,5 +36,26 @@ do_catchsql_test 2.3 { SELECT '{a:null,{"h":[1,[1,2,3]],"j":"abc"}:true}'->'$h[#-1]'; } {1 {malformed JSON}} +# Verify that escaped label names are compared correctly. +# +do_execsql_test 3.1 { + SELECT '{"a\x62c":123}' ->> 'abc'; +} 123 +do_execsql_test 3.2 { + SELECT '{"abc":123}' ->> 'a\x62c'; +} 123 + +db null null +do_execsql_test 3.3 { + DROP TABLE t1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(json_insert('{}','$.a\',111,'$."b\\"',222)); + INSERT INTO t1 VALUES(jsonb_insert('{}','$.a\',111,'$."b\\"',222)); + SELECT x->'$.a\', x->'$.a\\', x->'$."a\\"', x->'$."b\\"' FROM t1; +} {111 null 111 222 111 null 111 222} + +do_execsql_test 3.4 { + SELECT json_patch('{"a\x62c":123}','{"ab\x63":456}') ->> 'abc'; +} 456 finish_test From 3207199c98f4b876934dbd4d09615175e43e8b37 Mon Sep 17 00:00:00 2001 From: larrybr Date: Wed, 6 Dec 2023 16:27:29 +0000 Subject: [PATCH 342/347] In CLI, move -interactive flag handling back to arg-loop pass 2. FossilOrigin-Name: 63cb05a862532d2d56e9e81fe32ced09bf58f03146587a118f11c2a84e195e69 --- manifest | 15 +++++++-------- manifest.uuid | 2 +- src/shell.c.in | 9 ++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index 99b9d7e832..484e4397c6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\stokendata=1\soption\sand\srelated\sAPIs\sto\sfts5. -D 2023-12-06T14:36:34.858 +C In\sCLI,\smove\s-interactive\sflag\shandling\sback\sto\sarg-loop\spass\s2. +D 2023-12-06T16:27:29.628 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -737,7 +737,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e -F src/shell.c.in 7bb83293775e1a5586d65212997442bc7acc70a2f1b781745da64ec3c2e4ea97 +F src/shell.c.in 9b6c3e641de45651ad0b5e9c26cd2f72efabee28179a5315d15c54239515ee3a F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2153,9 +2153,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 5c48acdbb44185b352b54911a57a6986d6c7e624bdeba2af48b985d29f0292bf 8f46eace86e7b2e556913575aa3cd6f7987ac0efcc880f0af649d42c253aeb81 -R aee08254c1ed5ae187dbc54a7e67d0a2 -T +closed 8f46eace86e7b2e556913575aa3cd6f7987ac0efcc880f0af649d42c253aeb81 -U dan -Z b8398992a8dd36d240de5bcbcb58489b +P a76a636b23c0ebd95d47fdf8358de4729e51a5f68f1a730cd4d89b378e94ac0d +R 66c08e25d4bc58dda9bee0684e3ee757 +U larrybr +Z 3fccc4c8303ad91f158ca989e0b5a086 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c9e8895225..025e6d00a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a76a636b23c0ebd95d47fdf8358de4729e51a5f68f1a730cd4d89b378e94ac0d \ No newline at end of file +63cb05a862532d2d56e9e81fe32ced09bf58f03146587a118f11c2a84e195e69 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 4a4a0e1fef..6d1312d0f8 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -12061,10 +12061,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-interactive")==0 ){ - /* Need to check for interactive override here to so that it can - ** affect console setup (for Windows only) and testing thereof. - */ - stdin_is_interactive = 1; }else if( cli_strcmp(z,"-batch")==0 ){ /* Need to check for batch mode here to so we can avoid printing ** informational messages (like from process_sqliterc) before @@ -12338,7 +12334,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); return 0; }else if( cli_strcmp(z,"-interactive")==0 ){ - /* already handled */ + /* Need to check for interactive override here to so that it can + ** affect console setup (for Windows only) and testing thereof. + */ + stdin_is_interactive = 1; }else if( cli_strcmp(z,"-batch")==0 ){ /* already handled */ }else if( cli_strcmp(z,"-utf8")==0 ){ From 9df01b5ccf78535dd44e1f8c0b83fcee40ea5042 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 16:57:18 +0000 Subject: [PATCH 343/347] Fix the routine that determines the json_tree.path value for the first row so that it correctly takes into account escape sequences in the path argument. FossilOrigin-Name: b9243ee8a37c62eb8848e765bd4af83bc1b3d3eb24fb4268a1357ad1f8b2e1fb --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 23 ++++++++++++----------- test/json502.test | 4 ++++ 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 0ec0180763..48427cf150 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\scases\sfor\sobject\slabel\smatching\swith\sescape\ssequences. -D 2023-12-06T15:50:13.951 +C Fix\sthe\sroutine\sthat\sdetermines\sthe\sjson_tree.path\svalue\sfor\sthe\sfirst\srow\nso\sthat\sit\scorrectly\stakes\sinto\saccount\sescape\ssequences\sin\sthe\spath\nargument. +D 2023-12-06T16:57:18.873 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -690,7 +690,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 9d0b8fa594a931d3f84056020c6581bb631cee02ea5f0530a4fd5876a2cb6560 +F src/json.c 89ce1dbcd8373a33adb82652842956c8a5d2b5c48d6f2c197d47bcee5fdedfd3 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -1333,7 +1333,7 @@ F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a888011 F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1 F test/json105.test e64a8d73677fbae67886642cd5076e2ef3efe89f8483b87595cf9c030216c9bd F test/json501.test ab168a12eb6eb14d479f8c1cdae3ac062fd5a4679f17f976e96f1af518408330 -F test/json502.test 73dd17721c0b4a1320b163c3a1092d714aae5d100faf46f305515e1b3591c3a6 +F test/json502.test 3c697e506fc38ccb455b49660b21b6e62e08ede0f2d0c869a7d171e17809093c F test/jsonb01.test cace70765b36a36aec9a85a41ea65667d3bbf647d4400ddc3ac76f8fe7d94f90 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/kvtest.c 6e0228409ea7ca0497dad503fbd109badb5e59545d131014b6aaac68b56f484a @@ -2147,8 +2147,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 0e059a546ec11fa5c6d007bd65c249ee2422f1facbdb2792c53e0bc0ccc97e14 -R 4550d328e20b914ec87aa1544ecc75dd +P c6f2aa38e95b7888650cfa7bb773b18a28e01d883033ac77be6d504ffe417d18 +R 31c04da1922b93d8052617096f6b59a6 U drh -Z 5fceb780d654fe6791112d65204ddb27 +Z a482eb5d0e0092ccc9abb42b468f8c79 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6aa28612b9..3fd26623b4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c6f2aa38e95b7888650cfa7bb773b18a28e01d883033ac77be6d504ffe417d18 \ No newline at end of file +b9243ee8a37c62eb8848e765bd4af83bc1b3d3eb24fb4268a1357ad1f8b2e1fb \ No newline at end of file diff --git a/src/json.c b/src/json.c index 44abe2d164..c9014f74a5 100644 --- a/src/json.c +++ b/src/json.c @@ -4489,22 +4489,23 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){ */ static int jsonEachPathLength(JsonEachCursor *p){ u32 n = p->path.nUsed; + const char *z = p->path.zBuf; if( p->iRowid==0 && p->bRecursive && n>1 ){ - if( p->path.zBuf[n-1]==']' ){ + if( z[n-1]==']' ){ do{ - assert( n>0 ); + assert( n>1 ); n--; - }while( p->path.zBuf[n]!='[' ); + }while( z[n]!='[' ); + }else if( z[n-1]=='"' ){ + do{ + assert( n>1 ); + n--; + }while( z[n]!='.' || z[n+1]!='"' ); }else{ - u32 sz = 0; - jsonbPayloadSize(&p->sParse, p->i, &sz); - if( p->path.zBuf[n-1]=='"' ) sz += 2; - assert( szpath.zBuf[n]!='.' && ALWAYS(n>0) ){ + do{ + assert( n>1 ); n--; - assert( n>0 ); - } + }while( z[n]!='.' ); } } return n; diff --git a/test/json502.test b/test/json502.test index ed61260e3b..48c372c4f8 100644 --- a/test/json502.test +++ b/test/json502.test @@ -58,4 +58,8 @@ do_execsql_test 3.4 { SELECT json_patch('{"a\x62c":123}','{"ab\x63":456}') ->> 'abc'; } 456 +do_execsql_test 4.1 { + SELECT * FROM json_tree('{"\u0017":1}','$."\x17"'); +} {{\x17} 1 integer 1 1 null {$."\x17"} {$}} + finish_test From b597fea89436a57b8b4bc39f24dc7e14f5e92dcb Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 17:39:31 +0000 Subject: [PATCH 344/347] Correctly handle 8-byte sizes in the JSONB format. [forum:/forumpost/283daf08e91183fc|Forum post 283daf08e91183fc]. FossilOrigin-Name: 73d390f39c0bbbc017e01544e4d43c76761f2599bd57f900131c706270dfd202 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/json.c | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 484e4397c6..e532282489 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sCLI,\smove\s-interactive\sflag\shandling\sback\sto\sarg-loop\spass\s2. -D 2023-12-06T16:27:29.628 +C Correctly\shandle\s8-byte\ssizes\sin\sthe\sJSONB\sformat.\n[forum:/forumpost/283daf08e91183fc|Forum\spost\s283daf08e91183fc]. +D 2023-12-06T17:39:31.569 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -696,7 +696,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 -F src/json.c 0a6095d10a8c8251e1838a1f12abc89125b0b05f82497ad12896a7f714e397ce +F src/json.c 07247c969e80e0a70241235ea4d00bb823830ce6a48f101fbcb1c72e8abf6f91 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 F src/main.c 1b89f3de98d1b59fec5bac1d66d6ece21f703821b8eaa0d53d9604c35309f6f9 @@ -2153,8 +2153,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 a76a636b23c0ebd95d47fdf8358de4729e51a5f68f1a730cd4d89b378e94ac0d -R 66c08e25d4bc58dda9bee0684e3ee757 -U larrybr -Z 3fccc4c8303ad91f158ca989e0b5a086 +P 63cb05a862532d2d56e9e81fe32ced09bf58f03146587a118f11c2a84e195e69 +R 3207d50f882d9851bd1114b702910d8f +U drh +Z 9fdc41964fbd72df8cb3bfd54d04d4f1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 025e6d00a4..fdb030fa0a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -63cb05a862532d2d56e9e81fe32ced09bf58f03146587a118f11c2a84e195e69 \ No newline at end of file +73d390f39c0bbbc017e01544e4d43c76761f2599bd57f900131c706270dfd202 \ No newline at end of file diff --git a/src/json.c b/src/json.c index 8165eb4f31..e91c9b86dd 100644 --- a/src/json.c +++ b/src/json.c @@ -1792,7 +1792,7 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ } sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; n = 3; - }else{ + }else if( x==14 ){ if( i+4>=pParse->nBlob ){ *pSz = 0; return 0; @@ -1800,6 +1800,19 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ sz = (pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; n = 5; + }else{ + if( i+8>=pParse->nBlob + || pParse->aBlob[i+1]!=0 + || pParse->aBlob[i+2]!=0 + || pParse->aBlob[i+3]!=0 + || pParse->aBlob[i+4]!=0 + ){ + *pSz = 0; + return 0; + } + sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + + (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; + n = 9; } if( i+sz+n > pParse->nBlob && i+sz+n > pParse->nBlob-pParse->delta From 83ac79282acb7c78647d1a7474c0061da450f124 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 6 Dec 2023 18:10:50 +0000 Subject: [PATCH 345/347] Update documentation comments in fts5.h. FossilOrigin-Name: 38c50e22c98607e6c1fd78d7615cda534773b6d4fd85c712b54749fcd7af0c83 --- ext/fts5/extract_api_docs.tcl | 4 +++- ext/fts5/fts5.h | 6 +++++- manifest | 17 ++++++++--------- manifest.uuid | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ext/fts5/extract_api_docs.tcl b/ext/fts5/extract_api_docs.tcl index 2320d70b7d..6762a036d5 100644 --- a/ext/fts5/extract_api_docs.tcl +++ b/ext/fts5/extract_api_docs.tcl @@ -223,10 +223,12 @@ proc main {data} { Fts5ExtensionApi { set struct [get_fts5_struct $data "^struct Fts5ExtensionApi" "^.;"] set map [list] + set lKey [list] foreach {k v} [get_struct_members $data] { if {[string match x* $k]==0} continue - lappend map $k "$k" + lappend lKey $k } + foreach k [lsort -decr $lKey] { lappend map $k "$k" } output [string map $map $struct] } diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 63c9765eb6..c7a5e6b7de 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -274,7 +274,11 @@ struct Fts5PhraseIter { ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the -** current row. +** current row. Output variable (*ppToken) is set to point to a buffer +** containing the matching document token, and (*pnToken) to the size +** of that buffer in bytes. This API is not available if the specified +** token matches a prefix query term. In that case both output variables +** are always set to 0. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this diff --git a/manifest b/manifest index a803cb2a64..68dc896475 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\scorrect\scomparisons\sbetween\sobject\slabels\sin\sJSON\seven\swhen\sthe\stwo\slabels\ncontain\sdifferent\sJSON\sescapes. -D 2023-12-06T17:50:16.616 +C Update\sdocumentation\scomments\sin\sfts5.h. +D 2023-12-06T18:10:50.884 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -88,8 +88,8 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6dbd6348ef0cfc324a7 F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb -F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h ff90acaa97f8e865b66d1177d1b56b8c110fd5548ab5863bab43f055a1d745fe +F ext/fts5/extract_api_docs.tcl bc3a0ca78be7d3df08e7602c00ca48021ebae40682d75eb001bfdf6e54ffb44e +F ext/fts5/fts5.h 9d7867cc63631296462c90bafe57c4881b56e480fa510ffaee6a77049258c714 F ext/fts5/fts5Int.h defa43c0932265138ee910ca416e6baccf8b774e0f3d610e74be1ab2880e9834 F ext/fts5/fts5_aux.c ee770eec0af8646db9e18fc01a0dad7345b5f5e8cbba236704cfae2d777022ad F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b729225eeaf6a5 @@ -2153,9 +2153,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 73d390f39c0bbbc017e01544e4d43c76761f2599bd57f900131c706270dfd202 b9243ee8a37c62eb8848e765bd4af83bc1b3d3eb24fb4268a1357ad1f8b2e1fb -R fd697485adcd3130674f1a92f5c02f3d -T +closed b9243ee8a37c62eb8848e765bd4af83bc1b3d3eb24fb4268a1357ad1f8b2e1fb -U drh -Z 9804c6a78387d2d6c50275c2413cc6ef +P bda2e30cc22e180b19a7a05824dd345880eb402ae5450b2d2dd954946c3ae135 +R 17f891969e2a492b1525813205840ba9 +U dan +Z b2b93165d3875994ffe922a4d9022c9c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bba984a924..0b4ae40007 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bda2e30cc22e180b19a7a05824dd345880eb402ae5450b2d2dd954946c3ae135 \ No newline at end of file +38c50e22c98607e6c1fd78d7615cda534773b6d4fd85c712b54749fcd7af0c83 \ No newline at end of file From 82fc1b63f6e89afec382e7ddbb2d3ffe3ac8cefe Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 6 Dec 2023 18:25:41 +0000 Subject: [PATCH 346/347] Work around LLVM's newfound hatred of function pointer casts. [forum:/forumpost/1a7d257346636292|Forum post 1a7d257346636292]. FossilOrigin-Name: ec0ae4030968c782af48d1c776351c14b2ada21d40aeb97915f33df30706e18f --- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/build.c | 12 +++++++++--- src/expr.c | 14 ++++++++------ src/select.c | 33 ++++++++++++++------------------- src/sqliteInt.h | 5 +++++ 6 files changed, 47 insertions(+), 39 deletions(-) diff --git a/manifest b/manifest index 68dc896475..5f7ad7a854 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sdocumentation\scomments\sin\sfts5.h. -D 2023-12-06T18:10:50.884 +C Work\saround\sLLVM's\snewfound\shatred\sof\sfunction\spointer\scasts.\n[forum:/forumpost/1a7d257346636292|Forum\spost\s1a7d257346636292]. +D 2023-12-06T18:25:41.085 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -678,7 +678,7 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c f3b09c5414de3a11db73e11e1d66f4c5e53c9e89876ff3b531a887ab656ca303 x F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240 F src/btreeInt.h 3e2589726c4f105e653461814f65857465da68be1fac688de340c43b873f4062 -F src/build.c d0bb02989e9e652ee8a012c7780e32087c0dd99643d0cf609971029f78cb738a +F src/build.c 386eadecabe2e99a3783eb802ca01e665f8e0c2af0e0aab28161fd7def219a9d F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b @@ -686,7 +686,7 @@ F src/date.c 3b8d02977d160e128469de38493b4085f7c5cf4073193459909a6af3cf6d7c91 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 -F src/expr.c e9a491c7f156e5b25641c28af11b735a424e108a21b9f83b6f3e51c99a8141d9 +F src/expr.c f4dbcca060fa95539f6705b5ec4cb5eeaba5c0d84ff64efca8edab7a1d776807 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00 F src/func.c 472f6dcfa39cf54f89a6aec76c79c225fb880a6c14469c15d361331662b9bf43 @@ -736,12 +736,12 @@ F src/printf.c 18fbdf028345c8fbe6044f5f5bfda5a10d48d6287afef088cc21b0ca57985640 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e +F src/select.c a5058ae54211dc49faade7b78f21c69a3fa3ee652eb270fba6881e28e30e0497 F src/shell.c.in 9b6c3e641de45651ad0b5e9c26cd2f72efabee28179a5315d15c54239515ee3a F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h aab66d149269f15f6f1011081b389f001f00b84045c69a4f9ec96dd68cc3a7d7 +F src/sqliteInt.h 39b6fe0652c763d8ff6bd0a2427c009e5abe05fb70f5a0cb145a34bcc614fb90 F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2153,8 +2153,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 bda2e30cc22e180b19a7a05824dd345880eb402ae5450b2d2dd954946c3ae135 -R 17f891969e2a492b1525813205840ba9 -U dan -Z b2b93165d3875994ffe922a4d9022c9c +P 38c50e22c98607e6c1fd78d7615cda534773b6d4fd85c712b54749fcd7af0c83 +R 46759d7734ecdddbd46479e0808766c4 +U drh +Z d4b23af7ef5680cfa48b70dc8dcb0c9b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0b4ae40007..0e3872e86a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -38c50e22c98607e6c1fd78d7615cda534773b6d4fd85c712b54749fcd7af0c83 \ No newline at end of file +ec0ae4030968c782af48d1c776351c14b2ada21d40aeb97915f33df30706e18f \ No newline at end of file diff --git a/src/build.c b/src/build.c index 14639f096e..50ce97b51a 100644 --- a/src/build.c +++ b/src/build.c @@ -873,6 +873,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; deleteTable(db, pTable); } +void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ + sqlite3DeleteTable(db, (Table*)pTable); +} /* @@ -1410,7 +1413,8 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ /* ** Clean up the data structures associated with the RETURNING clause. */ -static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){ +static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){ + Returning *pRet = (Returning*)pArg; Hash *pHash; pHash = &(db->aDb[1].pSchema->trigHash); sqlite3HashInsert(pHash, pRet->zName, 0); @@ -1452,8 +1456,7 @@ void sqlite3AddReturning(Parse *pParse, ExprList *pList){ pParse->u1.pReturning = pRet; pRet->pParse = pParse; pRet->pReturnEL = pList; - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet); + sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); testcase( pParse->earlyCleanup ); if( db->mallocFailed ) return; sqlite3_snprintf(sizeof(pRet->zName), pRet->zName, @@ -5692,4 +5695,7 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){ sqlite3DbFree(db, pWith); } } +void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){ + sqlite3WithDelete(db, (With*)pWith); +} #endif /* !defined(SQLITE_OMIT_CTE) */ diff --git a/src/expr.c b/src/expr.c index 8a664ffb9c..047d2b6b39 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1223,9 +1223,7 @@ void sqlite3ExprAddFunctionOrderBy( assert( ExprUseXList(pExpr) ); if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ /* Ignore ORDER BY on zero-argument aggregates */ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, - pOrderBy); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy); return; } if( IsWindowFunc(pExpr) ){ @@ -1406,6 +1404,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } +void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){ + if( p ) sqlite3ExprDeleteNN(db, (Expr*)p); +} /* ** Clear both elements of an OnOrUsing object @@ -1431,9 +1432,7 @@ void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ ** pExpr to the pParse->pConstExpr list with a register number of 0. */ void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprDelete, - pExpr); + sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the @@ -2239,6 +2238,9 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ if( pList ) exprListDeleteNN(db, pList); } +void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){ + if( pList ) exprListDeleteNN(db, (ExprList*)pList); +} /* ** Return the bitwise-OR of all Expr.flags fields in the given diff --git a/src/select.c b/src/select.c index e5312c7ee7..f61b34f5fe 100644 --- a/src/select.c +++ b/src/select.c @@ -184,6 +184,9 @@ Select *sqlite3SelectNew( void sqlite3SelectDelete(sqlite3 *db, Select *p){ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); } +void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){ + if( p ) clearSelect(db, (Select*)p, 1); +} /* ** Return a pointer to the right-most SELECT statement in a compound. @@ -3204,9 +3207,7 @@ multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; if( pDelete ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3SelectDelete, - pDelete); + sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete); } return rc; } @@ -3757,8 +3758,7 @@ static int multiSelectOrderBy( /* Make arrangements to free the 2nd and subsequent arms of the compound ** after the parse has finished */ if( pSplit->pPrior ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior); + sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior); } pSplit->pPrior = pPrior; pPrior->pNext = pSplit; @@ -4579,9 +4579,7 @@ static int flattenSubquery( Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); - sqlite3ParserAddCleanup(pToplevel, - (void(*)(sqlite3*,void*))sqlite3DeleteTable, - pTabToDel); + sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; @@ -5628,8 +5626,7 @@ static struct Cte *searchWith( With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ if( bFree ){ - pWith = (With*)sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3WithDelete, + pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric, pWith); if( pWith==0 ) return 0; } @@ -7037,7 +7034,8 @@ static SrcItem *isSelfJoinView( /* ** Deallocate a single AggInfo object */ -static void agginfoFree(sqlite3 *db, AggInfo *p){ +static void agginfoFree(sqlite3 *db, void *pArg){ + AggInfo *p = (AggInfo*)pArg; sqlite3DbFree(db, p->aCol); sqlite3DbFree(db, p->aFunc); sqlite3DbFreeNN(db, p); @@ -7291,9 +7289,8 @@ int sqlite3Select( sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); } #endif - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, - p->pOrderBy); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, + p->pOrderBy); testcase( pParse->earlyCleanup ); p->pOrderBy = 0; } @@ -7485,9 +7482,8 @@ int sqlite3Select( ){ TREETRACE(0x800,pParse,p, ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, - pSub->pOrderBy); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, + pSub->pOrderBy); pSub->pOrderBy = 0; } @@ -8016,8 +8012,7 @@ int sqlite3Select( */ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); if( pAggInfo ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))agginfoFree, pAggInfo); + sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo); testcase( pParse->earlyCleanup ); } if( db->mallocFailed ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c7ced86999..a0d5962005 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4803,6 +4803,7 @@ void sqlite3ExprOrderByAggregateError(Parse*,Expr*); void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); void sqlite3ExprDelete(sqlite3*, Expr*); +void sqlite3ExprDeleteGeneric(sqlite3*,void*); void sqlite3ExprDeferredDelete(Parse*, Expr*); void sqlite3ExprUnmapAndDelete(Parse*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); @@ -4812,6 +4813,7 @@ void sqlite3ExprListSetSortOrder(ExprList*,int,int); void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); void sqlite3ExprListDelete(sqlite3*, ExprList*); +void sqlite3ExprListDeleteGeneric(sqlite3*,void*); u32 sqlite3ExprListFlags(const ExprList*); int sqlite3IndexHasDuplicateRootPage(Index*); int sqlite3Init(sqlite3*, char**); @@ -4902,6 +4904,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int); void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3CodeDropTable(Parse*, Table*, int, int); void sqlite3DeleteTable(sqlite3*, Table*); +void sqlite3DeleteTableGeneric(sqlite3*, void*); void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT void sqlite3AutoincrementBegin(Parse *pParse); @@ -4938,6 +4941,7 @@ int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,u32,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); +void sqlite3SelectDeleteGeneric(sqlite3*,void*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, Trigger*); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); @@ -5510,6 +5514,7 @@ const char *sqlite3JournalModename(int); void sqlite3CteDelete(sqlite3*,Cte*); With *sqlite3WithAdd(Parse*,With*,Cte*); void sqlite3WithDelete(sqlite3*,With*); + void sqlite3WithDeleteGeneric(sqlite3*,void*); With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) From 00f3ac5544db5fcb86ae09b791b2d43945c31aac Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 6 Dec 2023 18:34:59 +0000 Subject: [PATCH 347/347] Fix compiler warning about shadowed variable in fts5_index.c. FossilOrigin-Name: ee70e4c1c9c41617850228e48d8df44f105cf2fbbe789340ceca6f27ad6ce5eb --- ext/fts5/fts5_index.c | 4 ++-- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 9a2cc026b9..7ec1976d33 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -6743,9 +6743,9 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ /* Allocate array of iterators if they are not already allocated. */ if( pT->aPoslistReader==0 ){ - int nByte = pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)); pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( - &pIter->pIndex->rc, nByte + &pIter->pIndex->rc, + pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) ); if( pT->aPoslistReader==0 ) return; pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; diff --git a/manifest b/manifest index 5f7ad7a854..10b2d5cbfd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\saround\sLLVM's\snewfound\shatred\sof\sfunction\spointer\scasts.\n[forum:/forumpost/1a7d257346636292|Forum\spost\s1a7d257346636292]. -D 2023-12-06T18:25:41.085 +C Fix\scompiler\swarning\sabout\sshadowed\svariable\sin\sfts5_index.c. +D 2023-12-06T18:34:59.649 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -96,7 +96,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292 F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf F ext/fts5/fts5_expr.c b1ec526371b9ffde82341423a5b9753c42cbea629a41b69f26fa377d13b95a8e F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c be39b44ff8773cff56bcbc01f74701a83e068c20d773cafd01e8bb2fa0fc1bc5 +F ext/fts5/fts5_index.c 111fdb22e4d78a5c3813ac2c782409b3567291a48f19ae834cd50268c8e9fafc F ext/fts5/fts5_main.c fb7ec495d663f40d18e420e1986316591041a70e1e4b4696ab2a7384e4c7fd7a F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca56d6601426d F ext/fts5/fts5_tcl.c cf0fd0dbe64ec272491b749e0d594f563cda03336aeb60900129e6d18b0aefb8 @@ -2153,8 +2153,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 38c50e22c98607e6c1fd78d7615cda534773b6d4fd85c712b54749fcd7af0c83 -R 46759d7734ecdddbd46479e0808766c4 -U drh -Z d4b23af7ef5680cfa48b70dc8dcb0c9b +P ec0ae4030968c782af48d1c776351c14b2ada21d40aeb97915f33df30706e18f +R edfe66d6aa99b5210c61e8b6b973888d +U dan +Z 66c944797bd4988ebf2844c99b4718ad # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0e3872e86a..414f169a76 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ec0ae4030968c782af48d1c776351c14b2ada21d40aeb97915f33df30706e18f \ No newline at end of file +ee70e4c1c9c41617850228e48d8df44f105cf2fbbe789340ceca6f27ad6ce5eb \ No newline at end of file