From: dan Date: Mon, 4 Nov 2024 16:59:02 +0000 (+0000) Subject: Avoid loading the entire record into memory for an sqlite3_preupdate_old() call that... X-Git-Tag: major-relase~230 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4bf24c8830d21509df5819b0a2c7fdcd0e45f42e;p=thirdparty%2Fsqlite.git Avoid loading the entire record into memory for an sqlite3_preupdate_old() call that retrieves an IPK value. FossilOrigin-Name: 7f4de43733200beeb3ff0a70d51bbc68f5331895698ea95a82741cfd7bb66834 --- 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 e80bdc1732..76c6219937 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\stwo\smismatched\suses\sof\smalloc()\sand\ssqlite3_free()\sin\ssqlite3_stdio.c,\sas\sreported\sin\s[forum:7dd7c70038\s|\sforum\spost\s7dd7c70038]. -D 2024-11-04T13:57:20.510 +C Avoid\sloading\sthe\sentire\srecord\sinto\smemory\sfor\san\ssqlite3_preupdate_old()\scall\sthat\sretrieves\san\sIPK\svalue. +D 2024-11-04T16:59:02.337 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md c5b4009dca54d127d2d6033c22fd9cc34f53bedb6ef12c7cbaa468381c74ab28 @@ -595,6 +595,7 @@ F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8 F ext/session/sessionalter.test e852acb3d2357aac7d0b920a2109da758c4331bfdf85b41d39aa3a8c18914f65 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 e89f7aedcdd89e5ebac3a455224eb553a171e9586fc3e1e6a7b3388d2648ba8d @@ -847,9 +848,9 @@ F src/util.c ceebf912f673247e305f16f97f0bb7285fca1d37413b79680714a553a9021d33 F src/vacuum.c b763b6457bd058d2072ef9364832351fd8d11e8abf70cbb349657360f7d55c40 F src/vdbe.c 8a6eb02823b424b273614bae41579392a5c495424592b60423dd2c443a583df0 F src/vdbe.h c2549a215898a390de6669cfa32adba56f0d7e17ba5a7f7b14506d6fd5f0c36a -F src/vdbeInt.h af7d7e8291edd0b19f2cd698e60e4d4031078f9a2f2328ac8f0b7efb134f8a1d -F src/vdbeapi.c 53c7e26a2c0821a892b20eee2cde4656e31998212f3d515576c780dfaa45fd17 -F src/vdbeaux.c f06f011e4fac948941ea821ac365a9f1c163ef473e63756d6e499a37c6bda9ef +F src/vdbeInt.h 2da01c73e8e3736a9015d5b04aa04d209bc9023d279d237d4d409205e921ea1e +F src/vdbeapi.c 6353de05e8e78e497ccb33381ba5662ccc11c0339e5b1455faff01b6dacc3075 +F src/vdbeaux.c f0706ad786b8a6c5bc7ea622f3916c2ba2b883abc872d0b4911c4f021945c0e5 F src/vdbeblob.c 255be187436da38b01f276c02e6a08103489bbe2a7c6c21537b7aecbe0e1f797 F src/vdbemem.c df568ef0187e4be2788c35174f6d9b8566ab9475f9aff2d73907ed05aa5684b2 F src/vdbesort.c d0a3c7056c081703c8b6d91ad60f17da5e062a5c64bf568ed0fa1b5f4cae311f @@ -2198,8 +2199,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P a60e5d76d06ae0568fbc6e068a7012c77778607cd60da92a1b84ff8f33049a93 -R 1e21ba8321e5a6b22692a09525e9bc1b -U stephan -Z 743a8b18a247cccbb6a7ea690e8b0e07 +P af0a345b3b287f82b54249cfa574ef3ce52305a6452058aac98cd473c361919e +R 84f5bc88e95886a46e23b56039713592 +U dan +Z ba372c464e587d954c923d1075bdeb25 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6b47a0a442..8ed87eec77 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -af0a345b3b287f82b54249cfa574ef3ce52305a6452058aac98cd473c361919e +7f4de43733200beeb3ff0a70d51bbc68f5331895698ea95a82741cfd7bb66834 diff --git a/src/vdbeInt.h b/src/vdbeInt.h index b26860f3a6..467f488905 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 b6ad5f3d95..014ad95393 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -2197,60 +2197,64 @@ 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 ){ + *ppValue = pMem = &p->oldipk; sqlite3VdbeMemSetInt64(pMem, p->iKey1); - }else if( iIdx>=p->pUnpacked->nField ){ - /* This occurs when the table has been extended using ALTER TABLE - ** ADD COLUMN. The value to return is the default value of the column. */ - Column *pCol = &p->pTab->aCol[iIdx]; - if( pCol->iDflt>0 ){ - if( p->apDflt==0 ){ - int nByte = sizeof(sqlite3_value*)*p->pTab->nCol; - p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); - if( p->apDflt==0 ) goto preupdate_old_out; + }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( p->apDflt[iIdx]==0 ){ - sqlite3_value *pVal = 0; - Expr *pDflt; - assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); - pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; - rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); - if( rc==SQLITE_OK && pVal==0 ){ - rc = SQLITE_CORRUPT_BKPT; - } - p->apDflt[iIdx] = pVal; + if( rc!=SQLITE_OK ){ + sqlite3DbFree(db, aRec); + goto preupdate_old_out; } - *ppValue = p->apDflt[iIdx]; - }else{ - *ppValue = (sqlite3_value *)columnNullValue(); + p->aRecord = aRec; } - }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); + + pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; + if( iIdx>=p->pUnpacked->nField ){ + /* This occurs when the table has been extended using ALTER TABLE + ** ADD COLUMN. The value to return is the default value of the column. */ + Column *pCol = &p->pTab->aCol[iIdx]; + if( pCol->iDflt>0 ){ + if( p->apDflt==0 ){ + int nByte = sizeof(sqlite3_value*)*p->pTab->nCol; + p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte); + if( p->apDflt==0 ) goto preupdate_old_out; + } + if( p->apDflt[iIdx]==0 ){ + sqlite3_value *pVal = 0; + Expr *pDflt; + assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) ); + pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; + rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal); + if( rc==SQLITE_OK && pVal==0 ){ + rc = SQLITE_CORRUPT_BKPT; + } + p->apDflt[iIdx] = pVal; + } + *ppValue = p->apDflt[iIdx]; + }else{ + *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 8120536b98..4414f7a2ec 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -5529,6 +5529,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++){