From 02d6f9b295827d5d545801f5babcc572389f8d5a Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 29 Jan 2021 16:20:16 +0000 Subject: [PATCH 1/3] Fix possible division-by-zero in the new log() SQL functions. Problemm discovered by OSSFuzz. FossilOrigin-Name: 1ffd321a33b778e87614a26a91a8407ec7b9dec4f0f847b16b1dac4f3b910604 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/func.c | 9 +++++---- test/func7.test | 8 ++++---- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 7045fd7798..95498bd054 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Performance\soptimization\s(and\ssize\sreduction)\sin\ssqlite3TriggerList()\sfor\sthe\ncommon\scase\swhere\sthere\sare\sno\sTEMP\striggers. -D 2021-01-29T13:47:36.426 +C Fix\spossible\sdivision-by-zero\sin\sthe\snew\slog()\sSQL\sfunctions.\nProblemm\sdiscovered\sby\sOSSFuzz. +D 2021-01-29T16:20:16.527 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -496,7 +496,7 @@ F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72 -F src/func.c 796a7a4a0ff5eee82a04ee3c8265c5ebf9c6a9f5625621c5f97ed94f6224d7d9 +F src/func.c 2ea99e9e0531b7f020d5e8e167d25344d618afc718ddc94dd91fa8fef1c85a91 F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 @@ -1030,7 +1030,7 @@ F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1 F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31 F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c -F test/func7.test bb05a77daedf0e3f8764f323a49bc3b8d98f280a0bc6a370387117f4596bde05 +F test/func7.test b9e2a1a30a8562b00841b4a21a5d2d81754fa3ab99275fd71fd5279287b44b1c F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1 F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 @@ -1898,7 +1898,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9dc7fc9f04d5c14fc436e5ff5b4c06c1969ddde5857ebeb5dccd59b7c748c339 -R 0b7a0544b0d15fb4a458f0ab87fbb410 +P 0defaf730bdc82212a5d3feeb2e16f16423b1691b0aaa7da1787eb82ea39ae9e +R 818a051c7c4bf2ae05824d55152903eb U drh -Z 6a477571c6e856bdd183f551e55df63b +Z b6b8cd840ef34d4d25e074519b6e4b42 diff --git a/manifest.uuid b/manifest.uuid index 6f87f82bcf..dc726d75c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0defaf730bdc82212a5d3feeb2e16f16423b1691b0aaa7da1787eb82ea39ae9e \ No newline at end of file +1ffd321a33b778e87614a26a91a8407ec7b9dec4f0f847b16b1dac4f3b910604 \ No newline at end of file diff --git a/src/func.c b/src/func.c index e6f293ef06..6d7a77fdb6 100644 --- a/src/func.c +++ b/src/func.c @@ -1980,7 +1980,7 @@ static void logFunc( case SQLITE_INTEGER: case SQLITE_FLOAT: x = sqlite3_value_double(argv[0]); - if( x<0.0 ) return; + if( x<=0.0 ) return; break; default: return; @@ -1989,14 +1989,15 @@ static void logFunc( switch( sqlite3_value_numeric_type(argv[0]) ){ case SQLITE_INTEGER: case SQLITE_FLOAT: - b = x; + b = log(x); + if( b<=0.0 ) return; x = sqlite3_value_double(argv[1]); - if( x<0.0 ) return; + if( x<=0.0 ) return; break; default: return; } - ans = log(x)/log(b); + ans = log(x)/b; }else{ ans = log(x); switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ diff --git a/test/func7.test b/test/func7.test index 536f7eb414..c8ae2931e1 100644 --- a/test/func7.test +++ b/test/func7.test @@ -202,11 +202,11 @@ do_execsql_test func7-mysql-210 { #} {0.6931472 NULL} # log() means natural logarithm in MySQL do_execsql_test func7-mysql-230 { - SELECT log(2,65536), log(10,100), quote(log(1,100)); -} {16.0 2.0 Inf} + SELECT log(2,65536), log(10,100), quote(log(1,100)), quote(log(0,100)); +} {16.0 2.0 NULL NULL} do_execsql_test func7-mysql-240 { - SELECT log2(65536), quote(log2(-100)); -} {16.0 NULL} + SELECT log2(65536), quote(log2(-100)), quote(log2(0)); +} {16.0 NULL NULL} do_execsql_test func7-mysql-250 { SELECT round(log10(2),7), log10(100), quote(log10(-100)); } {0.30103 2.0 NULL} From 9e673ace5bcbda881f153d2fe987f2414ac85f42 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 1 Feb 2021 12:39:50 +0000 Subject: [PATCH 2/3] Improved corrupt database detection in balance_nonroot(). FossilOrigin-Name: 5d54d9fd406381383afdf10612bfd590afc4142215d9bca09e227e3aa5baa102 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/btree.c | 3 +++ test/fuzzdata8.db | Bin 1619968 -> 1626112 bytes 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 95498bd054..7cded2c472 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\spossible\sdivision-by-zero\sin\sthe\snew\slog()\sSQL\sfunctions.\nProblemm\sdiscovered\sby\sOSSFuzz. -D 2021-01-29T16:20:16.527 +C Improved\scorrupt\sdatabase\sdetection\sin\sbalance_nonroot(). +D 2021-02-01T12:39:50.859 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -482,7 +482,7 @@ F src/auth.c 8d1df0e2ef8bafbedd4f1fe4baff03eb27507da4bf6e449df3613d383c4018b2 F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 -F src/btree.c 47d9fe97d5c0d74506154e3597f8a23b81a00080751dc4d11fec91ee22796f4c +F src/btree.c 4da25694985ac8f5f714bfa58a6cd453f9161d7da9394a95605aaa4db2752757 F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331 F src/build.c d4c06261b0e532523ede58dc511381a7a9c155132e4b65a6bb2ff76fe657793a @@ -1046,7 +1046,7 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4 F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5 F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7 F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2 -F test/fuzzdata8.db 7f6c5443d67ba040f760b4d28da54cc9f68174fa212ae34ccb86c645de761ec4 +F test/fuzzdata8.db 977cb95f4a5d828056dea804a6de416debe3fa0182c77f47fe19a0554aaf4db0 F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536 @@ -1898,7 +1898,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 0defaf730bdc82212a5d3feeb2e16f16423b1691b0aaa7da1787eb82ea39ae9e -R 818a051c7c4bf2ae05824d55152903eb +P 1ffd321a33b778e87614a26a91a8407ec7b9dec4f0f847b16b1dac4f3b910604 +R b8a4f4fec4116f58162d1c62be0cf1fb U drh -Z b6b8cd840ef34d4d25e074519b6e4b42 +Z 4a762bd4c15e78835f4802843c46cdfa diff --git a/manifest.uuid b/manifest.uuid index dc726d75c2..11f8da895e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1ffd321a33b778e87614a26a91a8407ec7b9dec4f0f847b16b1dac4f3b910604 \ No newline at end of file +5d54d9fd406381383afdf10612bfd590afc4142215d9bca09e227e3aa5baa102 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index a3fbfef043..0f71b0479e 100644 --- a/src/btree.c +++ b/src/btree.c @@ -7975,6 +7975,9 @@ static int balance_nonroot( apOld[i] = 0; rc = sqlite3PagerWrite(pNew->pDbPage); nNew++; + if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ){ + rc = SQLITE_CORRUPT_BKPT; + } if( rc ) goto balance_cleanup; }else{ assert( i>0 ); diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db index 47d0d5a165f0d64501d44be2fe3902b232d67776..eb5b897ec024c77226996266cbe6719a1ef173e5 100644 GIT binary patch delta 24576 zcmeFZd3+T`(?5E8mUCv#I%f}C7S;fPAd<5ZEA2crS^WOX3-{<%H+9$q0S1HESno$=reE(%KUBRpl}ztx zFbKcsJ*ceKAEWXM{ZT5<>fNY3tv^EL3B4>RIb+BQkk!}p>m}jN9BupYbu}1)LYSyg?fO>xq1sKXXwqToTxXYa-80T z%AtB=Dxc6BQrTZ8MueVv1eK5K;Z%0j!>H__hf>*AuS;cXy*8Ci^jcJg>wYRjbuX3m zbQ%r8th)^c_N#8EvPw^|(vJ(eh00HLmC7@^LgfkFMCC`iNacsRK;;KIqw;;7#K;b3 zQt{qGDoQhUrCF+p#fZCz?*ORd6EtGLuEEw#!eR-;s<2<&x(;A5}vb|$T5#$_=X zk|77m2Fo2FM>Wad_}qqhbVH9c8Kmectvo&97}!;OjD9UC1f(IUs7VNr9rb zJsPa2W3xG8cI2|7Na<(O=}3e zV2gn1D=cbeYRtGX0p9PpK+JghZG0+S?dWPlS1etv=!yx%j57}l(32wq<747t+QkG~ zkD=;RUO6siL~QEcs^iT;o`^%bKfV>+Z}-Q2UJ*+bZ68*cGwJU(^mm&-9tgG@#~*7) z)p38Tj^)*ejq^3{|)0)%*`mQmeqYAf@rVMP506{0QRb4|x+;<49_;{D*m1 zD=J3>;)u^!F2h!B+frlvY8BTy=J)@BRxzyutpny3Enw$wYDi>ire%->`wvBHW z-m_nLWQ#GgX3Q8jZRV)4sWZn#wTO*s(K;#+6$!h3Q~hoKczX2z&7*Nqt>U71b4{E6 z@f&RaO|9MNfAaFfe%k#{9{msfG=-eI{kemhvHJjlx0HWFJe=syqu^TB*Qk- zUtUcarbOdGrzufjNAf+UJef&`!B-@SPSR^%#z7Y${;vcz&d*hisAzD<9`?4(4Q9xL2rH)>k)5hTO0} ztDz{;Bq^(h>at`gj<2a#9IeNXr#DV6o~%p6^pJ;lVtzI@#X8MQHyAqw({p@YtQBSY zMr5Z^iZQvIvO-dOvZj&WMYA!{lq#^ZD0MSE5ZNav_256xq14BesLHBjIG)U383x3l zf;$G81_HOKZ3*#1oi9uQ?#(Vf-&{B2$&sd3%y^#u{;UnK z`W7!HjaLE;d#9TA3G5QSTWabi!s(Yx`y_hib9ncNNr&T`l@+k-S=Is*CYcTk;^mmp z;K*ZsC;(GR$r;x@RzV-N?c6({_9oL) zyz(kkC%NOHbdxCoD(9J8SbonGBSO(YcOt)i4GIsq+)zHqEkQwOh!N`Ta$U!FFPOSf zJKId*aQjWuYX0DLND!4tP_@mZ!G_08Hu(IaDHKyaH#r$5>@Yn>>R@1BfWF-1h7^}# zh1YhOhGNEI(^3=r3W{F~Nhj7c1v`IfnkL}4D@-AbeT@fin7WFPvXmuq5zOUn+`x>9 zrbQxbm|{{P2TV`1n;6l;U7MkH&2@`G&V4NmD!+1l${+p)^qEp^47a%}1-!rBBs2Cc zvZ2<_4DbEhbe?fB++?@#;62_CG2IQq>$uI3*2QIl^7E!~ypiwr9#rV=13`I{^XF#Y zL-|Eh^FNt8EU?>f>StRmtXE|6sYWXe{@Wce{hH|o1~IRMtl*>nCLjGT>G~m9g;X_- zY{9_(1&L!!4Pg7%rh$;K($o`Ht~8}XX- z(QtpGsV;bNQy!D6iH3WS>M;2^b2mix8x)WECqHo_yB{1-qygnRo^P7XiGKj8&$AI2 zKGn2=OGPkX!dX}8?;8Bs9aAJ@poiKUW7>zV9HC^RsWz)2*TG*9;LvQ+I3j$42sxWg zf&8teNBNC^jw{l*l7|1Gy-$`%iYPG|a@V;ULg{`Jbx~g-mz4HO$S4}VEJ22}7fmvr zH7n7K8KG=T$Q!uBsyrbw6GZrxD|w|QGTzXXIwDgby_a!fel52JXM2c=Ek_-hAcajI3&pl2z&?A)gvMsN>u3>3}QqELMQ9GUFP&td=A49sv2UoB{;S#MG zq>VH+gvzxcp_tZC$+IvAc|GNUi8)bf$A7x9#WY$S!O3CfXpBo#P6^C|Wv#>x49n*@ zmM}<(wE6RU33>2fo+AY2IbG3Ewa?L;cj1NH`HnDb&_k&dnGaGk?9KCa=_IBkxf(F6 z%9b<+Rfh!)3bO1?p{R@01B&`9kCWS)?hWab-`nBAK}pI^k%d6T6nh;E8>oCCvRY8_ zt<4KlS31=Ee#Rb<6lpYK+F&J5WVKNmu8<$DLtevHAId`=*CFa>+cTJ%sw@^*UC8Yx zQ!`@~vP2KJ#I{_rgMm*0pp{WOG|R;jvk6K(L{BH-xk}QME{u6GAzis*B+A0)C^zJY zqv&( z!#HPCpy-IQj4LATxrBHKOj2h+`4plx=eY6)_P(IdOxGSGzE?&wSYE1hhLxukBfj~$ zGRG)|N`}J_k#46sDOQ~d`@U3K^OicU8>81l+n0Pu_*SM9qz^DRh0aHnt`PI1x*f{h zsslT`uaq&++N)2)$ZwS`tTUYYNNEKf4=4lkiY6E_=a%xc$hts97jrbM+^nvI<)5lg z!1OZ32bHzeI@q&XdD}=+j*en8*p=wku=6iUh9ZYahV8SUFjoBzcQAE;AoDF(H%t#z z$du?huiuw#5#L`l>32ONM=IkzTSh99d6wGtLO#z4d^y^R$w>lB2d| zIN7USHOdi^VdtPfpDh$*zVYh~>29?iMue-^MAiq28>o#@^Qq4<%;}`oCx3ohsSnyS z>RnKpt9EXaeZgsTH-X#NtVxjbmM@tKu`{H+?Hi8co2u&s)(_Zk{#r1qwK^DQMX5~` z(&u%tYAI8awi)&MlHpv?^&mtAZ*^9a zWj2uPmo&0;pz0%<=Bj0Ad{q67u|be~NC{wE4>d)R8%l-)@tni`?2p!>^DLKNhnO%m zn@i?#x(37Lua!_35%Igm@HQV287<}V)M$u4q*T=CVo!{IS?xy(^!E{LJxpyW%Z(+& z50CNj+RyE7*t(v2BgycN|L@%&G}e=(K{d(+fLn2@7}i?H%ZwN-v2bxFwrm8PLFs2x%73n(m78DnE1eTur0Xj0!0 zu;a7pGLzgyGVB?~IcOamm(fJ{1T0*y@~LW(S`OtVb5ktxsuD4gr$)+hQ^|0pIXB2L zw6_%|y}Uq=qy;@V$7uU4q2uN!}SEY$f}y}{<>6;AL%Qb%<;R(_#s0+yUpSITk= z$xxEY2cU#;t(y1hL_JC}V9;!XO5K(#)H8I=M>bBK{yT$63{D72ZUJ1mrF4SwW9kOp z@G6TOpn3giuBWqki%ZuG(_cFJC>Kc{}R~wn1%uD*h1dH#fwQ<2c^;r{J zoL6vm4X&5W<3zRu3a6-}fJQYAPTZ00(DFMa8Sf}&tA#BC7N)+68$9N>6seVDc(Z0+ zYk}`KHMbG+vMx)U`Df{R0m{mWL3((ESklluNS0gE8;M*Ir}C%c1^Ib;Yz3s2h!K$R zp^}PAqs*C#97hvKGM}*r!pJ!DIyonArXE|;#{7WE%jtz&&4BGK5I{ioNhgh-P6r|O>#TQ z@LL!sC;>m6V~!N%SBRjEP{D*Kj9FklrLy>~50=p4ypZa*KyCyru|k%a536!}$?$p{ z6kf1&#riLqKWFl0dg65qd~W%b;r*A*vcTSeT~}#4T5GkLcE@kRt}iV~IB>0bm&mr^ zuA3H_p;~DERbbnpXoMvN$8I)n5ZMm6nrcbJ?ys9~o8%6X;r=k*NisfPWS%9;Td9*G z&_@g5(07=;3zEhNW+*UOhC}9iesT>r%*%rwq!tk~U+&*k}Vpf%TX7GjG;2bE2LHRv%9tqOy!xN?Ewj$eu#mRDi0Z#v5{xnz* z2_Kjbv%OgLommrjYxN*!pd44z+H*m>ECKew{Gg_@p*ZoR`J9P$!Y-efTMFiVrIYoJ zw3^eO4DtrYJ~g*xyGqV;)p{Ef!!3${*30H$Cb=^ydOiNoV95MJ@xoK%Wd)kOWNwV6 zugt$Nc|XbH0I&>N^4xmXtU+mp5Ct@FU*|U`=5TV$U$$MT4e^2m14yKb99iiFxtPHcLrZ7gD!W?R6L-9OOW509+caAf3NIs zVhI>7S#B};$UFV@E(8f#zGw2gMCMV9*lWH*E%%X}xMHE%8f^J}h}dtg1EqdTW9)9V z95k_G5ct{F_P=9dzDZ6b%^OrBlX|d{5n|Fe)bbXSKcId-0>?7*WjK*wv*P#RmR$<# zitC$NQU&=q-8}_?`i@%vUF$O#ZjQESM)?uR@P=5^M^kLy#&VP?C#ivo*qF8uanRNP zo3*oC5ZM_hZ?DwF@Q#-2BKsKbZnxEgg6@{4*ek)}HnVOxvX`Zh$>sFwIjDGGX%1EM zLrfU9!g7_BT(%5mFulKJ75fyv%e7?V(j-fU$j(EbHRe#*P>=QzEx)i-2@v{<)=8cSOSE8Z7-LBF+@;kbLKC5f?%7;($;ID>v0EN{Vx&9u7EMp|f%@EJ7! zNbHV1Q!HxKdKJPmESK@x97}}A zuH{AC48cs@qSMGfW0?u`$}q^S#DyFLd7!6CiTmEkLlgXeE$7#Pg{Ax(imobsyOl0BC{g!S>qGBzQVVQ z%$WkN>0`+V()D$XxO0(&?GxAySaDE{h2^Wwy)b)~MHkskSn-}X0w+IV$!9g9ZqEq( z;lf4|_Fj=-A9bYV4#T^JWT@;GMEzh{jLSz`o~EiTR3(2G6KEB{n4c}>EPtoj3O6TM z{9MqAk-#0cfh~u$q1I5`Rb&|`vMT6uS!{$&_gJ%1C~vK{5^3( zsLqqtcYt7)_W18oi&z*@EKfq(+oFbb7Ptt|wX>nf9`YiN+&QhNb zyeFs!|EB1d2+m){ZF_fFN!AjUy=So!yeC_!a~aCsx7>lMNUIf1s`Z{gE4s4dmgwLF z+ZIQ)u;z1diY6#KWyyvvlg+PSnA1u^RG{n(4Qai8R-GWp*R7kW^jn7;S#Mld&l<}V z^V?>BW-XVq18%KiuqgU^1fv*{+4Jp5=aBiW2_x^q-BOkZpV3o zMKX-)Y5i0nB<4`L-zGM0?A_GN^{ zp3kB!%*6*OhG;=%@ z#adV3zNf6c1s0YUm_id>n)PcFi-6p_;uOf)>l%fp=2!_itY2e2D{xSTb*I1@{An;q z2D66&t+pbuB%4#!5IRlZ*7W;A>jlWH7JtA?ORY^9YxJkp09j2eUR`FLsYv}K!#9xH z%Q6IlVUEzb<|i4n%d>tUQ+!j5M7GzJMxdC$StE7GfmpDfW=%2T2&p`N@`|;kKoCvD zG0Pb|x7ON%Lo^X5EM@_z>skyFCYzVRu1(f~ED9saE$LMA#{faXRBJJ${VdLg?_;ge zcxB*8|!zc)BCq%GvyS1^}=Fo6GNisa>#9hxA+Bt|V_e|FN zONN_69u6fI%r(#Adj+uxUC}{+6N#fZ0kz|v>va=ri&D9jc8l>?o)prawwh(Og96_B z%xaQY2MoPsrJ4VktJc{HdmO*NX>|%Rg}MpnNc}*m&2PV{$b%%q7mq;lM%!RMp0$F@ z4tVq@>jAZE-c)^{WY{|5;oSvul;6R@x!+h57<_!k8s+FDQ-pak`;vLcLPHf2S~53(s8 zLcWt7P36t(mQ-HNj-s+Mo01~rE7{GcypT<)5r6zMy0YW{7u(1*$mQ8&rSeDFgapg) zXE&tsP&VaP$R*j7Um+J~69g@9&8Ex>c~drJR>&K&>rlBqoAN8wT8Iv7iFi4lP zeNdz6w1#7F z(o5MUDxc3bQn@Ibj6j;1O;#&S&8Epv8kdDN~1o5%<{gTPUZJ{8kJY{X;glwPo?rbeG-+Kd-aL*W4%tHTXC5_hRVfy z3YA&*(~UGzazcGL+v{98WF|BG~*%>NVlH2=@( zG=}xRr_*H8c>izDr=ijPpQh7vq(}ctJ`L$|>+DCVjLIgw+8CLgKxMt`E>zaZrZ zw))shvh5eyW0)|}mO-1N5?dthh@)SzvBmbe#CoEn*-WbRq-0nege)OvqpenOZU(4j z+ao_A8D1MyON70>|p^ zw!;iRXkqIpu>Mfp)0O}UlFf|$Vr;ZwNP_CVw!>RLh_#tzngPG)RMQIu90u8T@p-j3 zU5`V~v*zKLl3?2;5(e(LZ=HdsI$LOZy*OF0>zIgeu!#kabb(W zwsRsI3T3|vwK2M*#VKI)NZVP)hQa>sHpF*QY&}Fa9QOCLoyGHGZOui(v^ySE3VK{1)ZL?^ZG@2(qjUR0u77JaTRARB~YTI;G9w`~F&IetwujHeXUQ|aP0ev^x zCaY84iqwbGAO=-s3|u*B4~2x<_7E7d$@ZE&mL8r8xoEXWx+=4j3V2YB1^-Sx-DJ6StYg=GMj=#=xDZ5#f4-ewcoJh-}?QUV%WvQdO~K14Kd#KEXBN+F~mp*R#MT>|~SvAqe_ zD>f%8H*FvBowv9E3c*HO+%IewMV0{-)wU}9@rKPIvV|D<(so*af+x*>*lDuYh03NH zB@g^y+YD8UOb(duQtI0Vgm)bhK51+Rnq9<9xc`HVc79bhP>rK#=LeIJM#;tZd*W=m zvLP|de(2=_!Gj6sY+VUo@!A8}>%MJ2V~Zg9Yg@CuE$#gjmVz0IJzJD1^-o+1xsB}P zIb?fpicq{@D~F^rw#RF@PlE@Stes%K!@i6?3xQbs417kjH&>)F6cyqiL=On9=kNfl zWq(x7Jv2ifO>5~%TzP_P>F3tQ>8TXDo6ZUC6eM&Bb^1K+ObQuJv6Xsu3J|@3)yHgZ z2FD}qo#DX|+qRPWc2Qt2Vsc&kE|IeX#7;aH=Wxacsnjaa_t^ghNrwa{?4UJ2q%9F{L&04~2dI1|gi^pR z*q?&%XZ)QY%_8*B0`akJ;$q`kw~mdY(5m<{==&`SwgGU{WZazUT-r zC|E5?kaX7(208imzSwoNouX)KAwrh6P$rwCLgi%#;-qo*H3D0Q(nNcLB9AAP$$prr zSq4=U>8qQc?&lTAwEWYfZO>N9>e%XkG|k;%I_Pb$f=u zHbe4oEe@woau^wQSzu0ZG8G(nfXVm}x3 z3xvrJ5@tfdQbz&qTx-7(Y?YAsAwmeoT;u9oE)i&c#U}M-`E4C%INf`IwGe5JJYx5& z@wW8chUY8V{e0VoiiFL zdphRg{hoF~;MzPO$jm#KJ6ua<;Arirh8U~VkxRV|!+qE7y(M-4^&Wyt;9^uU>>K+R zj2(oERze1Z*A?S2=M#IH06C+aPO>0J4pc3$Q}D_l9)gNC!YV9%lnexN`tq!TeE(b| zS(4nVvK~HE3S~yAQBc#6G)))q(9ia6jFmyeI{yGj6Ez#`T5r#V$sah(c>fo>Ns%W> zhHn#*gcCur(95=Ph$*)hb2+_B*AXn9up842zdJ8nGIxP6{O< zj*$|Z3Zv>erYjZu9@VEvhUyys8m1iITVE%Q|0WnL#PTA?H3B_;Cw>+u{xDr1ad09O zi!`m^;aw!wLW8Px0wap-S45~hAk^a&oWzJd_Q%<41Y&cETY>CqASPwldj|PDg%SJg zMiEBtFqz<>=D5w@ISpy^{5xvQt0B)Y6U%D`{IH240ugO2wIO=Dy(zA!c0BVZ6%B$^ zd<=|K`)>tIWtW3`jtFIr}gge-{=p2~yvb?y>J!u6k5Wjz2~ojoGZ3E`8UmB zxLyz_ATem3V<)4mQ*zTMuzZ1|waBhR@=pJLJdovhRHO{ji6rU?{;H;CT=yCAfJ^1F^0 zDF4h^2MWT)I4J*)P_C4P_OTdU>bT9owZTzMVTZz3{Dp)Sa?f5~=xEJj4~6Uel%TfR zL3wW9VfjXT3<1F>$f04!Hyu}H%8m#va!i(QAADS&j?0T2Ger4o>a_|ImV|h5Shb^2 zz^4S^(cl18bMvUrg2N1zy@VLd8sWG>>bpoV=U;FTo^2q^lT60NaQH>;ApYQ|=s(aN zT&nyFlAjftb1rDp^dmQ&b@267$4hXOY4*!1uHR1;<8`)4|=eh33(O2 zhGPADj;kUQA#teI2@=-$e+UW*Nl2XqlN5+)6%!NSv~(4@3hv=;%8)pks0?o*cH(n- zEI4XAG*ncrzDb@zVe{#H=1YUL?N;UCI0KFI9_}O`(Ax?WO7hzf_N6ck(jHU1ShQLT zV^|p~htpo8!kNdrP$9QUDuuM&&Kw92l~zMqFV_N`SX+CQ(OHt*AEnNa@Cmub7{RUL z{Sr!ZfTWMLBG9J0G9k2sb`8QuIRj9+QRB-L$%4hRLTDM2m0`E?v`)~H1rttlg}lVb zBWsKKGge6LB0Y)28fvFa^3#&x``Mh0!DxvHsTLsYthAUokT|5|z|;jHA5v?v&KtPP zDEAIZS_6F(PhE)BJ{Fl1!iPDRz?HhrSy1#+h!fM!XcVK%Zm;Q#xgfE-qYdVSJGU_k zo7*Y%{WE7WWbF{zahm1_n{z|qTIUa(9J5%`O*<;dGby~0SVPlA?vf-9gZV&jr_|3s zZC{?JRa>|}`V3#`t4$Ut1+?%d2MuwOMnfC|fpFSihYr+er&kNA*GLqh7_3n!zBY8~ zNER16T$`-W?rXzXZL-{Cq!|mxDz<&f*jMo~d zb>C{K&!SCfDkrT62otp5WRBWVoP;JrD2md$M!~IS6h3p(7I3Xb zTfpY9@{C5otOAXKSy52Dny;KTYc!Fz1ZSSL$;+iy2UON~J)SSqCZZtVBtM$YRRK-aezJEjOBvN9lb0F=j>fhp8+6Ahg33(P@sM0KoOefpE z?j7V4iO*H!=RQvRQm_*O5S4!3>zFveLrPE)ubK7^fjwPYSXk-ENIVV%jvkR;}R zB~Yr7L3#vJjUgLo;y}U6t@H%h-WGvcF-YAgR_3AvA<0PzLftXdPS53){z7}ZaOZOD zZ+7ls>`@HA2)!sk#m}faE;SZ&KQyB+)Dj1J;}LJ3=UXP zYv!S zWt=2}|8D;*EyBvq&IXJ<4#}OgRW)=iB^~K3<=}T+oj%3}V)|I#wcqZ8q^8a%OL{m7 zq|2rmtLkCy#2=$pm&5l@>~E*j;jSNM)oAr|+`aXq>&Y5FVub14NM8#wT>EHy7U8y{ zV}SEV#s))TMo1mdb~&pc=DzS4#2DrA@cDfqhHzHl44keb1)zMBPzM}eX+QG;A3~`_ z#v;hMApKCIj$65X_9YZV5(k?Im!rpq!pT0axtKBBxm0AsP=CcqNgbtYqy$oOPe>=; z(s0z*@!F^|ErXRzNb2@(&=4jk6gLN(KQVdTRQ2`Rp<`qmjZ#ISHF`2NYh0n?H!+VR@SHqfy)Ld4X##8P@^zC z@cTVZugKCtrz0Dnf%7S-Y_BEbqx+o)1U3WM?2!GKbjf*!cB#GWMtI5_Lc2r|5bH3SHtR+I%MZvxcjqJB&JnOs;eg$%rNz;%w(G!N6? zb%u(t<1V?OF11}R@XGm6)<~mNhf3!y6U)HrBxg&;dpgBeveH7x{Zfd<1C3md2v}7? z&XLz9^ucW575#l5^hJi*6`a|P!X?DuE&o~%JtA8w=H3%bQfG8YEU2xsD~vG@v12t zvOW^({e8Mh#!{6?WlJHet(?UjLhV22fzP0{m+Ku);j>WPMrr|9hC1K>lL@|l%b{Er zzP#)_(%Bfw6(=QG7^G#GxKSfBD!u5O%nk4X-#gh#{4SBP=kRK_6h@Qj@8^d6*sGRn ztw}Z#0FqfV#x$*wMm`0t8@Nu$i>b*MvFfCgGCWhBk%r(?ja~VSE$8xmiD7UbBXMD1(7`sh^9o zPXJTP1j=h)lwgh{P@`Uf)IL&ULKs}D8SkwwULN9_r0|2Q zKgZSh{vD7o#&t-}Bl6Zl^)ZcL*s(5xVb_6fk~1+S#F@dd4{1FlZWY=<&J4|tJ*K+e z;km|TgQQpp2Wd5yr@3g~T>udSgccZ>={lzH!>->Bsp&?=ei^P}L4K8*+64VqYQ;6@ zD|9oVNXlI1E_o+4W%!+X)e6UKSblN2ab+(Y@?_vYGK+4S(fwl%kq zZtWp$JM3!A&HOdGwqeFqS0>?fHOC@>jzxCneQZbl6Ml=Il0|+>HfV=Cu)43CAdlbL zxnuB=H(jMN+l3WtOq604bITPxOu9R0#zk1Mp4a|yo-#F9TMQK&>Cht`rEH4N>~pPA zm<^p}uI~kTCv~|80w0-9sqt}el|p2Y&t2~QH1LE4XH-%u@0_LQeXM_rpUX!yNrUCAZa zL`l{pLuvOKnL(w+9j3msJ|x>giHvbI^?&wt3sNlZ7EpEF<&cpYf0r*TI{!niw5&M* zfiU-J47IzjQ9Ah_XH9!@dJaOMf%`NRb+k5qNDdM?S7cUN(^M$IvOl{vaRiiFp`FVw zE}G5W;YX63*Jpw2gN;q4Hl?nO$liyAe9H@N(HON86Cy& zxkm1}CfP$f4?4(2UzGSmg(>;1rF*G*YGdPUI{$g(;hFtEZhp&$_d~i)6M#e~b!&8G zQBYUPv2Y!){X_Kv!P*KeEHup}ujp|vhwyE#N?6#$oed73dlhH#3>I!7f*_{KwGgIn zWh#Et$z6+%tV8j;Y5+!k6qdrl*aO(pHs}o{kbanB*U@bE(IISa9?D~MS8Y!Tx=X>W{chU4&T-S`^%^WV;BE(LKhojb#}>HXCFBsLENW8tA^_D3+;s>wbO%vG z{fL|FeVLn%D1QYDPVyFB)jF9XtVn=L3~iQ&K(CaTis@SWJ03f|>waGp z{}mGhqbl5WV9P>xdmL8kew|}<%d*|g$bY&&5#^s+(?>Jl#ib;qKF8cB5-^ve6Z2sw z+=MsYfn_V`xPMZH+lY^pb7Fo$=`8Q%-zc5u!uqxC@0?uXoZLmJl4{j^?P5}6#E-Y}@g>M|5TFeB+TiqS7i{?2Zib|_^*!c$a zn6S(3fEb^rG0rV=Zxf(FOOFpi{GM+KzKsv@L}FBk=Ti|Px_TO7le(TuqG%z3rVsE$ zQqltrabmcK$P*(yPAHA^IN;SDo~GERp@&3m1J>9R4W;!wc9_@2W5a%t9!e##qZH*K z`8YstN!>P!@mvs1T08oZOk509#CT${bFAkm$G!@idVE;Bo#&=VSXasr55?a*dMN(x zhJN)tZLocU=R=XQese=TEnz`~r#=qs=AoUpmp)CwJMh8PM?GP@YdZ$|ct}b8kl4i& z4+#xD67*h18rVC@LxvNA(m?(~El7Nf*o++Pp(rmU_rB<9g3X6}&eQ2?GE4qMT{27l zvmQi@@YIHc=|o`cM9*msN)=D{kp55gTo=`_w(Z)sBeWs_Y2!$%yQg^$a&)SAw#N@S zQ|O(*OioS&TpjBn^lXlY(6jm|E#OTzfK=Vn7&^b^F=9*>AKZqp>p9}ET=%%J+hWfl zkEw8>h%6GNRlLcjQ1*rjG!4NE3@Z7KPx!j{7{NmcWu1+GP7>!}IymgKi$m$C*^@zu!|N z$X7^vTcPwWf44PCrTk|s9>46NB$3iHo)u8=xu+HWdeox|Af6y@5;l5E3B#Z7T>v?U zNystBJxiEKtzhnX?_L4-o$?SCA)X~2OgiG}gDK^nO&pd=K0;!nnoc-mw4wLp!j{y1gN^~8N$0Y(%SR5CyaOFXZbhC#l>WM%jtaaJ&&I+ zYUECIy3o^_-|tPq(Nmrtv`~JJzG1{W2kQJ{ewD`zTR-wNg-e&YYvc9AR0!U#@K9FL z@$WtHMDLHJ(A36@w4?sQBjNYwJQS56+S>5xOHRN*j<-U z!%MK)aZh{xo0GTDE%-v$hg5}od>@MWlGluF?~#3Qi5dChP4X3QB>$xldUfDBoVQn>cmKASALFgXf2#~KY2xko ze<%ZD>t?Y3>x`hPz&+0xO7af%z7mvEG9*gAE)XvJ8bkOAZ`?n6y}{cV0DVSy2jlRL z-mM~g9IryRjQ>6L0XKsEEuOI1sGbydrmy9#Idvi%&`*=P4b|sR)=m;~l z^#rGh&;XC`BQbxVSJ^wrn<5gH>|LEd*xQ&7>T`tIgnEMl8UpNb??ec1>#c;M2ya77 z8{*9qD5)(m-a8KJb?{zCq6`Isks^+J=%_rY&QT$MqPGdBTX!87n7Jv+yH>C}Wc zmD->#tO6(KWKaMjG0@a|I;iA?V3P1z{!B0B()=Mw+C&*-I<0-&(~7e)<1YirAD#8JQkCG^uA299SIb^dDdHtGUE~&dP_y<)6?5AKj%Lv^(t>9r~G_S)DyWD zSN>D;+HhJZm+7#FJ}h+J;|l7+CqXVIV?w)N^CP?+`BPIMq0CFC^9#JEL^c%@fAk&- zDwm53qIv#yZ*8tJhaO6}*&E&qG~z|12MGaRseoC(c^U_sosQ0_-rK=9ZTb7W798yH zdTGqjdmY4gy+I{5a#}hENB$Mw^$bp67=*R;UVS+7T7>U>&^Dh2=K?Z$KKKs>m|o_6 zi8?;!b#t;l=kl|O`6s;19_qkDV*` zTT?!!3xj0OMrW+|2>}bvdtPJt=e@K>{6i;rTwk8cNu0q|*Nz#nz6C*<&x68GJo|wh zM+i5LpdKvaY}oPB``)Gm<~HC+<%Ao&DMn?$S{RF1cU)Ma3LtZ!^kIf9HmE-j1?w8G@g-u~i0SmMkz%m2~a zh%@k6kjyMh{?fCHnzMN+6X%}S$JwUM@k3_LdLo0OCdBAC&2QB6=adHXpIM6HNqE(t zc!Er66fg|)YC!5u+noQxS8K)=Kjf^g~4r%0jQ=l_^`UTH9^p_nxEh!o1aT|*}J2UR#$nzKJzU9kDda_ zQv^d8@+v5*^d9C)k@Zl~Z#DEy!_|-arZDz0re=9J&{FoKX9Q<6pQ~YWE(%*v8@gdu zf8QdBt$sP+pM_DM_{b#(PwO9K{ty^`&bJ7op74Fj*ee*2{2cxo;v@KL4O~q!df=l= zzB<@?xbJ8BJdI>Ht@7ncXDBKs#hf+P_q@mtxXV2C&gW6(+VruA4mD3Waca7cyz|U#>)7x<>Dw2-{I#1|^a z1=RQ!O#ZjeN=N#h^ZiR;ufdhqd{5)-7kzJ-SSUuU@*QRB>ucp~+L-Ti)b#xbrsexq z2G6X^Z$M(1&kbpP{Rwo2+NWXv@xDe3sqfhWdy_K#Xu6}m(_l-2ePG_m%k_2j63`|bnDrV*q} z@R?!Z1m8la?Bvhos;~zt-VFH=Pag5L5!qfSmi!H{$uZxf3X8yL<-Yo&f*YR5rjO1X z;<$GgKCF%C;D`JcUHcw>V5n69k|+4K@g@uL_GaI7!km*StuRIO*QUcB^8PkZ_@d7T z_Y){viR4lO<+c2SaPtMAn&9&-!+u_HW<6jD!tU_wpw(I;J**KDAle*nbRG zobf$?l^%aQbUI6Dq})zR7=QoQFdU1}OjB_tL{pZSo-rK*rR`ub$BqLyPJef6_xRD8?$Hu0^-HIIeFnPxAvNFkMNknYG+(q>GWHgkN`97uRG zBm}k%b`OB6;VMBF*L-u#lxS&SqRk)@DA~i(Q^*Ui`?gXzEStW|bNEj)e}l3ktCoODR?U2B59+4W%!uksgOX?{gt~{gfmVbIun9AuasRz=or~7(Cd*zc?t|4DpdD zosvIAv3P$t_58YTFfOR&e^y|p2_mBpBBj*zuU6zH8wF04R6)*Urq0gtj8Av|vu0mVh!k+%g z^2JwEvLjLJ<-g73b41-|oI3jcM<4&UqI!wGH5<%Jq5zaH0sAER(L^5*NbY6yLd;m> zTzqM;->*u|=tBw|dh3kerusbs`#gWFznk1#GOVDu)=;?}>SO)iOLT-aZIYiRgX?*$ znyh@PpQigSaOj(UN^C!o>yH)Sv#0%$l%<#6k&dAMks=#R*p$G&ixQ^N!o@cr>7w0? zBcAcU#t4l|Pte>v;kYR#%<$Cmu z%5qBzf^_1f#o?-D{%odx^G^3{+VI|SJggk}ynh>$uaP*ug#Z&0Ik6WReb2#9DZRI_ zu(mjgO5PFWWEtgH9kiuRtpJHxh_fSYLF*_p|*jDBo|V{FgcY zbjsImERy&PG9h|b?9Cyiggocswf;;F>oKE7skPbw`!Y<^{pk#ioBY2??1$G@((WK_ zn0+wM;rG%gWK+Ni?B@b)R8>d75mso|3|alDTzf_57X^g7#TL(9K1bfkiwiI==D6KF8D_!cb+M(T(Pj((`LttQ|f+M z;ZzJR_B_R!JEX}vP)ImOZ8p$mEKl@#q0;W8k1O}&=Mhx7pdh^Z5jk;EVB8?@QdT<9 z#Dd;rs{jWxV6Qf#W094Q6`2rCpj`)_=_jgnU{|=Ka0b3B(tTo87CedjD|3My9ErrA zlt)vj%faoEe4>`EvL{7Z8H{p`{!q_U(>WnID?w2tTId21Z}lZ2Qbll1Hm83BcrJ%j**e>T1PZ8a%@RgK7 zG1ZM@9VD5YbqGEaVL)_uKn)YM4bY{qR58>zJ&9)jZRL>|Ito68JeR@e#L0>n)AR9o zNFDz3(vg&%D&A~?DnSoy;l^qTzpe;eSFosITD25bk{_h=6zpW0k1R;TV1{Rzh;}MO z2B{3m$oYgwbfT(_Sr6J|jm|UxpiY zWZx2h$R*n^GP~iGLR+?&*O-s3i*>+w8vdY5@5FN-bYf8VfIC53r`oJid>n;VBoKhp h*r}dimdjcz2BP3*Y>jw%3tBaaVX-Ryx@?4+@Do^p8O8tr delta 23567 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 From e0d2096afa8af30299d9638e6760bbc58b6edca6 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Feb 2021 20:41:13 +0000 Subject: [PATCH 3/3] Modify the sessions extension to use more efficient SQL when applying a changeset or patchset. FossilOrigin-Name: e4ccfac09b6fe8cc3aec29d10f4e4c83097964f29882343db52ed91f6f0dde1c --- ext/session/session2.test | 58 ++++++- ext/session/sqlite3session.c | 315 +++++++++++++++++++++-------------- manifest | 16 +- manifest.uuid | 2 +- 4 files changed, 249 insertions(+), 142 deletions(-) diff --git a/ext/session/session2.test b/ext/session/session2.test index cd8c2869e7..806687745e 100644 --- a/ext/session/session2.test +++ b/ext/session/session2.test @@ -35,7 +35,7 @@ proc test_reset {} { test_reset do_execsql_test 1.0 { - CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t1(a INT PRIMARY KEY, b); INSERT INTO t1 VALUES('i', 'one'); } do_iterator_test 1.1 t1 { @@ -184,7 +184,7 @@ set set_of_tests { test_reset do_common_sql { - CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t1(a int PRIMARY KEY, b); CREATE TABLE t2(a, b INTEGER PRIMARY KEY); CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b)); CREATE TABLE t4(a, b, PRIMARY KEY(b, a)); @@ -206,17 +206,17 @@ sqlite3 db3 test.db3 do_test 3.0 { execsql { ATTACH 'test.db3' AS 'aux'; - CREATE TABLE t1(a, b PRIMARY KEY); + CREATE TABLE t1(a int, b PRIMARY KEY); CREATE TABLE t2(x, y, z); CREATE TABLE t3(a); - CREATE TABLE aux.t1(a PRIMARY KEY, b); + CREATE TABLE aux.t1(a int PRIMARY KEY, b); CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY); CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b)); CREATE TABLE aux.t4(a, b, PRIMARY KEY(b, a)); } execsql { - CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t1(a int PRIMARY KEY, b); CREATE TABLE t2(a, b INTEGER PRIMARY KEY); CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b)); CREATE TABLE t4(a, b, PRIMARY KEY(b, a)); @@ -588,4 +588,52 @@ do_execsql_test 10.2 { } {0 0 1 1} S delete +#------------------------------------------------------------------------- +test_reset +do_common_sql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e, f); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<32 + ) + INSERT INTO t1 SELECT NULL, 0, 0, 0, 0, 0 FROM s +} + +do_then_apply_sql { + UPDATE t1 SET f=f+1 WHERE a=1; + UPDATE t1 SET e=e+1 WHERE a=2; + UPDATE t1 SET e=e+1, f=f+1 WHERE a=3; + UPDATE t1 SET d=d+1 WHERE a=4; + UPDATE t1 SET d=d+1, f=f+1 WHERE a=5; + UPDATE t1 SET d=d+1, e=e+1 WHERE a=6; + UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=7; + UPDATE t1 SET c=c+1 WHERE a=8; + UPDATE t1 SET c=c+1, f=f+1 WHERE a=9; + UPDATE t1 SET c=c+1, e=e+1 WHERE a=10; + UPDATE t1 SET c=c+1, e=e+1, f=f+1 WHERE a=11; + UPDATE t1 SET c=c+1, d=d+1 WHERE a=12; + UPDATE t1 SET c=c+1, d=d+1, f=f+1 WHERE a=13; + UPDATE t1 SET c=c+1, d=d+1, e=e+1 WHERE a=14; + UPDATE t1 SET c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=15; + UPDATE t1 SET d=d+1 WHERE a=16; + UPDATE t1 SET d=d+1, f=f+1 WHERE a=17; + UPDATE t1 SET d=d+1, e=e+1 WHERE a=18; + UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=19; + UPDATE t1 SET d=d+1, d=d+1 WHERE a=20; + UPDATE t1 SET d=d+1, d=d+1, f=f+1 WHERE a=21; + UPDATE t1 SET d=d+1, d=d+1, e=e+1 WHERE a=22; + UPDATE t1 SET d=d+1, d=d+1, e=e+1, f=f+1 WHERE a=23; + UPDATE t1 SET d=d+1, c=c+1 WHERE a=24; + UPDATE t1 SET d=d+1, c=c+1, f=f+1 WHERE a=25; + UPDATE t1 SET d=d+1, c=c+1, e=e+1 WHERE a=26; + UPDATE t1 SET d=d+1, c=c+1, e=e+1, f=f+1 WHERE a=27; + UPDATE t1 SET d=d+1, c=c+1, d=d+1 WHERE a=28; + UPDATE t1 SET d=d+1, c=c+1, d=d+1, f=f+1 WHERE a=29; + UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1 WHERE a=30; + UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=31; +} + +do_test 11.0 { + compare_db db db2 +} {} + finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 2ee6b427e2..cafa5b29b7 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -3510,16 +3510,25 @@ int sqlite3changeset_invert_strm( return rc; } + +typedef struct SessionUpdate SessionUpdate; +struct SessionUpdate { + sqlite3_stmt *pStmt; + u32 *aMask; + SessionUpdate *pNext; +}; + typedef struct SessionApplyCtx SessionApplyCtx; struct SessionApplyCtx { sqlite3 *db; sqlite3_stmt *pDelete; /* DELETE statement */ - sqlite3_stmt *pUpdate; /* UPDATE statement */ sqlite3_stmt *pInsert; /* INSERT statement */ sqlite3_stmt *pSelect; /* SELECT statement */ int nCol; /* Size of azCol[] and abPK[] arrays */ const char **azCol; /* Array of column names */ u8 *abPK; /* Boolean array - true if column is in PK */ + u32 *aUpdateMask; /* Used by sessionUpdateFind */ + SessionUpdate *pUp; int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ int bInvertConstraints; /* Invert when iterating constraints buffer */ @@ -3529,6 +3538,167 @@ struct SessionApplyCtx { u8 bRebase; /* True to collect rebase information */ }; +/* Number of prepared UPDATE statements to cache. */ +#define SESSION_UPDATE_CACHE_SZ 12 + +/* +** Find a prepared UPDATE statement suitable for the UPDATE step currently +** being visited by the iterator. The UPDATE is of the form: +** +** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ? +*/ +static int sessionUpdateFind( + sqlite3_changeset_iter *pIter, + SessionApplyCtx *p, + int bPatchset, + sqlite3_stmt **ppStmt +){ + int rc = SQLITE_OK; + SessionUpdate *pUp = 0; + int nCol = pIter->nCol; + int nU32 = (pIter->nCol+33)/32; + int ii; + + if( p->aUpdateMask==0 ){ + p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32)); + if( p->aUpdateMask==0 ){ + rc = SQLITE_NOMEM; + } + } + + if( rc==SQLITE_OK ){ + memset(p->aUpdateMask, 0, nU32*sizeof(u32)); + rc = SQLITE_CORRUPT; + for(ii=0; iinCol; ii++){ + if( sessionChangesetNew(pIter, ii) ){ + p->aUpdateMask[ii/32] |= (1<<(ii%32)); + rc = SQLITE_OK; + } + } + } + + if( rc==SQLITE_OK ){ + if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32)); + + if( p->pUp ){ + int nUp = 0; + SessionUpdate **pp = &p->pUp; + while( 1 ){ + nUp++; + if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){ + pUp = *pp; + *pp = pUp->pNext; + pUp->pNext = p->pUp; + p->pUp = pUp; + break; + } + + if( (*pp)->pNext ){ + pp = &(*pp)->pNext; + }else{ + if( nUp>=SESSION_UPDATE_CACHE_SZ ){ + sqlite3_finalize((*pp)->pStmt); + sqlite3_free(*pp); + *pp = 0; + } + break; + } + } + } + + if( pUp==0 ){ + int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32); + int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0); + pUp = (SessionUpdate*)sqlite3_malloc(nByte); + if( pUp==0 ){ + rc = SQLITE_NOMEM; + }else{ + const char *zSep = ""; + SessionBuffer buf; + + memset(&buf, 0, sizeof(buf)); + pUp->aMask = (u32*)&pUp[1]; + memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32)); + + sessionAppendStr(&buf, "UPDATE main.", &rc); + sessionAppendIdent(&buf, pIter->zTab, &rc); + sessionAppendStr(&buf, " SET ", &rc); + + /* Create the assignments part of the UPDATE */ + for(ii=0; iinCol; ii++){ + if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){ + sessionAppendStr(&buf, zSep, &rc); + sessionAppendIdent(&buf, p->azCol[ii], &rc); + sessionAppendStr(&buf, " = ?", &rc); + sessionAppendInteger(&buf, ii*2+1, &rc); + zSep = ", "; + } + } + + /* Create the WHERE clause part of the UPDATE */ + zSep = ""; + sessionAppendStr(&buf, " WHERE ", &rc); + for(ii=0; iinCol; ii++){ + if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){ + sessionAppendStr(&buf, zSep, &rc); + if( bStat1 && ii==1 ){ + assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 ); + sessionAppendStr(&buf, + "idx IS CASE " + "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL " + "ELSE ?4 END ", &rc + ); + }else{ + sessionAppendIdent(&buf, p->azCol[ii], &rc); + sessionAppendStr(&buf, " IS ?", &rc); + sessionAppendInteger(&buf, ii*2+2, &rc); + } + zSep = " AND "; + } + } + + if( rc==SQLITE_OK ){ + char *zSql = (char*)buf.aBuf; + rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0); + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pUp); + pUp = 0; + }else{ + pUp->pNext = p->pUp; + p->pUp = pUp; + } + sqlite3_free(buf.aBuf); + } + } + } + + assert( (rc==SQLITE_OK)==(pUp!=0) ); + if( pUp ){ + *ppStmt = pUp->pStmt; + }else{ + *ppStmt = 0; + } + return rc; +} + +/* +** Free all cached UPDATE statements. +*/ +static void sessionUpdateFree(SessionApplyCtx *p){ + SessionUpdate *pUp; + SessionUpdate *pNext; + for(pUp=p->pUp; pUp; pUp=pNext){ + pNext = pUp->pNext; + sqlite3_finalize(pUp->pStmt); + sqlite3_free(pUp); + } + p->pUp = 0; + sqlite3_free(p->aUpdateMask); + p->aUpdateMask = 0; +} + /* ** Formulate a statement to DELETE a row from database db. Assuming a table ** structure like this: @@ -3598,103 +3768,6 @@ static int sessionDeleteRow( return rc; } -/* -** Formulate and prepare a statement to UPDATE a row from database db. -** Assuming a table structure like this: -** -** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); -** -** The UPDATE statement looks like this: -** -** UPDATE x SET -** a = CASE WHEN ?2 THEN ?3 ELSE a END, -** b = CASE WHEN ?5 THEN ?6 ELSE b END, -** c = CASE WHEN ?8 THEN ?9 ELSE c END, -** d = CASE WHEN ?11 THEN ?12 ELSE d END -** WHERE a = ?1 AND c = ?7 AND (?13 OR -** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND -** ) -** -** For each column in the table, there are three variables to bind: -** -** ?(i*3+1) The old.* value of the column, if any. -** ?(i*3+2) A boolean flag indicating that the value is being modified. -** ?(i*3+3) The new.* value of the column, if any. -** -** Also, a boolean flag that, if set to true, causes the statement to update -** a row even if the non-PK values do not match. This is required if the -** conflict-handler is invoked with CHANGESET_DATA and returns -** CHANGESET_REPLACE. This is variable "?(nCol*3+1)". -** -** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left -** pointing to the prepared version of the SQL statement. -*/ -static int sessionUpdateRow( - sqlite3 *db, /* Database handle */ - const char *zTab, /* Table name */ - SessionApplyCtx *p /* Session changeset-apply context */ -){ - int rc = SQLITE_OK; - int i; - const char *zSep = ""; - SessionBuffer buf = {0, 0, 0}; - - /* Append "UPDATE tbl SET " */ - sessionAppendStr(&buf, "UPDATE main.", &rc); - sessionAppendIdent(&buf, zTab, &rc); - sessionAppendStr(&buf, " SET ", &rc); - - /* Append the assignments */ - for(i=0; inCol; i++){ - sessionAppendStr(&buf, zSep, &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " = CASE WHEN ?", &rc); - sessionAppendInteger(&buf, i*3+2, &rc); - sessionAppendStr(&buf, " THEN ?", &rc); - sessionAppendInteger(&buf, i*3+3, &rc); - sessionAppendStr(&buf, " ELSE ", &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " END", &rc); - zSep = ", "; - } - - /* Append the PK part of the WHERE clause */ - sessionAppendStr(&buf, " WHERE ", &rc); - for(i=0; inCol; i++){ - if( p->abPK[i] ){ - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " = ?", &rc); - sessionAppendInteger(&buf, i*3+1, &rc); - sessionAppendStr(&buf, " AND ", &rc); - } - } - - /* Append the non-PK part of the WHERE clause */ - sessionAppendStr(&buf, " (?", &rc); - sessionAppendInteger(&buf, p->nCol*3+1, &rc); - sessionAppendStr(&buf, " OR 1", &rc); - for(i=0; inCol; i++){ - if( !p->abPK[i] ){ - sessionAppendStr(&buf, " AND (?", &rc); - sessionAppendInteger(&buf, i*3+2, &rc); - sessionAppendStr(&buf, "=0 OR ", &rc); - sessionAppendIdent(&buf, p->azCol[i], &rc); - sessionAppendStr(&buf, " IS ?", &rc); - sessionAppendInteger(&buf, i*3+1, &rc); - sessionAppendStr(&buf, ")", &rc); - } - } - sessionAppendStr(&buf, ")", &rc); - - if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0); - } - sqlite3_free(buf.aBuf); - - return rc; -} - - /* ** Formulate and prepare an SQL statement to query table zTab by primary ** key. Assuming the following table structure: @@ -3775,17 +3848,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){ "?3)" ); } - if( rc==SQLITE_OK ){ - rc = sessionPrepare(db, &p->pUpdate, - "UPDATE main.sqlite_stat1 SET " - "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, " - "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, " - "stat = CASE WHEN ?8 THEN ?9 ELSE stat END " - "WHERE tbl=?1 AND idx IS " - "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END " - "AND (?10 OR ?8=0 OR stat IS ?7)" - ); - } if( rc==SQLITE_OK ){ rc = sessionPrepare(db, &p->pDelete, "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " @@ -4102,7 +4164,7 @@ static int sessionApplyOneOp( int nCol; int rc = SQLITE_OK; - assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect ); + assert( p->pDelete && p->pInsert && p->pSelect ); assert( p->azCol && p->abPK ); assert( !pbReplace || *pbReplace==0 ); @@ -4142,29 +4204,28 @@ static int sessionApplyOneOp( }else if( op==SQLITE_UPDATE ){ int i; + sqlite3_stmt *pUp = 0; + int bPatchset = (pbRetry==0 || pIter->bPatchset); + + rc = sessionUpdateFind(pIter, p, bPatchset, &pUp); /* Bind values to the UPDATE statement. */ for(i=0; rc==SQLITE_OK && ipUpdate, i*3+2, !!pNew); - if( pOld ){ - rc = sessionBindValue(p->pUpdate, i*3+1, pOld); + if( p->abPK[i] || (bPatchset==0 && pOld) ){ + rc = sessionBindValue(pUp, i*2+2, pOld); } if( rc==SQLITE_OK && pNew ){ - rc = sessionBindValue(p->pUpdate, i*3+3, pNew); + rc = sessionBindValue(pUp, i*2+1, pNew); } } - if( rc==SQLITE_OK ){ - sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset); - } if( rc!=SQLITE_OK ) return rc; /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, ** the result will be SQLITE_OK with 0 rows modified. */ - sqlite3_step(p->pUpdate); - rc = sqlite3_reset(p->pUpdate); + sqlite3_step(pUp); + rc = sqlite3_reset(pUp); if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ /* A NOTFOUND or DATA error. Search the table to see if it contains @@ -4387,14 +4448,13 @@ static int sessionChangesetApply( ); if( rc!=SQLITE_OK ) break; + sessionUpdateFree(&sApply); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_finalize(sApply.pDelete); - sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pSelect); sApply.db = db; sApply.pDelete = 0; - sApply.pUpdate = 0; sApply.pInsert = 0; sApply.pSelect = 0; sApply.nCol = 0; @@ -4458,11 +4518,10 @@ static int sessionChangesetApply( } sApply.bStat1 = 1; }else{ - if((rc = sessionSelectRow(db, zTab, &sApply)) - || (rc = sessionUpdateRow(db, zTab, &sApply)) - || (rc = sessionDeleteRow(db, zTab, &sApply)) - || (rc = sessionInsertRow(db, zTab, &sApply)) - ){ + if( (rc = sessionSelectRow(db, zTab, &sApply)) + || (rc = sessionDeleteRow(db, zTab, &sApply)) + || (rc = sessionInsertRow(db, zTab, &sApply)) + ){ break; } sApply.bStat1 = 0; @@ -4521,9 +4580,9 @@ static int sessionChangesetApply( *pnRebase = sApply.rebase.nBuf; sApply.rebase.aBuf = 0; } + sessionUpdateFree(&sApply); sqlite3_finalize(sApply.pInsert); sqlite3_finalize(sApply.pDelete); - sqlite3_finalize(sApply.pUpdate); sqlite3_finalize(sApply.pSelect); sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */ sqlite3_free((char*)sApply.constraints.aBuf); diff --git a/manifest b/manifest index 7cded2c472..5463576ba6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\scorrupt\sdatabase\sdetection\sin\sbalance_nonroot(). -D 2021-02-01T12:39:50.859 +C Modify\sthe\ssessions\sextension\sto\suse\smore\sefficient\sSQL\swhen\sapplying\sa\schangeset\sor\spatchset. +D 2021-02-02T20:41:13.378 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -428,7 +428,7 @@ F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0 F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a F ext/session/session1.test 0b2f88995832ea040ae8e83a1ad4afa99c00b85c779d213da73a95ea4113233e -F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0 +F ext/session/session2.test 7f53d755d921e0baf815c4258348e0ed460dfd8a772351bca5ad3ccbb1dc786e F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479 F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40 F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169 @@ -454,7 +454,7 @@ F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810 F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5 F ext/session/sessionwor.test 67b5ab91d4f93ce65ff1f58240ac5ddf73f8670facc1ffa49cef56293d52818d -F ext/session/sqlite3session.c d2aaaf05241ac7d23a1b1eaa8b1f165c90f7ff0fe57ff1932a87c6b89b886117 +F ext/session/sqlite3session.c 1d0553077b55ffcfa69963c354e9bad3bace6ce79bbe7368e650c6ae1e106314 F ext/session/sqlite3session.h f53c99731882bf59c7362855cdeba176ce1fe8eeba089e38a8cce0172f8473aa F ext/session/test_session.c 93ca965112d2b4d9d669c9c0be6b1e52942a268796050a145612df1eee175ce0 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 @@ -1898,7 +1898,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1ffd321a33b778e87614a26a91a8407ec7b9dec4f0f847b16b1dac4f3b910604 -R b8a4f4fec4116f58162d1c62be0cf1fb -U drh -Z 4a762bd4c15e78835f4802843c46cdfa +P 5d54d9fd406381383afdf10612bfd590afc4142215d9bca09e227e3aa5baa102 +R d44cc6b5db574b719ae909c431a63549 +U dan +Z 32b2c74d653dfa71fce784320ccf15ed diff --git a/manifest.uuid b/manifest.uuid index 11f8da895e..2cd9cb7572 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5d54d9fd406381383afdf10612bfd590afc4142215d9bca09e227e3aa5baa102 \ No newline at end of file +e4ccfac09b6fe8cc3aec29d10f4e4c83097964f29882343db52ed91f6f0dde1c \ No newline at end of file