-C Fix\sa\sharmless\stypo\sin\sa\scode\scomment.
-D 2025-03-31T23:18:06.613
+C This\sis\san\sexperimental\soptimization\sthat\sattempts\sto\skeep\sa\sJSONB\svalue\nthe\ssame\ssize\s(same\snumber\sof\sbytes)\safter\sdoing\sa\sreplace\sof\san\selements\nwith\sa\sslightly\ssmaller\selement,\sby\sdenormalizing\sthe\ssize\sfield.\s\sThis\ncan\sperhaps\savoid\sunnecessary\spage\supdates\sand\smemmove()\soperations\swhen\nmaking\ssmall\schanges\sin\sthe\smiddle\sof\sa\slarge\sJSONB\svalue.
+D 2025-04-01T15:17:01.606
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c a5f0366266be993ebf533808f22cb7a788624805b55bc45424ceed3f48c54a16
-F src/json.c 3c3975ba06163fde503200cf2827b3d49c862b1285ebe1e7fd2057dfada48ef9
+F src/json.c 1de02e754ea25c5c02ac1edd7b3b5fd20d0da55e74190d0942d779a2a00bf07a
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
F src/main.c 07f78d917ffcdf327982840cfd8e855fd000527a2ea5ace372ce4febcbd0bf97
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P e64c6a3856b839e4e8c0a1cb1713b0d2f1d3cb9b915dd215b0d3cb229502d539
-R d6afe3ea0b11e3d65d616d932eff7348
+P dd251377bd1a8e95a4a0179c50595f290b08ea93659f4906f88f5a9dff534aa1
+R d43087fc8a1c599b5542c89f392aa309
+T *branch * json-opt
+T *sym-json-opt *
+T -sym-trunk *
U drh
-Z e51963c174f092cb0aa94669a8f5d2cf
+Z 9a10eb32279f050fc9d357fd9e297aac
# Remove this line to create a well-formed Fossil manifest.
** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
** accept BLOB values that have JSON encoded using a binary representation
** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
-** format SQLite JSONB is completely different and incompatible with
-** PostgreSQL JSONB.
+** format for 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
**
** 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,
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by one 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
** 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)
+** (as SQLite is currently unable to 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
}
-/* Append an node type byte together with the payload size and
+/* Append a 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
pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
}
+/*
+** If the JSONB at aIns[0..nIns-1] can be expanded (by denormalizing the
+** size field) by d bytes, then write the expansion into aOut[] and
+** return true. In this way, an overwrite happens without changing the
+** size of the JSONB, which reduces memcpy() operations and also make it
+** faster and easier to update the B-Tree entry that contains the JSONB
+** in the database.
+**
+** If the expansion of aIns[] by d bytes cannot be (easily) accomplished
+** then return false.
+**
+** The d parameter is guaranteed to be between 1 and 8.
+**
+** This routine is an optimization. A correct answer is obtained if it
+** always leaves the output unchanged and returns false.
+*/
+static int jsonBlobOverwrite(
+ u8 *aOut, /* Overwrite here */
+ const u8 *aIns, /* New content */
+ u32 nIns, /* Bytes of new content */
+ u32 d /* Need to expand new content by this much */
+){
+ u32 szPayload; /* Bytes of payload */
+ u32 i; /* New header size, after expansion & a loop counter */
+ u8 szHdr; /* Size of header before expansion */
+
+ /* Lookup table for finding the upper 4 bits of the first byte of the
+ ** expanded aIns[], based on the size of the expanded aIns[] header:
+ **
+ ** 2 3 4 5 6 7 8 9 */
+ static const u8 aType[] = { 0xc0, 0xd0, 0, 0xe0, 0, 0, 0, 0xf0 };
+
+ if( (aIns[0]&0x0f)<=2 ) return ; /* Cannot enlarge NULL, true, false */
+ switch( aIns[0]>>4 ){
+ default: { /* aIns[] header size 1 */
+ if( ((1<<d)&0x116)==0 ) return 0; /* d must be 1, 2, 4, or 8 */
+ i = d + 1; /* New hdr sz: 2, 3, 5, or 9 */
+ szHdr = 1;
+ break;
+ }
+ case 12: { /* aIns[] header size is 2 */
+ if( ((1<<d)&0x8a)==0) return 0; /* d must be 1, 3, or 7 */
+ i = d + 2; /* New hdr sz: 2, 5, or 9 */
+ szHdr = 2;
+ break;
+ }
+ case 13: { /* aIns[] header size is 3 */
+ if( d!=2 && d!=6 ) return 0; /* d must be 2 or 6 */
+ i = d + 3; /* New hdr sz: 5 or 9 */
+ szHdr = 3;
+ break;
+ }
+ case 14: { /* aIns[] header size is 5 */
+ if( d!=4 ) return 0; /* d must be 4 */
+ i = 9; /* New hdr sz: 9 */
+ szHdr = 5;
+ break;
+ }
+ case 15: { /* aIns[] header size is 9 */
+ return 0; /* No solution */
+ }
+ }
+ assert( i>=2 && i<=9 && aType[i-2]!=0 );
+ aOut[0] = (aIns[0] & 0x0f) | aType[i-2];
+ memcpy(&aOut[i], &aIns[szHdr], nIns-szHdr);
+ szPayload = nIns - szHdr;
+ while( 1/*edit-by-break*/ ){
+ i--;
+ aOut[i] = szPayload & 0xff;
+ if( i==1 ) break;
+ szPayload >>= 8;
+ }
+ assert( (szPayload>>8)==0 );
+ return 1;
+}
+
/*
** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
** content beginning at iDel, and replacing them with nIns bytes of
u32 nIns /* Bytes of content to insert */
){
i64 d = (i64)nIns - (i64)nDel;
+ if( d<0 && d>=(-8) && aIns!=0
+ && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d)
+ ){
+ return;
+ }
if( d!=0 ){
if( pParse->nBlob + d > pParse->nBlobAlloc ){
jsonBlobExpand(pParse, pParse->nBlob+d);
pParse->nBlob += d;
pParse->delta += d;
}
- if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+ if( nIns && aIns ){
+ memcpy(&pParse->aBlob[iDel], aIns, nIns);
+ }
}
/*