From d770c86b824ad64013faeb2a0e98ad47c248d143 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 4 Nov 2024 17:33:26 +0000 Subject: [PATCH] Avoid loading the entire record into memory for an sqlite3_preupdate_old() call that retrieves an IPK value. FossilOrigin-Name: f9a90a0d2cd04e44dcc53e8bd270447f5acf9b4d679e23b67810875b6ee95ca7 --- ext/session/sessionblob.test | 55 ++++++++++++++++++++++++++++++++ manifest | 22 ++++++------- manifest.uuid | 2 +- src/vdbeInt.h | 1 + src/vdbeapi.c | 61 +++++++++++++++++++----------------- src/vdbeaux.c | 1 + 6 files changed, 101 insertions(+), 41 deletions(-) create mode 100644 ext/session/sessionblob.test diff --git a/ext/session/sessionblob.test b/ext/session/sessionblob.test new file mode 100644 index 0000000000..ed7ec67b48 --- /dev/null +++ b/ext/session/sessionblob.test @@ -0,0 +1,55 @@ +# 2024 November 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +if {$::tcl_platform(pointerSize)<8} { + finish_test + return +} + +set testprefix sessionblob + +forcedelete test.db2 +sqlite3 db2 test.db2 + +set NBLOB 2000000 + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(123, zeroblob($NBLOB)); +} + +do_test 1.1 { + sqlite3session S db main + S attach t1 +} {} + +set b2 [string repeat x 1000] +do_test 1.2 { + set ::blob [db incrblob t1 b 123] + for {set ii 0} {$ii < $NBLOB} {incr ii [string length $b2]} { + seek $::blob $ii + puts -nonewline $::blob $b2 + } + close $::blob +} {} + +S delete + +finish_test + diff --git a/manifest b/manifest index b0a2fbf76f..f6a9073209 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sOPFS\sVFS's\sxOpen()\sto\shonor\sthe\sread-only\sflag.\sFix\sthe\sOPFS\sSAHPool\sVFS\sto\senable\sre-installation\sof\sthe\sVFS\safter\scalling\sOpfsSAHPoolUtil.removeVfs(). -D 2024-10-17T12:17:18.240 +C Avoid\sloading\sthe\sentire\srecord\sinto\smemory\sfor\san\ssqlite3_preupdate_old()\scall\sthat\sretrieves\san\sIPK\svalue. +D 2024-11-04T17:33:26.757 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -568,6 +568,7 @@ F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8 F ext/session/sessionalter.test 460bdac2832a550519f6bc32e5db2c0cee94f335870aaf25a3a403a81ab20e17 F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b85419035bc04b45ee F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf +F ext/session/sessionblob.test 87faf667870b72f08e91969abd9f52a383ab7b514506ee194d64a39d8faff00a F ext/session/sessionchange.test 77c4702050f24270b58070e2cf01c95c3d232a3ef164b70f31974b386ce69903 F ext/session/sessionconflict.test 8b8cbd98548e2e636ddc17d0986276f60e833fb865617dd4f88ea5bbe3a16b96 F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec @@ -826,9 +827,9 @@ F src/util.c 4d6d7ebfe6772a1b950c97bbb1d1a72ad4874617ec498ab8aa73b7f5a43e44bb F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 3b1793c5d2235ae89b01ef051a33d7d2ad3704c71799653b112686735ad401ff F src/vdbe.h c2d78d15112c3fc5ab87f5e8e0b75d2db1c624409de2e858c3d1aafb1650bb4f -F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c 80235ac380e9467fec1cb0883354d841f2a771976e766995f7e0c77f845406df -F src/vdbeaux.c 6e37cb918506c28fe7657454fcbc2e01e66bfba4164f306c2f075fd5c5fef609 +F src/vdbeInt.h dc5a717607385857dd65e8661fc967aeee77b3c0bcbfb7c7acb1ea99e9007578 +F src/vdbeapi.c 1e6880bbc1b5930752ed1b67fbfcc751d4782e59763859249db699fb3e75a986 +F src/vdbeaux.c dd58f3dd2c0fee9b1a497201269511af633b67ce9ddfc0719c9b5b2b48a15d1e F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5 F src/vdbemem.c 831a244831eaa45335f9ae276b50a7a82ee10d8c46c2c72492d4eb8c98d94d89 F src/vdbesort.c 237840ca1947511fa59bd4e18b9eeae93f2af2468c34d2427b059f896230a547 @@ -2192,10 +2193,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 242cb4bbee0707f470833d9f47efcfb5631f2302b9d48cffdbba63e64984827c -Q +0a32624015f16fd881a4ecbb56b7833391028d327a95f4c899eee864ed7fe00d -Q +b7f7a5deeae61920dbfec7606cf9014de711f959a285b29e12673abfd2f88646 -R 35167de4f82ff4a760245cc11df277ba -U stephan -Z 23bdd4bd7373bf8ed99c8093853b8c62 +P 63ee3584201fe3a126991e278bd4d7bf2d4ae5c4937d97e311686a69fd1cf69b +Q +7f4de43733200beeb3ff0a70d51bbc68f5331895698ea95a82741cfd7bb66834 +R 1925f50d4d915494bbec6d09eb751f2d +U dan +Z bfb0012b12d420c95f9f9c70ce345eb0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0551b59cb6..7bcfcc43d2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -63ee3584201fe3a126991e278bd4d7bf2d4ae5c4937d97e311686a69fd1cf69b +f9a90a0d2cd04e44dcc53e8bd270447f5acf9b4d679e23b67810875b6ee95ca7 diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 2a23c3f285..23f987b6b6 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -540,6 +540,7 @@ struct PreUpdate { int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ + Mem oldipk; /* Memory cell holding "old" IPK value */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 3182e4070f..a256e68c23 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -2180,37 +2180,40 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ goto preupdate_old_out; } - /* If the old.* record has not yet been loaded into memory, do so now. */ - if( p->pUnpacked==0 ){ - u32 nRec; - u8 *aRec; - - assert( p->pCsr->eCurType==CURTYPE_BTREE ); - nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); - aRec = sqlite3DbMallocRaw(db, nRec); - if( !aRec ) goto preupdate_old_out; - rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); - if( rc==SQLITE_OK ){ - p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); - if( !p->pUnpacked ) rc = SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ){ - sqlite3DbFree(db, aRec); - goto preupdate_old_out; - } - p->aRecord = aRec; - } - - pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; if( iIdx==p->pTab->iPKey ){ + pMem = *ppValue = &p->oldipk; sqlite3VdbeMemSetInt64(pMem, p->iKey1); - }else if( iIdx>=p->pUnpacked->nField ){ - *ppValue = (sqlite3_value *)columnNullValue(); - }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ - if( pMem->flags & (MEM_Int|MEM_IntReal) ){ - testcase( pMem->flags & MEM_Int ); - testcase( pMem->flags & MEM_IntReal ); - sqlite3VdbeMemRealify(pMem); + }else{ + /* If the old.* record has not yet been loaded into memory, do so now. */ + if( p->pUnpacked==0 ){ + u32 nRec; + u8 *aRec; + + assert( p->pCsr->eCurType==CURTYPE_BTREE ); + nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); + aRec = sqlite3DbMallocRaw(db, nRec); + if( !aRec ) goto preupdate_old_out; + rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); + if( rc==SQLITE_OK ){ + p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); + if( !p->pUnpacked ) rc = SQLITE_NOMEM; + } + if( rc!=SQLITE_OK ){ + sqlite3DbFree(db, aRec); + goto preupdate_old_out; + } + p->aRecord = aRec; + } + + pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; + if( iIdx>=p->pUnpacked->nField ){ + *ppValue = (sqlite3_value *)columnNullValue(); + }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){ + if( pMem->flags & (MEM_Int|MEM_IntReal) ){ + testcase( pMem->flags & MEM_Int ); + testcase( pMem->flags & MEM_IntReal ); + sqlite3VdbeMemRealify(pMem); + } } } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 665f6cd17a..18b1183a41 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -5525,6 +5525,7 @@ void sqlite3VdbePreUpdateHook( sqlite3DbFree(db, preupdate.aRecord); vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); + sqlite3VdbeMemRelease(&preupdate.oldipk); if( preupdate.aNew ){ int i; for(i=0; inField; i++){